Compare commits

...

128 Commits

Author SHA1 Message Date
Morph 87cfe5b1da time: Fix CalculateSpanBetween implementation
CalculateSpanBetween passes in the ClockSnapshots through 2 input buffers and not as raw arguments. Fix this by reading the 2 input buffers instead of popping raw arguments.

Partially fixes Super Smash Bros. Ultimate's Spirit Board
2021-03-10 11:42:59 -05:00
Rodrigo Locatti daf5c5060b Merge pull request #5891 from ameerj/bgra-ogl
renderer_opengl: Use compute shaders to swizzle BGR textures on copy
2021-03-09 02:47:51 -03:00
bunnei d1a7b2eca7 Merge pull request #6021 from ReinUsesLisp/skip-cache-heuristic
buffer_cache: Heuristically decide to skip cache on uniform buffers
2021-03-08 17:48:55 -08:00
bunnei 9c4c9f1e7d Merge pull request #5990 from german77/mousePanningV2
InputCommon: Mouse fixes
2021-03-08 14:50:58 -08:00
bunnei 69ce5e41eb Merge pull request #6041 from bunnei/fiber-leaks
common: fiber: Use weak_ptr when yielding.
2021-03-08 13:51:37 -08:00
Morph 9d77ae39de Merge pull request #6047 from lioncash/dynarmic
external: Update dynarmic
2021-03-08 00:52:19 -05:00
Lioncash de21c9e330 external: Update dynarmic 2021-03-08 00:44:54 -05:00
bunnei 8f7eb194af common: Fiber: use a reference for YieldTo.
- Fixes another small leak.
2021-03-07 13:46:53 -08:00
bunnei 68ffac250a common: fiber: Use weak_ptr when yielding.
- Avoids a memory leak, as taking a strong reference of the fiber here causes a circular reference.
- Supersedes #6006 with a more narrow fix.
2021-03-05 22:10:03 -08:00
bunnei 4cf5b860bd Merge pull request #6036 from bunnei/thread-leak
hle: kernel: KThread: Rework dummy threads & fix memory leak.
2021-03-05 17:15:35 -08:00
bunnei 47af34003b hle: kernel: KThread: Rework dummy threads & fix memory leak.
- Dummy threads are created on thread local storage for all host threads.
- Fixes a leak by removing creation of fibers, which are not applicable here.
2021-03-05 17:10:57 -08:00
LC 97415ad07a Merge pull request #6029 from Morph1984/compile-utf8
CMakeLists: Add /utf-8 compile option for MSVC
2021-03-05 20:09:37 -05:00
bunnei 7b29a8ce4e Merge pull request #6039 from yuzu-emu/revert-6006-fiber-unique-ptr
Revert "core: Switch to unique_ptr for usage of Common::Fiber."
2021-03-05 17:08:48 -08:00
bunnei a5ab85ac37 Revert "core: Switch to unique_ptr for usage of Common::Fiber." 2021-03-05 17:08:17 -08:00
bunnei 9d010be483 Merge pull request #6034 from Morph1984/mbedtls
externals: Update mbedtls to 2.16.9
2021-03-05 15:48:28 -08:00
bunnei 34a3ee1631 Merge pull request #6006 from bunnei/fiber-unique-ptr
core: Switch to unique_ptr for usage of Common::Fiber.
2021-03-04 23:59:06 -08:00
Morph 96c9e67b1b aes_util: Remove malformed mbedtls_cipher_finish function call 2021-03-05 02:05:05 -05:00
Morph 6faabd6d69 externals: Update mbedtls to 2.16.9
mbedtls 2.16 is the last version which has licensing for GPL 2.0. This updates mbedtls to our own fork of mbedtls 2.16
2021-03-05 02:05:05 -05:00
Morph e7038344aa CMakeLists: Add /utf-8 compile option for MSVC
Ensures that the source and execution character sets are in UTF-8
2021-03-05 01:46:56 -05:00
ameerj 5213f70230 texture_cache: Blacklist BGRA8 copies and views on OpenGL
In order to force the BGRA8 conversion on Nvidia using OpenGL, we need to forbid texture copies and views with other formats.

This commit also adds a boolean relating to this, as this needs to be done only for the OpenGL api, Vulkan must remain unchanged.
2021-03-04 14:14:49 -05:00
ameerj 0639244d85 renderer_opengl: Swizzle BGR textures on copy
OpenGL does not natively support BGR internal formats, which causes many BGR textures to render incorrectly, with Red and Blue channels swapped.

This commit aims to address this by swizzling the blue and red channels on texture copies when a BGR format is encountered.
2021-03-04 14:14:19 -05:00
bunnei b8b5891585 Merge pull request #5989 from ReinUsesLisp/cmdpool
vk_command_pool: Reduce the command pool size from 4096 to 4
2021-03-04 11:07:31 -08:00
bunnei 394475c4e3 Merge pull request #6004 from german77/udprandom
InputCommon: Use an unique client id for each udp socket instance
2021-03-03 15:45:32 -08:00
bunnei f8bfec3109 Merge pull request #5815 from comex/net-error-reform
Network error handling reform
2021-03-02 17:08:47 -08:00
LC 4a45012f35 Merge pull request #6020 from bunnei/shutdown-crash-2
core: Shutdown: Move kernel cleanup to later in shutdown.
2021-03-02 09:28:28 -05:00
ReinUsesLisp 5ad62e7bfc buffer_cache: Heuristically decide to skip cache on uniform buffers
Some games benefit from skipping caches (Pokémon Sword), and others
don't (Animal Crossing: New Horizons). Add an heuristic to decide this
at runtime.

The cache hit ratio has to be ~98% or better to not skip the cache.
There are 16 frames of buffer.
2021-03-02 02:44:19 -03:00
bunnei 925671071c core: Shutdown: Move kernel cleanup to later in shutdown.
- Fixes a shutdown crash due to a race condition with GPU still accessing memory.
2021-03-01 21:42:06 -08:00
bunnei cd25817938 Merge pull request #6019 from Kelebek1/bcat
[Service::nifm] Fix bcat_backend's default initialisation
2021-03-01 19:27:06 -08:00
Kelebek1 c7a7e47615 Fix default bcat_backend init 2021-03-02 03:20:16 +00:00
german 9b3af0027b inputCommon: Use an unique client id for each socket instance 2021-03-01 09:19:33 -06:00
Morph ac8b1445ff Merge pull request #6016 from ameerj/remove-async-nvdec
gpu_thread: Remove Async NVDEC placeholders
2021-03-01 04:22:46 -05:00
ameerj 52e9d7fa49 gpu_thread: Remove Async NVDEC placeholders
This commit removes early placeholders for an implementation of async nvdec. With recent changes to the source code, the placeholders are no longer accurate, and can cause a nullptr dereference due to the nature of the cdma_pusher lifetime.
2021-02-28 22:03:00 -05:00
comex 2910aa77b2 [network] Error handling reform
`network.cpp` has several error paths which either:
- report "Unhandled host socket error=n" and return `SUCCESS`, or
- switch on a few possible errors, log them, and translate them to
  Errno; the same switch statement is copied and pasted in multiple
  places in the code

Convert these paths to use a helper function `GetAndLogLastError`, which
is roughly the equivalent of one of the switch statements, but:
- handling more cases (both ones that were already in `Errno`, and a few
  more I added), and
- using OS functions to convert the error to a string when logging, so
  it'll describe the error even if it's not one of the ones in the
  switch statement.
  - To handle this, refactor the logic in `GetLastErrorMsg` to expose a
    new function `NativeErrorToString` which takes the error number
    explicitly as an argument.  And improve the Windows version a bit.

Also, add a test which exercises two random error paths.
2021-02-28 17:25:31 -05:00
bunnei 9e9341f4b4 Merge pull request #6007 from bunnei/ldn-error
core: hle: ldn: Error out on call to Initialization.
2021-02-28 13:34:20 -08:00
Morph ee9ebeeb80 Merge pull request #5276 from german77/gestures
HID: Implement gestures
2021-02-27 22:18:41 -05:00
german e895ab7d6f Implements touch, pan, pinch and rotation gestures 2021-02-27 19:54:42 -06:00
german77 4738e14cb0 inputCommon: Mouse fixes 2021-02-27 17:53:10 -06:00
bunnei 55f556c53e Merge pull request #5984 from jbeich/gcc-freebsd
common,video-core: unbreak GCC 11 build on FreeBSD 13
2021-02-27 14:15:00 -07:00
bunnei ab65cb499d core: hle: ldn: Error out on call to Initialization.
- Since we do not emulate LDN, returning an error here makes more sense.
2021-02-27 11:59:29 -08:00
bunnei 51fb0a6f96 core: Switch to unique_ptr for usage of Common::Fiber.
- With using unique_ptr instead of shared_ptr, we have more explicit ownership of the context.
- Fixes a memory leak due to circular reference of the shared pointer.
2021-02-27 11:56:04 -08:00
bunnei 09f7c355c6 Merge pull request #5953 from bunnei/memory-refactor-1
Kernel Rework: Memory updates and refactoring (Part 1)
2021-02-27 12:48:35 -07:00
bunnei bfa1644464 Merge pull request #5944 from Morph1984/gc-vibrations
hid: Implement GameCube Controller Vibrations
2021-02-26 19:10:36 -07:00
bunnei 272bc4c3d6 Merge pull request #5997 from Kelebek1/Depth
[OpenGL] Implement glDepthRangeIndexeddNV
2021-02-26 15:06:55 -07:00
bunnei 1ba578c4aa Merge pull request #5977 from Morph1984/stub-acc
acc: Stub GetNintendoAccountUserResourceCacheForApplication
2021-02-24 17:46:15 -07:00
Kelebek1 d31dbb1bc1 Implement glDepthRangeIndexeddNV 2021-02-24 22:26:53 +00:00
ReinUsesLisp aae399c1a8 vk_command_pool: Reduce the command pool size from 4096 to 4
This allows drivers to reuse memory more easily and preallocate less.
The optimal number has been measured booting Pokémon Sword.
2021-02-23 19:08:24 -03:00
Jan Beich 1841ca4b9b video_core: add missing header after 468bd9c1b0
src/video_core/shader_notify.cpp: In member function 'void VideoCore::ShaderNotify::MarkShaderComplete()':
src/video_core/shader_notify.cpp:33:10: error: 'unique_lock' is not a member of 'std'
   33 |     std::unique_lock lock{mutex};
      |          ^~~~~~~~~~~
src/video_core/shader_notify.cpp:6:1: note: 'std::unique_lock' is defined in header '<mutex>'; did you forget to '#include <mutex>'?
    5 | #include "video_core/shader_notify.h"
  +++ |+#include <mutex>
    6 |
src/video_core/shader_notify.cpp: In member function 'void VideoCore::ShaderNotify::MarkSharderBuilding()':
src/video_core/shader_notify.cpp:38:10: error: 'unique_lock' is not a member of 'std'
   38 |     std::unique_lock lock{mutex};
      |          ^~~~~~~~~~~
src/video_core/shader_notify.cpp:38:10: note: 'std::unique_lock' is defined in header '<mutex>'; did you forget to '#include <mutex>'?
2021-02-23 00:04:36 +00:00
Jan Beich 71526ecfc7 common: add missing header after f3805376f7
In file included from src/video_core/dma_pusher.cpp:5:
src/./common/cityhash.h:69:47: error: 'size_t' has not been declared
   69 | [[nodiscard]] u64 CityHash64(const char* buf, size_t len);
      |                                               ^~~~~~
src/./common/cityhash.h:73:55: error: 'size_t' has not been declared
   73 | [[nodiscard]] u64 CityHash64WithSeed(const char* buf, size_t len, u64 seed);
      |                                                       ^~~~~~
src/./common/cityhash.h:77:56: error: 'size_t' has not been declared
   77 | [[nodiscard]] u64 CityHash64WithSeeds(const char* buf, size_t len, u64 seed0, u64 seed1);
      |                                                        ^~~~~~
src/./common/cityhash.h:80:47: error: 'size_t' has not been declared
   80 | [[nodiscard]] u128 CityHash128(const char* s, size_t len);
      |                                               ^~~~~~
src/./common/cityhash.h:84:55: error: 'size_t' has not been declared
   84 | [[nodiscard]] u128 CityHash128WithSeed(const char* s, size_t len, u128 seed);
      |                                                       ^~~~~~
2021-02-23 00:04:32 +00:00
LC ae876ed047 Merge pull request #5981 from lat9nq/ci-add-clang
ci: Add clang build scripts
2021-02-22 07:12:30 -05:00
lat9nq fb0b4c7e27 ci: Add clang build scripts
Adds scripts that instruct CI to build yuzu with the installed Clang
compiler on yuzuemu/build-environments:linux-fresh.

These scripts are based on the .ci/scripts/linux scripts, minus AppImage
building since that isn't necessary. Re-uses linux-fresh since that
container has Clang 12 installed.
2021-02-22 01:40:44 -05:00
bunnei 20245e660f Merge pull request #5936 from Kelebek1/Offsets
Offsets for TexelFetch and TextureGather in Vulkan
2021-02-21 21:23:45 -07:00
Morph ec19a85890 hid: Implement GameCube Controller Vibrations
Implements both SendVibrationGcErmCommand and GetActualVibrationGcErmCommand, and modifies GetVibrationDeviceInfo to account for additional controllers.
2021-02-21 10:32:59 -05:00
Morph 3de8e7a8f2 acc: Stub GetNintendoAccountUserResourceCacheForApplication
This command returns a Nintendo Account ID and writes 2 output buffers. The first output buffer is a NasUserBaseForApplication and the second output buffer is currently empty.

Used by:
- Pokken Tournament DX
- Super Smash Bros. Ultimate
- Super Nintendo Entertainment System - Nintendo Switch Online
- Mario Kart 8 Deluxe
2021-02-21 10:29:25 -05:00
bunnei 3d0394681c Merge pull request #5971 from ameerj/reslimit-dtor
kernel: Fix resource release exception on exit
2021-02-20 21:15:00 -08:00
ameerj 8e4c9c9852 kernel: Fix resource release exception on exit
After rewriting the resource limit, objects releasing reserved resources require a live kernel instance.
This commit fixes exceptions that occur due to the kernel being destroyed before some objects released their resources, allowing for a graceful exit.
2021-02-20 20:51:11 -05:00
Ameer J 2807a98168 Merge pull request #5965 from Morph1984/shader-count
gl_disk_shader_cache: Log total shader entries count on game load
2021-02-20 20:18:00 -05:00
Morph 1a5d4d7840 gl_disk_shader_cache: Log total shader entries count on game load 2021-02-20 11:08:19 -05:00
bunnei def03d4075 Merge pull request #5964 from bunnei/timing-fix
common: wall_clock: Fix integer overflow with StandardWallClock.
2021-02-19 19:11:05 -08:00
bunnei 3acb265c9e common: wall_clock: Fix integer overflow with StandardWallClock.
- Previous optimized impl. resulted in an integer overflow, so revert.
- This is our slow/fallback path that should never be really be used, so the optimization in unimportant.
2021-02-19 18:04:23 -08:00
bunnei 728ee181eb Merge pull request #5924 from ReinUsesLisp/inline-bindings
vk_update_descriptor: Inline and improve code for binding buffers
2021-02-19 12:27:10 -08:00
bunnei 93e20867b0 hle: kernel: Migrate PageHeap/PageTable to KPageHeap/KPageTable. 2021-02-18 16:16:25 -08:00
bunnei b1e27890e8 hle: kernel: Migrate MemoryManager to KMemoryManager. 2021-02-18 16:16:25 -08:00
bunnei 93109c870e hle: kernel: Migrate PageLinkedList to KPageLinkedList. 2021-02-18 16:16:25 -08:00
bunnei 65e0178cc0 hle: kernel: Migrate to KMemoryBlock, KMemoryBlockManager, and others. 2021-02-18 16:16:25 -08:00
bunnei 9e520e8f12 hle: kernel: Migrate SlabHeap to KSlabHeap. 2021-02-18 16:16:25 -08:00
bunnei 1d162f28d1 hle: kernel: Migrate MemoryLayout to KMemoryLayout. 2021-02-18 16:16:25 -08:00
bunnei 7ed5dd0d62 hle: kernel: Migrate AddressSpaceInfo to KAddressSpaceInfo. 2021-02-18 16:16:25 -08:00
bunnei 701ef616b2 hle: kernel: memory_manager: Rename AllocateContinuous to AllocateContinuous. 2021-02-18 16:16:24 -08:00
bunnei f7a008d77f hle: kernel: KSystemControl does not belong in Memory namespace. 2021-02-18 16:16:24 -08:00
bunnei 6a19086001 hle: kernel: memory: PageHeap: Migrate to KPageBitmap class. 2021-02-18 16:16:24 -08:00
bunnei a02566136c hle: kernel: Add KPageBitmap class. 2021-02-18 16:16:24 -08:00
bunnei e7c33d1ad6 hle: kernel: system_control: Add function GenerateRandomU64. 2021-02-18 16:16:24 -08:00
bunnei c9235764c7 common: Add implementation of TinyMT (Mersenne Twister RNG). 2021-02-18 16:16:24 -08:00
bunnei 6da91da08e hle: kernel: Add KSpinLock implementation. 2021-02-18 16:16:24 -08:00
bunnei 24e1e17a8a core: memory: Add templated GetPointer methods. 2021-02-18 16:16:24 -08:00
bunnei b5b92fd1e5 common: alignment: Add DivideUp utility method. 2021-02-18 16:16:24 -08:00
bunnei 0d62f30b00 hle: kernel: Rename SharedMemory to KSharedMemory. 2021-02-18 16:16:12 -08:00
bunnei 51e8b2733c Merge pull request #5957 from lat9nq/update-dynarmic
externals: Update dynarmic to latest
2021-02-18 15:53:44 -08:00
bunnei 9cae3e6e90 Merge pull request #4973 from ameerj/nvdec-opt
nvdec: Reuse allocated buffers and general cleanup
2021-02-18 15:12:07 -08:00
lat9nq 0c24ae300c externals: Update dynarmic to latest
Updates dynarmic to its latest commit. Includes a fix for argument
limits while compiling with Clang 12.
2021-02-18 14:44:49 -05:00
Morph 6686468df0 Merge pull request #5955 from yuzu-emu/revert-3603-port-5123
Revert "Port citra-emu/citra#5123: "SDL: Disable hidapi drivers due to compatibility problems with certain controllers""
2021-02-19 00:26:02 +08:00
Morph 1c550ff954 Revert "Port citra-emu/citra#5123: "SDL: Disable hidapi drivers due to compatibility problems with certain controllers"" 2021-02-18 11:16:14 -05:00
LC c864f2c532 Merge pull request #5952 from ReinUsesLisp/cityhash
common/cityhash: Use common types
2021-02-18 04:28:54 -05:00
LC 90f93a408a Merge pull request #5954 from lat9nq/ffmpeg-431-2
cmake: Update FFmpeg to 4.3.1
2021-02-18 04:26:59 -05:00
ReinUsesLisp f3805376f7 common/cityhash: Use common types
Allow sharing return types with the rest of the code base. For example,
we use 'u128 = std::array<u64, 2>', meanwhile Google's code uses
'uint128 = std::pair<u64, u64>'.

While we are at it, use size_t instead of std::size_t.
2021-02-18 00:45:17 -03:00
ReinUsesLisp 9ca5e52f07 tests: Add tests for CityHash 2021-02-18 00:44:57 -03:00
bunnei 6be0975bf2 Merge pull request #5121 from bunnei/optimize-core-timing
core: Optimize core timing utility functions to avoid unnecessary math
2021-02-16 13:17:22 -08:00
Morph 723e038dba Merge pull request #5929 from german77/mousePanning
Improve mouse panning
2021-02-16 22:52:35 +08:00
bunnei aaccb21f81 Merge pull request #4298 from FearlessTobi/remove-cache-setting
yuzu/configure_filesystem: Remove "Select Cache Directory" option
2021-02-15 20:31:16 -08:00
LC df1a9d09a9 Merge pull request #5942 from ReinUsesLisp/fixup-rebase
vk_rasterizer: Fix loading shader addresses twice
2021-02-15 19:35:56 -05:00
ReinUsesLisp 24d0cc3ab8 vk_rasterizer: Fix loading shader addresses twice
This was recently introduced on a wrongly rebased commit.
2021-02-15 21:34:13 -03:00
bunnei 86212d4bcd Merge pull request #3603 from FearlessTobi/port-5123
Port citra-emu/citra#5123: "SDL: Disable hidapi drivers due to compatibility problems with certain controllers"
2021-02-15 16:25:53 -08:00
bunnei f3345e84ad core: core_timing_util: Optimize core timing math.
- Avoids a lot of unnecessary 128-bit math for imperceptible accuracy.
2021-02-15 14:54:06 -08:00
bunnei 592a649918 common: wall_clock: Optimize GetClockCycles/GetCPUCycles to use a single MUL instruction. 2021-02-15 14:51:43 -08:00
bunnei 0a91599aec common: Merge uint128 to a single header file with inlines. 2021-02-15 14:46:04 -08:00
bunnei cffa6f4e62 Merge pull request #5923 from ReinUsesLisp/vk-dirty-pipeline
fixed_pipeline_cache: Use dirty flags to lazily update key
2021-02-15 13:17:27 -08:00
LC ed543c4d5c Merge pull request #5939 from Morph1984/web_types
core/CMakeLists: Add web_types.h
2021-02-15 14:02:10 -05:00
bunnei b53b50adec Merge pull request #4940 from german77/nativeGC
HID: Implement GC controller in game
2021-02-15 10:32:19 -08:00
Morph 48cfc47050 core/CMakeLists: Add web_types.h 2021-02-15 09:40:30 -05:00
bunnei 90610bde9b Merge pull request #5935 from lat9nq/controller_access_keys
debugger: controller: Add access key
2021-02-14 22:33:59 -08:00
Kelebek1 9d8f793969 Review 1 2021-02-15 05:26:28 +00:00
bunnei 8378b8a61f Merge pull request #5909 from ogniK5377/I3dl2Reverb
audren: Implement I3dl2Reverb
2021-02-14 20:09:15 -08:00
Kelebek1 fb54c38631 Implement texture offset support for TexelFetch and TextureGather and add offsets for Tlds
Formatting
2021-02-15 00:36:37 +00:00
lat9nq 6269cd7f1d debugger: controller: Add access key
Adds the access key to the Controller P1 selection at View -> Debugger
-> Controller P1. Avoids using the windowTitle as that would add a
literal & to the beginning of the window title.
2021-02-14 16:10:12 -05:00
bunnei b0a3915351 Merge pull request #5920 from bunnei/am-ldn-fix
Fix LDN Initialization return code & resulting AM overflow
2021-02-14 02:46:01 -08:00
bunnei eae9f2e440 yuzu: Various frontend improvements to avoid crashes and improve experience on Linux. 2021-02-14 00:20:41 -08:00
bunnei d9a8060ce3 hle: service: ldn: IUserLocalCommunicationService: Improve the stub. 2021-02-13 21:45:09 -08:00
german 594973bdd2 Improve mouse panning 2021-02-13 22:23:32 -06:00
bunnei 51c13606d6 hle: service: ldn: IUserLocalCommunicationService: Indicate that LDN is disabled.
- Fixes crash on Pokemon Sword/Shield when pressing 'Y'.
2021-02-13 20:11:26 -08:00
bunnei d25011c92f hle: service: am: IStorageAccessor: Fix out of bounds error handling. 2021-02-13 20:11:26 -08:00
LC 0bbf5e61f1 Merge pull request #5925 from ReinUsesLisp/resource-pool-clean
vk_resource_pool: Load GPU tick once and compare with it
2021-02-13 20:57:44 -05:00
ReinUsesLisp b8ffdbb167 vk_resource_pool: Load GPU tick once and compare with it
Other minor style improvements. Rename free_iterator to hint_iterator,
to describe better what it does.
2021-02-13 17:53:58 -03:00
ReinUsesLisp 21b40de318 vk_update_descriptor: Inline and improve code for binding buffers
Allow compilers with our settings inline hot code.
2021-02-13 17:46:24 -03:00
ReinUsesLisp 70353649d7 fixed_pipeline_cache: Use dirty flags to lazily update key
Use dirty flags to avoid building pipeline key from scratch on each draw
call. This saves a bit of unnecesary work on each draw call.
2021-02-13 17:44:47 -03:00
Rodrigo Locatti 95722823b9 Merge pull request #5921 from ameerj/srgb-views
gl_texture_cache: Lazily create non-sRGB texture views for sRGB formats
2021-02-13 16:51:53 -03:00
ameerj b675c44e49 rebase, fix name shadowing, more const 2021-02-13 13:07:56 -05:00
ameerj 3c37d66c28 Address PR feedback
Co-Authored-By: LC <712067+lioncash@users.noreply.github.com>
2021-02-13 13:07:56 -05:00
ameerj 09722cb4a7 streamline cdma_pusher/command_classes 2021-02-13 13:07:56 -05:00
ameerj 77564f987c streamline cdma_pusher/command_classes 2021-02-13 13:07:53 -05:00
ameerj ac265a72ce nvdec cleanup 2021-02-13 13:07:31 -05:00
Chloe Marcec 7ad63ea542 revert to std::sin and std::cos 2021-02-12 18:48:10 -08:00
Chloe Marcec d28b942458 address issues 2021-02-12 18:48:10 -08:00
Chloe Marcec 4a7fd91857 audren: Implement I3dl2Reverb
Most notable fix is the voices in Fire Emblem Three Houses
2021-02-12 18:48:10 -08:00
lat9nq c44ab0f8f6 cmake: Update FFmpeg to 4.3.1
Download FFmpeg package version 4.3.1. Uses a file defined within the
package to determine with DLLs to copy.

Also corrects a submodule name.
2021-02-09 22:17:22 -05:00
german bcd4e4f650 Use GC image 2021-02-09 08:12:21 -06:00
german a994a40467 hid: Implement GC controller 2021-02-07 22:59:46 -06:00
FearlessTobi 8e77d331be yuzu/configure_filesystem: Remove "Select Cache Directory" option
This tab of the settings is already extremely bloated and the setting itself is quite useless.
With a gamelist of almost 30 games, the cache directory is smaller than 1MB for me and therefore I don't see why it needs to be configurable.
2021-01-04 06:29:48 +01:00
Vitor Kiguchi e6f9fe1f60 sdl_joystick: disable the use of the hidapi drivers due to many problems caused by them.
The main problem is the loss of compatibility with some controllers, but there are also
unwanted changes to the behaviour of PS4 controllers (hardcoded lightbar color).
2020-08-30 05:06:49 +02:00
194 changed files with 4227 additions and 2254 deletions
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash -ex
# Exit on error, rather than continuing with the rest of the script.
set -e
cd /yuzu
ccache -s
mkdir build || true && cd build
cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/clang -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
make -j$(nproc)
ccache -s
ctest -VV -C Release
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash -ex
mkdir -p "ccache" || true
chmod a+x ./.ci/scripts/clang/docker.sh
# the UID for the container yuzu user is 1027
sudo chown -R 1027 ./
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/clang/docker.sh $1
sudo chown -R $UID ./
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash -ex
. .ci/scripts/common/pre-upload.sh
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
ARCHIVE_NAME="${REV_NAME}.tar.xz"
COMPRESSION_FLAGS="-cJvf"
if [ "${RELEASE_NAME}" = "mainline" ]; then
DIR_NAME="${REV_NAME}"
else
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
fi
mkdir "$DIR_NAME"
cp build/bin/yuzu-cmd "$DIR_NAME"
cp build/bin/yuzu "$DIR_NAME"
. .ci/scripts/common/post-upload.sh
+4 -1
View File
@@ -12,6 +12,9 @@ jobs:
windows:
BuildSuffix: 'windows-mingw'
ScriptFolder: 'windows'
clang:
BuildSuffix: 'clang'
ScriptFolder: 'clang'
linux:
BuildSuffix: 'linux'
ScriptFolder: 'linux'
@@ -24,4 +27,4 @@ jobs:
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
version: $(parameters.version)
version: $(parameters.version)
+2 -2
View File
@@ -27,7 +27,7 @@
url = https://github.com/ReinUsesLisp/sirit
[submodule "mbedtls"]
path = externals/mbedtls
url = https://github.com/DarkLordZach/mbedtls
url = https://github.com/yuzu-emu/mbedtls
[submodule "libzip"]
path = externals/libzip/libzip
url = https://github.com/nih-at/libzip.git
@@ -37,6 +37,6 @@
[submodule "opus"]
path = externals/opus/opus
url = https://github.com/xiph/opus.git
[submodule "externals/ffmpeg"]
[submodule "ffmpeg"]
path = externals/ffmpeg
url = https://git.ffmpeg.org/ffmpeg.git
+1 -1
View File
@@ -504,7 +504,7 @@ if (YUZU_USE_BUNDLED_FFMPEG)
endif()
else() # WIN32
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-4.2.1")
set(FFmpeg_EXT_NAME "ffmpeg-4.3.1")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
+2 -6
View File
@@ -1,10 +1,6 @@
function(copy_yuzu_FFmpeg_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST}
avcodec-58.dll
avutil-56.dll
swresample-3.dll
swscale-5.dll
)
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
endfunction(copy_yuzu_FFmpeg_deps)
+1 -1
View File
@@ -1,7 +1,7 @@
[Icon Theme]
Name=colorful_dark
Comment=Colorful theme (Dark style)
Inherits=default
Inherits=colorful
Directories=16x16
[16x16]
+1 -1
View File
@@ -1,7 +1,7 @@
[Icon Theme]
Name=colorful_midnight_blue
Comment=Colorful theme (Midnight Blue style)
Inherits=default
Inherits=colorful
Directories=16x16
[16x16]
-4
View File
@@ -1257,10 +1257,6 @@ QComboBox::item:alternate {
background: #19232D;
}
QComboBox::item:checked {
font-weight: bold;
}
QComboBox::item:selected {
border: 0px solid transparent;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

+3
View File
@@ -5156,6 +5156,9 @@ GLAPI PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv;
typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f);
GLAPI PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed;
#define glDepthRangeIndexed glad_glDepthRangeIndexed
typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC)(GLuint index, GLdouble n, GLdouble f);
GLAPI PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV;
#define glDepthRangeIndexeddNV glad_glDepthRangeIndexeddNV
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat *data);
GLAPI PFNGLGETFLOATI_VPROC glad_glGetFloati_v;
#define glGetFloati_v glad_glGetFloati_v
+2
View File
@@ -1044,6 +1044,7 @@ PFNGLDEPTHMASKPROC glad_glDepthMask = NULL;
PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL;
PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv = NULL;
PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL;
PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV = NULL;
PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
PFNGLDISABLEPROC glad_glDisable = NULL;
@@ -7971,6 +7972,7 @@ static void load_GL_NV_depth_buffer_float(GLADloadproc load) {
glad_glDepthRangedNV = (PFNGLDEPTHRANGEDNVPROC)load("glDepthRangedNV");
glad_glClearDepthdNV = (PFNGLCLEARDEPTHDNVPROC)load("glClearDepthdNV");
glad_glDepthBoundsdNV = (PFNGLDEPTHBOUNDSDNVPROC)load("glDepthBoundsdNV");
glad_glDepthRangeIndexeddNV = (PFNGLDEPTHRANGEINDEXEDDNVPROC)load("glDepthRangeIndexeddNV");
}
static void load_GL_NV_draw_texture(GLADloadproc load) {
if(!GLAD_GL_NV_draw_texture) return;
+2
View File
@@ -27,6 +27,7 @@ if (MSVC)
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /utf-8 - Set source and execution character sets to UTF-8
# /volatile:iso - Use strict standards-compliant volatile semantics.
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
# /Zc:inline - Let codegen omit inline functions in object files
@@ -38,6 +39,7 @@ if (MSVC)
/permissive-
/EHsc
/std:c++latest
/utf-8
/volatile:iso
/Zc:externConstexpr
/Zc:inline
+2
View File
@@ -15,6 +15,8 @@ add_library(audio_core STATIC
command_generator.cpp
command_generator.h
common.h
delay_line.cpp
delay_line.h
effect_context.cpp
effect_context.h
info_updater.cpp
+340 -17
View File
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cmath>
#include <numbers>
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/command_generator.h"
#include "audio_core/effect_context.h"
@@ -13,6 +15,20 @@ namespace AudioCore {
namespace {
constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
using DelayLineTimes = std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>;
constexpr DelayLineTimes FDN_MIN_DELAY_LINE_TIMES{5.0f, 6.0f, 13.0f, 14.0f};
constexpr DelayLineTimes FDN_MAX_DELAY_LINE_TIMES{45.704f, 82.782f, 149.94f, 271.58f};
constexpr DelayLineTimes DECAY0_MAX_DELAY_LINE_TIMES{17.0f, 13.0f, 9.0f, 7.0f};
constexpr DelayLineTimes DECAY1_MAX_DELAY_LINE_TIMES{19.0f, 11.0f, 10.0f, 6.0f};
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_TAP_TIMES{
0.017136f, 0.059154f, 0.161733f, 0.390186f, 0.425262f, 0.455411f, 0.689737f,
0.745910f, 0.833844f, 0.859502f, 0.000000f, 0.075024f, 0.168788f, 0.299901f,
0.337443f, 0.371903f, 0.599011f, 0.716741f, 0.817859f, 0.851664f};
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{
0.67096f, 0.61027f, 1.0f, 0.35680f, 0.68361f, 0.65978f, 0.51939f,
0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.38270f,
0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
template <std::size_t N>
void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
@@ -65,6 +81,154 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
}
}
float Pow10(float x) {
if (x >= 0.0f) {
return 1.0f;
} else if (x <= -5.3f) {
return 0.0f;
}
return std::pow(10.0f, x);
}
float SinD(float degrees) {
return std::sin(degrees * std::numbers::pi_v<float> / 180.0f);
}
float CosD(float degrees) {
return std::cos(degrees * std::numbers::pi_v<float> / 180.0f);
}
float ToFloat(s32 sample) {
return static_cast<float>(sample) / 65536.f;
}
s32 ToS32(float sample) {
constexpr auto min = -8388608.0f;
constexpr auto max = 8388607.f;
float rescaled_sample = sample * 65536.0f;
if (rescaled_sample < min) {
rescaled_sample = min;
}
if (rescaled_sample > max) {
rescaled_sample = max;
}
return static_cast<s32>(rescaled_sample);
}
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
template <std::size_t CHANNEL_COUNT>
void ApplyReverbGeneric(I3dl2ReverbState& state,
const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
s32 sample_count) {
auto GetTapLookup = []() {
if constexpr (CHANNEL_COUNT == 1) {
return REVERB_TAP_INDEX_1CH;
} else if constexpr (CHANNEL_COUNT == 2) {
return REVERB_TAP_INDEX_2CH;
} else if constexpr (CHANNEL_COUNT == 4) {
return REVERB_TAP_INDEX_4CH;
} else if constexpr (CHANNEL_COUNT == 6) {
return REVERB_TAP_INDEX_6CH;
}
};
const auto& tap_index_lut = GetTapLookup();
for (s32 sample = 0; sample < sample_count; sample++) {
std::array<f32, CHANNEL_COUNT> out_samples{};
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fsamp{};
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> mixed{};
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> osamp{};
// Mix everything into a single sample
s32 temp_mixed_sample = 0;
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
temp_mixed_sample += input[i][sample];
}
const auto current_sample = ToFloat(temp_mixed_sample);
const auto early_tap = state.early_delay_line.TapOut(state.early_to_late_taps);
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_TAPS; i++) {
const auto tapped_samp =
state.early_delay_line.TapOut(state.early_tap_steps[i]) * EARLY_GAIN[i];
out_samples[tap_index_lut[i]] += tapped_samp;
if constexpr (CHANNEL_COUNT == 6) {
// handle lfe
out_samples[5] += tapped_samp;
}
}
state.lowpass_0 = current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1;
state.early_delay_line.Tick(state.lowpass_0);
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
out_samples[i] *= state.early_gain;
}
// Two channel seems to apply a latet gain, we require to save this
f32 filter{};
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
filter = state.fdn_delay_line[i].GetOutputSample();
const auto computed = filter * state.lpf_coefficients[0][i] + state.shelf_filter[i];
state.shelf_filter[i] =
filter * state.lpf_coefficients[1][i] + computed * state.lpf_coefficients[2][i];
fsamp[i] = computed;
}
// Mixing matrix
mixed[0] = fsamp[1] + fsamp[2];
mixed[1] = -fsamp[0] - fsamp[3];
mixed[2] = fsamp[0] - fsamp[3];
mixed[3] = fsamp[1] - fsamp[2];
if constexpr (CHANNEL_COUNT == 2) {
for (auto& mix : mixed) {
mix *= (filter * state.late_gain);
}
}
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
const auto late = early_tap * state.late_gain;
osamp[i] = state.decay_delay_line0[i].Tick(late + mixed[i]);
osamp[i] = state.decay_delay_line1[i].Tick(osamp[i]);
state.fdn_delay_line[i].Tick(osamp[i]);
}
if constexpr (CHANNEL_COUNT == 1) {
output[0][sample] = ToS32(state.dry_gain * ToFloat(input[0][sample]) +
(out_samples[0] + osamp[0] + osamp[1]));
} else if constexpr (CHANNEL_COUNT == 2 || CHANNEL_COUNT == 4) {
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
output[i][sample] =
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
}
} else if constexpr (CHANNEL_COUNT == 6) {
const auto temp_center = state.center_delay_line.Tick(0.5f * (osamp[2] - osamp[3]));
for (std::size_t i = 0; i < 4; i++) {
output[i][sample] =
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
}
output[4][sample] =
ToS32(state.dry_gain * ToFloat(input[4][sample]) + (out_samples[4] + temp_center));
output[5][sample] =
ToS32(state.dry_gain * ToFloat(input[5][sample]) + (out_samples[5] + osamp[3]));
}
}
}
} // namespace
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
@@ -271,11 +435,10 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
}
// Generate biquad filter
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
// dsp_state.biquad_filter_state,
// mix_buffer_count + channel, mix_buffer_count +
// channel, worker_params.sample_count,
// voice_info.GetInParams().node_id);
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
// dsp_state.biquad_filter_state,
// mix_buffer_count + channel, mix_buffer_count + channel,
// worker_params.sample_count, voice_info.GetInParams().node_id);
}
}
@@ -376,21 +539,54 @@ void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
bool enabled) {
if (!enabled) {
auto* reverb = dynamic_cast<EffectI3dl2Reverb*>(info);
const auto& params = reverb->GetParams();
auto& state = reverb->GetState();
const auto channel_count = params.channel_count;
if (channel_count != 1 && channel_count != 2 && channel_count != 4 && channel_count != 6) {
return;
}
const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
const auto channel_count = params.channel_count;
std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
const auto status = params.status;
for (s32 i = 0; i < channel_count; i++) {
// TODO(ogniK): Actually implement reverb
/*
if (params.input[i] != params.output[i]) {
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
}*/
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
std::memset(output, 0, worker_params.sample_count * sizeof(s32));
input[i] = GetMixBuffer(mix_buffer_offset + params.input[i]);
output[i] = GetMixBuffer(mix_buffer_offset + params.output[i]);
}
if (enabled) {
if (status == ParameterStatus::Initialized) {
InitializeI3dl2Reverb(reverb->GetParams(), state, info->GetWorkBuffer());
} else if (status == ParameterStatus::Updating) {
UpdateI3dl2Reverb(reverb->GetParams(), state, false);
}
}
if (enabled) {
switch (channel_count) {
case 1:
ApplyReverbGeneric<1>(state, input, output, worker_params.sample_count);
break;
case 2:
ApplyReverbGeneric<2>(state, input, output, worker_params.sample_count);
break;
case 4:
ApplyReverbGeneric<4>(state, input, output, worker_params.sample_count);
break;
case 6:
ApplyReverbGeneric<6>(state, input, output, worker_params.sample_count);
break;
}
} else {
for (s32 i = 0; i < channel_count; i++) {
// Only copy if the buffer input and output do not match!
if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32));
}
}
}
}
@@ -528,6 +724,133 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
return sample_count;
}
void CommandGenerator::InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
std::vector<u8>& work_buffer) {
// Reset state
state.lowpass_0 = 0.0f;
state.lowpass_1 = 0.0f;
state.lowpass_2 = 0.0f;
state.early_delay_line.Reset();
state.early_tap_steps.fill(0);
state.early_gain = 0.0f;
state.late_gain = 0.0f;
state.early_to_late_taps = 0;
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
state.fdn_delay_line[i].Reset();
state.decay_delay_line0[i].Reset();
state.decay_delay_line1[i].Reset();
}
state.last_reverb_echo = 0.0f;
state.center_delay_line.Reset();
for (auto& coef : state.lpf_coefficients) {
coef.fill(0.0f);
}
state.shelf_filter.fill(0.0f);
state.dry_gain = 0.0f;
const auto sample_rate = info.sample_rate / 1000;
f32* work_buffer_ptr = reinterpret_cast<f32*>(work_buffer.data());
s32 delay_samples{};
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
delay_samples =
AudioCommon::CalculateDelaySamples(sample_rate, FDN_MAX_DELAY_LINE_TIMES[i]);
state.fdn_delay_line[i].Initialize(delay_samples, work_buffer_ptr);
work_buffer_ptr += delay_samples + 1;
delay_samples =
AudioCommon::CalculateDelaySamples(sample_rate, DECAY0_MAX_DELAY_LINE_TIMES[i]);
state.decay_delay_line0[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
work_buffer_ptr += delay_samples + 1;
delay_samples =
AudioCommon::CalculateDelaySamples(sample_rate, DECAY1_MAX_DELAY_LINE_TIMES[i]);
state.decay_delay_line1[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
work_buffer_ptr += delay_samples + 1;
}
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 5.0f);
state.center_delay_line.Initialize(delay_samples, work_buffer_ptr);
work_buffer_ptr += delay_samples + 1;
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 400.0f);
state.early_delay_line.Initialize(delay_samples, work_buffer_ptr);
UpdateI3dl2Reverb(info, state, true);
}
void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
bool should_clear) {
state.dry_gain = info.dry_gain;
state.shelf_filter.fill(0.0f);
state.lowpass_0 = 0.0f;
state.early_gain = Pow10(std::min(info.room + info.reflection, 5000.0f) / 2000.0f);
state.late_gain = Pow10(std::min(info.room + info.reverb, 5000.0f) / 2000.0f);
const auto sample_rate = info.sample_rate / 1000;
const f32 hf_gain = Pow10(info.room_hf / 2000.0f);
if (hf_gain >= 1.0f) {
state.lowpass_2 = 1.0f;
state.lowpass_1 = 0.0f;
} else {
const auto a = 1.0f - hf_gain;
const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference /
static_cast<f32>(info.sample_rate)));
const auto c = std::sqrt(b * b - 4.0f * a * a);
state.lowpass_1 = (b - c) / (2.0f * a);
state.lowpass_2 = 1.0f - state.lowpass_1;
}
state.early_to_late_taps = AudioCommon::CalculateDelaySamples(
sample_rate, 1000.0f * (info.reflection_delay + info.reverb_delay));
state.last_reverb_echo = 0.6f * info.diffusion * 0.01f;
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
const auto length =
FDN_MIN_DELAY_LINE_TIMES[i] +
(info.density / 100.0f) * (FDN_MAX_DELAY_LINE_TIMES[i] - FDN_MIN_DELAY_LINE_TIMES[i]);
state.fdn_delay_line[i].SetDelay(AudioCommon::CalculateDelaySamples(sample_rate, length));
const auto delay_sample_counts = state.fdn_delay_line[i].GetDelay() +
state.decay_delay_line0[i].GetDelay() +
state.decay_delay_line1[i].GetDelay();
float a = (-60.0f * static_cast<f32>(delay_sample_counts)) /
(info.decay_time * static_cast<f32>(info.sample_rate));
float b = a / info.hf_decay_ratio;
float c = CosD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate)) /
SinD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate));
float d = Pow10((b - a) / 40.0f);
float e = Pow10((b + a) / 40.0f) * 0.7071f;
state.lpf_coefficients[0][i] = e * ((d * c) + 1.0f) / (c + d);
state.lpf_coefficients[1][i] = e * (1.0f - (d * c)) / (c + d);
state.lpf_coefficients[2][i] = (c - d) / (c + d);
state.decay_delay_line0[i].SetCoefficient(state.last_reverb_echo);
state.decay_delay_line1[i].SetCoefficient(-0.9f * state.last_reverb_echo);
}
if (should_clear) {
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
state.fdn_delay_line[i].Clear();
state.decay_delay_line0[i].Clear();
state.decay_delay_line1[i].Clear();
}
state.early_delay_line.Clear();
state.center_delay_line.Clear();
}
const auto max_early_delay = state.early_delay_line.GetMaxDelay();
const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f);
for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
const auto length = AudioCommon::CalculateDelaySamples(
sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
state.early_tap_steps[tap] = std::min(length, max_early_delay);
}
}
void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
s32 channel, s32 node_id) {
const auto last = static_cast<s32>(last_volume * 32768.0f);
+5
View File
@@ -21,6 +21,8 @@ class ServerMixInfo;
class EffectContext;
class EffectBase;
struct AuxInfoDSP;
struct I3dl2ReverbParams;
struct I3dl2ReverbState;
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
class CommandGenerator {
@@ -80,6 +82,9 @@ private:
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
u32 sample_count, u32 read_offset, u32 read_count);
void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
std::vector<u8>& work_buffer);
void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
// DSP Code
s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
s32 channel, std::size_t mix_offset);
+23
View File
@@ -33,6 +33,29 @@ constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this
// and our const ends up being 0x3f04, the 4 bytes are most
// likely the sample history
constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f;
constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f;
constexpr std::size_t I3DL2REVERB_TAPS = 20;
constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4;
using Fractional = s32;
template <typename T>
constexpr Fractional ToFractional(T x) {
return static_cast<Fractional>(x * static_cast<T>(0x4000));
}
constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) {
return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14);
}
constexpr s32 FractionalToFixed(Fractional x) {
const auto s = x & (1 << 13);
return static_cast<s32>(x >> 14) + s;
}
constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) {
return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time)));
}
static constexpr u32 VersionFromRevision(u32_le rev) {
// "REV7" -> 7
+104
View File
@@ -0,0 +1,104 @@
#include <cstring>
#include "audio_core/delay_line.h"
namespace AudioCore {
DelayLineBase::DelayLineBase() = default;
DelayLineBase::~DelayLineBase() = default;
void DelayLineBase::Initialize(s32 max_delay_, float* src_buffer) {
buffer = src_buffer;
buffer_end = buffer + max_delay_;
max_delay = max_delay_;
output = buffer;
SetDelay(max_delay_);
Clear();
}
void DelayLineBase::SetDelay(s32 new_delay) {
if (max_delay < new_delay) {
return;
}
delay = new_delay;
input = (buffer + ((output - buffer) + new_delay) % (max_delay + 1));
}
s32 DelayLineBase::GetDelay() const {
return delay;
}
s32 DelayLineBase::GetMaxDelay() const {
return max_delay;
}
f32 DelayLineBase::TapOut(s32 last_sample) {
const float* ptr = input - (last_sample + 1);
if (ptr < buffer) {
ptr += (max_delay + 1);
}
return *ptr;
}
f32 DelayLineBase::Tick(f32 sample) {
*(input++) = sample;
const auto out_sample = *(output++);
if (buffer_end < input) {
input = buffer;
}
if (buffer_end < output) {
output = buffer;
}
return out_sample;
}
float* DelayLineBase::GetInput() {
return input;
}
const float* DelayLineBase::GetInput() const {
return input;
}
f32 DelayLineBase::GetOutputSample() const {
return *output;
}
void DelayLineBase::Clear() {
std::memset(buffer, 0, sizeof(float) * max_delay);
}
void DelayLineBase::Reset() {
buffer = nullptr;
buffer_end = nullptr;
max_delay = 0;
input = nullptr;
output = nullptr;
delay = 0;
}
DelayLineAllPass::DelayLineAllPass() = default;
DelayLineAllPass::~DelayLineAllPass() = default;
void DelayLineAllPass::Initialize(u32 delay_, float coeffcient_, f32* src_buffer) {
DelayLineBase::Initialize(delay_, src_buffer);
SetCoefficient(coeffcient_);
}
void DelayLineAllPass::SetCoefficient(float coeffcient_) {
coefficient = coeffcient_;
}
f32 DelayLineAllPass::Tick(f32 sample) {
const auto temp = sample - coefficient * *output;
return coefficient * temp + DelayLineBase::Tick(temp);
}
void DelayLineAllPass::Reset() {
coefficient = 0.0f;
DelayLineBase::Reset();
}
} // namespace AudioCore
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include "common/common_types.h"
namespace AudioCore {
class DelayLineBase {
public:
DelayLineBase();
~DelayLineBase();
void Initialize(s32 max_delay_, float* src_buffer);
void SetDelay(s32 new_delay);
s32 GetDelay() const;
s32 GetMaxDelay() const;
f32 TapOut(s32 last_sample);
f32 Tick(f32 sample);
float* GetInput();
const float* GetInput() const;
f32 GetOutputSample() const;
void Clear();
void Reset();
protected:
float* buffer{nullptr};
float* buffer_end{nullptr};
s32 max_delay{};
float* input{nullptr};
float* output{nullptr};
s32 delay{};
};
class DelayLineAllPass final : public DelayLineBase {
public:
DelayLineAllPass();
~DelayLineAllPass();
void Initialize(u32 delay, float coeffcient_, f32* src_buffer);
void SetCoefficient(float coeffcient_);
f32 Tick(f32 sample);
void Reset();
private:
float coefficient{};
};
} // namespace AudioCore
+22
View File
@@ -90,6 +90,14 @@ s32 EffectBase::GetProcessingOrder() const {
return processing_order;
}
std::vector<u8>& EffectBase::GetWorkBuffer() {
return work_buffer;
}
const std::vector<u8>& EffectBase::GetWorkBuffer() const {
return work_buffer;
}
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
@@ -117,6 +125,12 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
usage = UsageState::Initialized;
params.status = ParameterStatus::Initialized;
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
if (!skipped) {
auto& cur_work_buffer = GetWorkBuffer();
// Has two buffers internally
cur_work_buffer.resize(in_params.buffer_size * 2);
std::fill(cur_work_buffer.begin(), cur_work_buffer.end(), 0);
}
}
}
@@ -129,6 +143,14 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
GetParams().status = ParameterStatus::Updated;
}
I3dl2ReverbState& EffectI3dl2Reverb::GetState() {
return state;
}
const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const {
return state;
}
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
EffectBiquadFilter::~EffectBiquadFilter() = default;
+30 -1
View File
@@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "audio_core/common.h"
#include "audio_core/delay_line.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -194,6 +195,8 @@ public:
[[nodiscard]] bool IsEnabled() const;
[[nodiscard]] s32 GetMixID() const;
[[nodiscard]] s32 GetProcessingOrder() const;
[[nodiscard]] std::vector<u8>& GetWorkBuffer();
[[nodiscard]] const std::vector<u8>& GetWorkBuffer() const;
protected:
UsageState usage{UsageState::Invalid};
@@ -201,6 +204,7 @@ protected:
s32 mix_id{};
s32 processing_order{};
bool enabled = false;
std::vector<u8> work_buffer{};
};
template <typename T>
@@ -212,7 +216,7 @@ public:
return internal_params;
}
const I3dl2ReverbParams& GetParams() const {
const T& GetParams() const {
return internal_params;
}
@@ -229,6 +233,27 @@ public:
void UpdateForCommandGeneration() override;
};
struct I3dl2ReverbState {
f32 lowpass_0{};
f32 lowpass_1{};
f32 lowpass_2{};
DelayLineBase early_delay_line{};
std::array<u32, AudioCommon::I3DL2REVERB_TAPS> early_tap_steps{};
f32 early_gain{};
f32 late_gain{};
u32 early_to_late_taps{};
std::array<DelayLineBase, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fdn_delay_line{};
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line0{};
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line1{};
f32 last_reverb_echo{};
DelayLineBase center_delay_line{};
std::array<std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>, 3> lpf_coefficients{};
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> shelf_filter{};
f32 dry_gain{};
};
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
public:
explicit EffectI3dl2Reverb();
@@ -237,8 +262,12 @@ public:
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
I3dl2ReverbState& GetState();
const I3dl2ReverbState& GetState() const;
private:
bool skipped = false;
I3dl2ReverbState state{};
};
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
+1 -1
View File
@@ -167,8 +167,8 @@ add_library(common STATIC
threadsafe_queue.h
time_zone.cpp
time_zone.h
tiny_mt.h
tree.h
uint128.cpp
uint128.h
uuid.cpp
uuid.h
+5
View File
@@ -42,6 +42,11 @@ requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, si
return (value & mask) == 0;
}
template <typename T, typename U>
requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
return (x + (y - 1)) / y;
}
template <typename T, size_t Align = 16>
class AlignmentAllocator {
public:
+87 -91
View File
@@ -28,8 +28,10 @@
// compromising on hash quality.
#include <algorithm>
#include <string.h> // for memcpy and memset
#include "cityhash.h"
#include <cstring>
#include <utility>
#include "common/cityhash.h"
#include "common/swap.h"
// #include "config.h"
@@ -42,21 +44,17 @@
using namespace std;
typedef uint8_t uint8;
typedef uint32_t uint32;
typedef uint64_t uint64;
namespace Common {
static uint64 UNALIGNED_LOAD64(const char* p) {
uint64 result;
memcpy(&result, p, sizeof(result));
static u64 unaligned_load64(const char* p) {
u64 result;
std::memcpy(&result, p, sizeof(result));
return result;
}
static uint32 UNALIGNED_LOAD32(const char* p) {
uint32 result;
memcpy(&result, p, sizeof(result));
static u32 unaligned_load32(const char* p) {
u32 result;
std::memcpy(&result, p, sizeof(result));
return result;
}
@@ -76,64 +74,64 @@ static uint32 UNALIGNED_LOAD32(const char* p) {
#endif
#endif
static uint64 Fetch64(const char* p) {
return uint64_in_expected_order(UNALIGNED_LOAD64(p));
static u64 Fetch64(const char* p) {
return uint64_in_expected_order(unaligned_load64(p));
}
static uint32 Fetch32(const char* p) {
return uint32_in_expected_order(UNALIGNED_LOAD32(p));
static u32 Fetch32(const char* p) {
return uint32_in_expected_order(unaligned_load32(p));
}
// Some primes between 2^63 and 2^64 for various uses.
static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
static const uint64 k1 = 0xb492b66fbe98f273ULL;
static const uint64 k2 = 0x9ae16a3b2f90404fULL;
static constexpr u64 k0 = 0xc3a5c85c97cb3127ULL;
static constexpr u64 k1 = 0xb492b66fbe98f273ULL;
static constexpr u64 k2 = 0x9ae16a3b2f90404fULL;
// Bitwise right rotate. Normally this will compile to a single
// instruction, especially if the shift is a manifest constant.
static uint64 Rotate(uint64 val, int shift) {
static u64 Rotate(u64 val, int shift) {
// Avoid shifting by 64: doing so yields an undefined result.
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
}
static uint64 ShiftMix(uint64 val) {
static u64 ShiftMix(u64 val) {
return val ^ (val >> 47);
}
static uint64 HashLen16(uint64 u, uint64 v) {
return Hash128to64(uint128(u, v));
static u64 HashLen16(u64 u, u64 v) {
return Hash128to64(u128{u, v});
}
static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
static u64 HashLen16(u64 u, u64 v, u64 mul) {
// Murmur-inspired hashing.
uint64 a = (u ^ v) * mul;
u64 a = (u ^ v) * mul;
a ^= (a >> 47);
uint64 b = (v ^ a) * mul;
u64 b = (v ^ a) * mul;
b ^= (b >> 47);
b *= mul;
return b;
}
static uint64 HashLen0to16(const char* s, std::size_t len) {
static u64 HashLen0to16(const char* s, size_t len) {
if (len >= 8) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) + k2;
uint64 b = Fetch64(s + len - 8);
uint64 c = Rotate(b, 37) * mul + a;
uint64 d = (Rotate(a, 25) + b) * mul;
u64 mul = k2 + len * 2;
u64 a = Fetch64(s) + k2;
u64 b = Fetch64(s + len - 8);
u64 c = Rotate(b, 37) * mul + a;
u64 d = (Rotate(a, 25) + b) * mul;
return HashLen16(c, d, mul);
}
if (len >= 4) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch32(s);
u64 mul = k2 + len * 2;
u64 a = Fetch32(s);
return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
}
if (len > 0) {
uint8 a = s[0];
uint8 b = s[len >> 1];
uint8 c = s[len - 1];
uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2);
u8 a = s[0];
u8 b = s[len >> 1];
u8 c = s[len - 1];
u32 y = static_cast<u32>(a) + (static_cast<u32>(b) << 8);
u32 z = static_cast<u32>(len) + (static_cast<u32>(c) << 2);
return ShiftMix(y * k2 ^ z * k0) * k2;
}
return k2;
@@ -141,22 +139,21 @@ static uint64 HashLen0to16(const char* s, std::size_t len) {
// This probably works well for 16-byte strings as well, but it may be overkill
// in that case.
static uint64 HashLen17to32(const char* s, std::size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k1;
uint64 b = Fetch64(s + 8);
uint64 c = Fetch64(s + len - 8) * mul;
uint64 d = Fetch64(s + len - 16) * k2;
static u64 HashLen17to32(const char* s, size_t len) {
u64 mul = k2 + len * 2;
u64 a = Fetch64(s) * k1;
u64 b = Fetch64(s + 8);
u64 c = Fetch64(s + len - 8) * mul;
u64 d = Fetch64(s + len - 16) * k2;
return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul);
}
// Return a 16-byte hash for 48 bytes. Quick and dirty.
// Callers do best to use "random-looking" values for a and b.
static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a,
uint64 b) {
static pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
a += w;
b = Rotate(b + a + z, 21);
uint64 c = a;
u64 c = a;
a += x;
a += y;
b += Rotate(a, 44);
@@ -164,34 +161,34 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y,
}
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) {
static pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
b);
}
// Return an 8-byte hash for 33 to 64 bytes.
static uint64 HashLen33to64(const char* s, std::size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k2;
uint64 b = Fetch64(s + 8);
uint64 c = Fetch64(s + len - 24);
uint64 d = Fetch64(s + len - 32);
uint64 e = Fetch64(s + 16) * k2;
uint64 f = Fetch64(s + 24) * 9;
uint64 g = Fetch64(s + len - 8);
uint64 h = Fetch64(s + len - 16) * mul;
uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
uint64 v = ((a + g) ^ d) + f + 1;
uint64 w = swap64((u + v) * mul) + h;
uint64 x = Rotate(e + f, 42) + c;
uint64 y = (swap64((v + w) * mul) + g) * mul;
uint64 z = e + f + c;
static u64 HashLen33to64(const char* s, size_t len) {
u64 mul = k2 + len * 2;
u64 a = Fetch64(s) * k2;
u64 b = Fetch64(s + 8);
u64 c = Fetch64(s + len - 24);
u64 d = Fetch64(s + len - 32);
u64 e = Fetch64(s + 16) * k2;
u64 f = Fetch64(s + 24) * 9;
u64 g = Fetch64(s + len - 8);
u64 h = Fetch64(s + len - 16) * mul;
u64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
u64 v = ((a + g) ^ d) + f + 1;
u64 w = swap64((u + v) * mul) + h;
u64 x = Rotate(e + f, 42) + c;
u64 y = (swap64((v + w) * mul) + g) * mul;
u64 z = e + f + c;
a = swap64((x + z) * mul + y) + b;
b = ShiftMix((z + a) * mul + d + h) * mul;
return b + x;
}
uint64 CityHash64(const char* s, std::size_t len) {
u64 CityHash64(const char* s, size_t len) {
if (len <= 32) {
if (len <= 16) {
return HashLen0to16(s, len);
@@ -204,15 +201,15 @@ uint64 CityHash64(const char* s, std::size_t len) {
// For strings over 64 bytes we hash the end first, and then as we
// loop we keep 56 bytes of state: v, w, x, y, and z.
uint64 x = Fetch64(s + len - 40);
uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
u64 x = Fetch64(s + len - 40);
u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
x = x * k1 + Fetch64(s);
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
len = (len - 1) & ~static_cast<std::size_t>(63);
len = (len - 1) & ~static_cast<size_t>(63);
do {
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
@@ -229,21 +226,21 @@ uint64 CityHash64(const char* s, std::size_t len) {
HashLen16(v.second, w.second) + x);
}
uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
u64 CityHash64WithSeed(const char* s, size_t len, u64 seed) {
return CityHash64WithSeeds(s, len, k2, seed);
}
uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) {
u64 CityHash64WithSeeds(const char* s, size_t len, u64 seed0, u64 seed1) {
return HashLen16(CityHash64(s, len) - seed0, seed1);
}
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
// of any length representable in signed long. Based on City and Murmur.
static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
uint64 a = Uint128Low64(seed);
uint64 b = Uint128High64(seed);
uint64 c = 0;
uint64 d = 0;
static u128 CityMurmur(const char* s, size_t len, u128 seed) {
u64 a = seed[0];
u64 b = seed[1];
u64 c = 0;
u64 d = 0;
signed long l = static_cast<long>(len) - 16;
if (l <= 0) { // len <= 16
a = ShiftMix(a * k1) * k1;
@@ -266,20 +263,20 @@ static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
}
a = HashLen16(a, c);
b = HashLen16(d, b);
return uint128(a ^ b, HashLen16(b, a));
return u128{a ^ b, HashLen16(b, a)};
}
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
u128 CityHash128WithSeed(const char* s, size_t len, u128 seed) {
if (len < 128) {
return CityMurmur(s, len, seed);
}
// We expect len >= 128 to be the common case. Keep 56 bytes of state:
// v, w, x, y, and z.
pair<uint64, uint64> v, w;
uint64 x = Uint128Low64(seed);
uint64 y = Uint128High64(seed);
uint64 z = len * k1;
pair<u64, u64> v, w;
u64 x = seed[0];
u64 y = seed[1];
u64 z = len * k1;
v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
w.first = Rotate(y + z, 35) * k1 + x;
@@ -313,7 +310,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
w.first *= 9;
v.first *= k0;
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
for (std::size_t tail_done = 0; tail_done < len;) {
for (size_t tail_done = 0; tail_done < len;) {
tail_done += 32;
y = Rotate(x + y, 42) * k0 + v.second;
w.first += Fetch64(s + len - tail_done + 16);
@@ -328,13 +325,12 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
// different 56-byte-to-8-byte hashes to get a 16-byte final result.
x = HashLen16(x, v.first);
y = HashLen16(y + z, w.first);
return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
return u128{HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)};
}
uint128 CityHash128(const char* s, std::size_t len) {
return len >= 16
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
: CityHash128WithSeed(s, len, uint128(k0, k1));
u128 CityHash128(const char* s, size_t len) {
return len >= 16 ? CityHash128WithSeed(s + 16, len - 16, u128{Fetch64(s), Fetch64(s + 8) + k0})
: CityHash128WithSeed(s, len, u128{k0, k1});
}
} // namespace Common
+11 -22
View File
@@ -62,49 +62,38 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <utility>
#include "common/common_types.h"
namespace Common {
using uint128 = std::pair<uint64_t, uint64_t>;
[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
return x.first;
}
[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
return x.second;
}
// Hash function for a byte array.
[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
[[nodiscard]] u64 CityHash64(const char* buf, size_t len);
// Hash function for a byte array. For convenience, a 64-bit seed is also
// hashed into the result.
[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
[[nodiscard]] u64 CityHash64WithSeed(const char* buf, size_t len, u64 seed);
// Hash function for a byte array. For convenience, two seeds are also
// hashed into the result.
[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
uint64_t seed1);
[[nodiscard]] u64 CityHash64WithSeeds(const char* buf, size_t len, u64 seed0, u64 seed1);
// Hash function for a byte array.
[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
[[nodiscard]] u128 CityHash128(const char* s, size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result.
[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
[[nodiscard]] u128 CityHash128WithSeed(const char* s, size_t len, u128 seed);
// Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function.
[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
[[nodiscard]] inline u64 Hash128to64(const u128& x) {
// Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
const u64 mul = 0x9ddfea08eb382d69ULL;
u64 a = (x[0] ^ x[1]) * mul;
a ^= (a >> 47);
uint64_t b = (Uint128High64(x) ^ a) * kMul;
u64 b = (x[1] ^ a) * mul;
b ^= (b >> 47);
b *= kMul;
b *= mul;
return b;
}
+5 -1
View File
@@ -52,9 +52,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
// Defined in Misc.cpp.
// Defined in misc.cpp.
[[nodiscard]] std::string GetLastErrorMsg();
// Like GetLastErrorMsg(), but passing an explicit error code.
// Defined in misc.cpp.
[[nodiscard]] std::string NativeErrorToString(int e);
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
+13 -10
View File
@@ -116,16 +116,19 @@ void Fiber::Rewind() {
boost::context::detail::jump_fcontext(impl->rewind_context, this);
}
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->impl->guard.lock();
to->impl->previous_fiber = from;
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
to.impl->guard.lock();
to.impl->previous_fiber = weak_from.lock();
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
// "from" might no longer be valid if the thread was killed
if (auto from = weak_from.lock()) {
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
+1 -1
View File
@@ -41,7 +41,7 @@ public:
/// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber.
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
+29 -15
View File
@@ -12,27 +12,41 @@
#include "common/common_funcs.h"
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
std::string GetLastErrorMsg() {
static constexpr std::size_t buff_size = 255;
char err_str[buff_size];
std::string NativeErrorToString(int e) {
#ifdef _WIN32
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
return std::string(err_str, buff_size);
#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
LPSTR err_str;
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
if (!res) {
return "(FormatMessageA failed to format error)";
}
std::string ret(err_str);
LocalFree(err_str);
return ret;
#else
char err_str[255];
#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
// Thread safe (GNU-specific)
const char* str = strerror_r(errno, err_str, buff_size);
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);
#else
// Thread safe (XSI-compliant)
const int success = strerror_r(errno, err_str, buff_size);
if (success != 0) {
return {};
int second_err = strerror_r(e, err_str, sizeof(err_str));
if (second_err != 0) {
return "(strerror_r failed to format error)";
}
return std::string(err_str);
#endif // GLIBC etc.
#endif // _WIN32
}
std::string GetLastErrorMsg() {
#ifdef _WIN32
return NativeErrorToString(GetLastError());
#else
return NativeErrorToString(errno);
#endif
}
+250
View File
@@ -0,0 +1,250 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/alignment.h"
#include "common/common_types.h"
namespace Common {
// Implementation of TinyMT (mersenne twister RNG).
// Like Nintendo, we will use the sample parameters.
class TinyMT {
public:
static constexpr std::size_t NumStateWords = 4;
struct State {
std::array<u32, NumStateWords> data{};
};
private:
static constexpr u32 ParamMat1 = 0x8F7011EE;
static constexpr u32 ParamMat2 = 0xFC78FF1F;
static constexpr u32 ParamTmat = 0x3793FDFF;
static constexpr u32 ParamMult = 0x6C078965;
static constexpr u32 ParamPlus = 0x0019660D;
static constexpr u32 ParamXor = 0x5D588B65;
static constexpr u32 TopBitmask = 0x7FFFFFFF;
static constexpr int MinimumInitIterations = 8;
static constexpr int NumDiscardedInitOutputs = 8;
static constexpr u32 XorByShifted27(u32 value) {
return value ^ (value >> 27);
}
static constexpr u32 XorByShifted30(u32 value) {
return value ^ (value >> 30);
}
private:
State state{};
private:
// Internal API.
void FinalizeInitialization() {
const u32 state0 = this->state.data[0] & TopBitmask;
const u32 state1 = this->state.data[1];
const u32 state2 = this->state.data[2];
const u32 state3 = this->state.data[3];
if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
this->state.data[0] = 'T';
this->state.data[1] = 'I';
this->state.data[2] = 'N';
this->state.data[3] = 'Y';
}
for (int i = 0; i < NumDiscardedInitOutputs; i++) {
this->GenerateRandomU32();
}
}
u32 GenerateRandomU24() {
return (this->GenerateRandomU32() >> 8);
}
static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) {
u32& state0 = state->data[(index + 0) % NumStateWords];
u32& state1 = state->data[(index + 1) % NumStateWords];
u32& state2 = state->data[(index + 2) % NumStateWords];
u32& state3 = state->data[(index + 3) % NumStateWords];
const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
const u32 y = x + index + value;
state0 = y;
state1 += x;
state2 += y;
}
static void GenerateInitialValueXor(TinyMT::State* state, int index) {
u32& state0 = state->data[(index + 0) % NumStateWords];
u32& state1 = state->data[(index + 1) % NumStateWords];
u32& state2 = state->data[(index + 2) % NumStateWords];
u32& state3 = state->data[(index + 3) % NumStateWords];
const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
const u32 y = x - index;
state0 = y;
state1 ^= x;
state2 ^= y;
}
public:
constexpr TinyMT() = default;
// Public API.
// Initialization.
void Initialize(u32 seed) {
this->state.data[0] = seed;
this->state.data[1] = ParamMat1;
this->state.data[2] = ParamMat2;
this->state.data[3] = ParamTmat;
for (int i = 1; i < MinimumInitIterations; i++) {
const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]);
this->state.data[i % NumStateWords] ^= mixed * ParamMult + i;
}
this->FinalizeInitialization();
}
void Initialize(const u32* seed, int seed_count) {
this->state.data[0] = 0;
this->state.data[1] = ParamMat1;
this->state.data[2] = ParamMat2;
this->state.data[3] = ParamTmat;
{
const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
GenerateInitialValuePlus(&this->state, 0, seed_count);
for (int i = 0; i < num_init_iterations; i++) {
GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords,
(i < seed_count) ? seed[i] : 0);
}
for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
GenerateInitialValueXor(&this->state,
(i + 1 + num_init_iterations) % NumStateWords);
}
}
this->FinalizeInitialization();
}
// State management.
void GetState(TinyMT::State& out) const {
out.data = this->state.data;
}
void SetState(const TinyMT::State& state_) {
this->state.data = state_.data;
}
// Random generation.
void GenerateRandomBytes(void* dst, std::size_t size) {
const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
const uintptr_t end = start + size;
const uintptr_t aligned_start = Common::AlignUp(start, 4);
const uintptr_t aligned_end = Common::AlignDown(end, 4);
// Make sure we're aligned.
if (start < aligned_start) {
const u32 rnd = this->GenerateRandomU32();
std::memcpy(dst, &rnd, aligned_start - start);
}
// Write as many aligned u32s as we can.
{
u32* cur_dst = reinterpret_cast<u32*>(aligned_start);
u32* const end_dst = reinterpret_cast<u32*>(aligned_end);
while (cur_dst < end_dst) {
*(cur_dst++) = this->GenerateRandomU32();
}
}
// Handle any leftover unaligned data.
if (aligned_end < end) {
const u32 rnd = this->GenerateRandomU32();
std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end);
}
}
u32 GenerateRandomU32() {
// Advance state.
const u32 x0 =
(this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2];
const u32 y0 = this->state.data[3];
const u32 x1 = x0 ^ (x0 << 1);
const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
const u32 state0 = this->state.data[1];
u32 state1 = this->state.data[2];
u32 state2 = x1 ^ (y1 << 10);
const u32 state3 = y1;
if ((y1 & 1) != 0) {
state1 ^= ParamMat1;
state2 ^= ParamMat2;
}
this->state.data[0] = state0;
this->state.data[1] = state1;
this->state.data[2] = state2;
this->state.data[3] = state3;
// Temper.
const u32 t1 = state0 + (state2 >> 8);
u32 t0 = state3 ^ t1;
if ((t1 & 1) != 0) {
t0 ^= ParamTmat;
}
return t0;
}
u64 GenerateRandomU64() {
const u32 lo = this->GenerateRandomU32();
const u32 hi = this->GenerateRandomU32();
return (u64{hi} << 32) | u64{lo};
}
float GenerateRandomF32() {
// Floats have 24 bits of mantissa.
constexpr u32 MantissaBits = 24;
return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits));
}
double GenerateRandomF64() {
// Doubles have 53 bits of mantissa.
// The smart way to generate 53 bits of random would be to use 32 bits
// from the first rnd32() call, and then 21 from the second.
// Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32()
// call, and (32 - 6) bits from the second. We'll do what they do, but
// There's not a clear reason why.
constexpr u32 MantissaBits = 53;
constexpr u32 Shift1st = (64 - MantissaBits) / 2;
constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st;
const u32 first = (this->GenerateRandomU32() >> Shift1st);
const u32 second = (this->GenerateRandomU32() >> Shift2nd);
return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) *
(1.0 / (u64{1} << MantissaBits));
}
};
} // namespace Common
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(_umul128)
#pragma intrinsic(_udiv128)
#endif
#include <cstring>
#include "common/uint128.h"
namespace Common {
#ifdef _MSC_VER
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
u128 r{};
r[0] = _umul128(a, b, &r[1]);
u64 remainder;
#if _MSC_VER < 1923
return udiv128(r[1], r[0], d, &remainder);
#else
return _udiv128(r[1], r[0], d, &remainder);
#endif
}
#else
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
const u64 diva = a / d;
const u64 moda = a % d;
const u64 divb = b / d;
const u64 modb = b % d;
return diva * b + moda * divb + moda * modb / d;
}
#endif
u128 Multiply64Into128(u64 a, u64 b) {
u128 result;
#ifdef _MSC_VER
result[0] = _umul128(a, b, &result[1]);
#else
unsigned __int128 tmp = a;
tmp *= b;
std::memcpy(&result, &tmp, sizeof(u128));
#endif
return result;
}
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
u64 remainder = dividend[0] % divisor;
u64 accum = dividend[0] / divisor;
if (dividend[1] == 0)
return {accum, remainder};
// We ignore dividend[1] / divisor as that overflows
const u64 first_segment = (dividend[1] % divisor) << 32;
accum += (first_segment / divisor) << 32;
const u64 second_segment = (first_segment % divisor) << 32;
accum += (second_segment / divisor);
remainder += second_segment % divisor;
if (remainder >= divisor) {
accum++;
remainder -= divisor;
}
return {accum, remainder};
}
} // namespace Common
+102 -3
View File
@@ -4,19 +4,118 @@
#pragma once
#include <cstring>
#include <utility>
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(__umulh)
#pragma intrinsic(_umul128)
#pragma intrinsic(_udiv128)
#else
#include <x86intrin.h>
#endif
#include "common/common_types.h"
namespace Common {
// This function multiplies 2 u64 values and divides it by a u64 value.
[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
[[nodiscard]] static inline u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
#ifdef _MSC_VER
u128 r{};
r[0] = _umul128(a, b, &r[1]);
u64 remainder;
#if _MSC_VER < 1923
return udiv128(r[1], r[0], d, &remainder);
#else
return _udiv128(r[1], r[0], d, &remainder);
#endif
#else
const u64 diva = a / d;
const u64 moda = a % d;
const u64 divb = b / d;
const u64 modb = b % d;
return diva * b + moda * divb + moda * modb / d;
#endif
}
// This function multiplies 2 u64 values and produces a u128 value;
[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
[[nodiscard]] static inline u128 Multiply64Into128(u64 a, u64 b) {
u128 result;
#ifdef _MSC_VER
result[0] = _umul128(a, b, &result[1]);
#else
unsigned __int128 tmp = a;
tmp *= b;
std::memcpy(&result, &tmp, sizeof(u128));
#endif
return result;
}
[[nodiscard]] static inline u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
#ifdef __SIZEOF_INT128__
const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
return static_cast<u64>(base / divisor);
#elif defined(_M_X64) || defined(_M_ARM64)
std::array<u64, 2> r = {0, numerator};
u64 remainder;
#if _MSC_VER < 1923
return udiv128(r[1], r[0], divisor, &remainder);
#else
return _udiv128(r[1], r[0], divisor, &remainder);
#endif
#else
// This one is bit more inaccurate.
return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
#endif
}
[[nodiscard]] static inline u64 MultiplyHigh(u64 a, u64 b) {
#ifdef __SIZEOF_INT128__
return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
#elif defined(_M_X64) || defined(_M_ARM64)
return __umulh(a, b); // MSVC
#else
// Generic fallback
const u64 a_lo = u32(a);
const u64 a_hi = a >> 32;
const u64 b_lo = u32(b);
const u64 b_hi = b >> 32;
const u64 a_x_b_hi = a_hi * b_hi;
const u64 a_x_b_mid = a_hi * b_lo;
const u64 b_x_a_mid = b_hi * a_lo;
const u64 a_x_b_lo = a_lo * b_lo;
const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
32;
const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
return multhi;
#endif
}
// This function divides a u128 by a u32 value and produces two u64 values:
// the result of division and the remainder
[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
[[nodiscard]] static inline std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
u64 remainder = dividend[0] % divisor;
u64 accum = dividend[0] / divisor;
if (dividend[1] == 0)
return {accum, remainder};
// We ignore dividend[1] / divisor as that overflows
const u64 first_segment = (dividend[1] % divisor) << 32;
accum += (first_segment / divisor) << 32;
const u64 second_segment = (first_segment % divisor) << 32;
accum += (second_segment / divisor);
remainder += second_segment % divisor;
if (remainder >= divisor) {
accum++;
remainder -= divisor;
}
return {accum, remainder};
}
} // namespace Common
+2
View File
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstdint>
#include "common/uint128.h"
#include "common/wall_clock.h"
-58
View File
@@ -8,68 +8,10 @@
#include <mutex>
#include <thread>
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(__umulh)
#pragma intrinsic(_udiv128)
#else
#include <x86intrin.h>
#endif
#include "common/atomic_ops.h"
#include "common/uint128.h"
#include "common/x64/native_clock.h"
namespace {
[[nodiscard]] u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
#ifdef __SIZEOF_INT128__
const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
return static_cast<u64>(base / divisor);
#elif defined(_M_X64) || defined(_M_ARM64)
std::array<u64, 2> r = {0, numerator};
u64 remainder;
#if _MSC_VER < 1923
return udiv128(r[1], r[0], divisor, &remainder);
#else
return _udiv128(r[1], r[0], divisor, &remainder);
#endif
#else
// This one is bit more inaccurate.
return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
#endif
}
[[nodiscard]] u64 MultiplyHigh(u64 a, u64 b) {
#ifdef __SIZEOF_INT128__
return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
#elif defined(_M_X64) || defined(_M_ARM64)
return __umulh(a, b); // MSVC
#else
// Generic fallback
const u64 a_lo = u32(a);
const u64 a_hi = a >> 32;
const u64 b_lo = u32(b);
const u64 b_hi = b >> 32;
const u64 a_x_b_hi = a_hi * b_hi;
const u64 a_x_b_mid = a_hi * b_lo;
const u64 b_x_a_mid = b_hi * a_lo;
const u64 a_x_b_lo = a_lo * b_lo;
const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
32;
const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
return multhi;
#endif
}
} // namespace
namespace Common {
u64 EstimateRDTSCFrequency() {
+24 -20
View File
@@ -19,7 +19,6 @@ add_library(core STATIC
core.h
core_timing.cpp
core_timing.h
core_timing_util.cpp
core_timing_util.h
cpu_manager.cpp
cpu_manager.h
@@ -157,6 +156,8 @@ add_library(core STATIC
hle/kernel/hle_ipc.h
hle/kernel/k_address_arbiter.cpp
hle/kernel/k_address_arbiter.h
hle/kernel/k_address_space_info.cpp
hle/kernel/k_address_space_info.h
hle/kernel/k_affinity_mask.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
@@ -165,6 +166,18 @@ add_library(core STATIC
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
hle/kernel/k_memory_block.h
hle/kernel/k_memory_block_manager.cpp
hle/kernel/k_memory_block_manager.h
hle/kernel/k_memory_layout.h
hle/kernel/k_memory_manager.cpp
hle/kernel/k_memory_manager.h
hle/kernel/k_page_bitmap.h
hle/kernel/k_page_heap.cpp
hle/kernel/k_page_heap.h
hle/kernel/k_page_linked_list.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h
hle/kernel/k_priority_queue.h
hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h
@@ -176,8 +189,15 @@ add_library(core STATIC
hle/kernel/k_scoped_lock.h
hle/kernel/k_scoped_resource_reservation.h
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
hle/kernel/k_shared_memory.cpp
hle/kernel/k_shared_memory.h
hle/kernel/k_slab_heap.h
hle/kernel/k_spin_lock.cpp
hle/kernel/k_spin_lock.h
hle/kernel/k_synchronization_object.cpp
hle/kernel/k_synchronization_object.h
hle/kernel/k_system_control.cpp
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.h
@@ -185,23 +205,7 @@ add_library(core STATIC
hle/kernel/k_writable_event.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory/address_space_info.cpp
hle/kernel/memory/address_space_info.h
hle/kernel/memory/memory_block.h
hle/kernel/memory/memory_block_manager.cpp
hle/kernel/memory/memory_block_manager.h
hle/kernel/memory/memory_layout.h
hle/kernel/memory/memory_manager.cpp
hle/kernel/memory/memory_manager.h
hle/kernel/memory/memory_types.h
hle/kernel/memory/page_linked_list.h
hle/kernel/memory/page_heap.cpp
hle/kernel/memory/page_heap.h
hle/kernel/memory/page_table.cpp
hle/kernel/memory/page_table.h
hle/kernel/memory/slab_heap.h
hle/kernel/memory/system_control.cpp
hle/kernel/memory/system_control.h
hle/kernel/memory_types.h
hle/kernel/object.cpp
hle/kernel/object.h
hle/kernel/physical_core.cpp
@@ -219,8 +223,6 @@ add_library(core STATIC
hle/kernel/service_thread.h
hle/kernel/session.cpp
hle/kernel/session.h
hle/kernel/shared_memory.cpp
hle/kernel/shared_memory.h
hle/kernel/svc.cpp
hle/kernel/svc.h
hle/kernel/svc_common.h
@@ -266,6 +268,7 @@ add_library(core STATIC
hle/service/am/applets/software_keyboard.h
hle/service/am/applets/web_browser.cpp
hle/service/am/applets/web_browser.h
hle/service/am/applets/web_types.h
hle/service/am/idle.cpp
hle/service/am/idle.h
hle/service/am/omm.cpp
@@ -400,6 +403,7 @@ add_library(core STATIC
hle/service/hid/controllers/xpad.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/errors.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
hle/service/ldr/ldr.cpp
+2 -10
View File
@@ -299,25 +299,17 @@ struct System::Impl {
gpu_core->WaitIdle();
}
// Shutdown emulation session
services.reset();
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
// Close all CPU/threading state
cpu_manager.Shutdown();
// Shutdown kernel and core timing
time_manager.Shutdown();
core_timing.Shutdown();
kernel.Shutdown();
// Close app loader
app_loader.reset();
gpu_core.reset();
perf_stats.reset();
// Clear all applets
kernel.Shutdown();
applet_manager.ClearAll();
LOG_DEBUG(Core, "Shutdown OK");
-84
View File
@@ -1,84 +0,0 @@
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "core/core_timing_util.h"
#include <cinttypes>
#include <limits>
#include "common/logging/log.h"
#include "common/uint128.h"
#include "core/hardware_properties.h"
namespace Core::Timing {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE;
s64 msToCycles(std::chrono::milliseconds ms) {
if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return Hardware::BASE_CLOCK_RATE * (ms.count() / 1000);
}
return (Hardware::BASE_CLOCK_RATE * ms.count()) / 1000;
}
s64 usToCycles(std::chrono::microseconds us) {
if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return Hardware::BASE_CLOCK_RATE * (us.count() / 1000000);
}
return (Hardware::BASE_CLOCK_RATE * us.count()) / 1000000;
}
s64 nsToCycles(std::chrono::nanoseconds ns) {
const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE);
return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first;
}
u64 msToClockCycles(std::chrono::milliseconds ns) {
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
return Common::Divide128On32(temp, 1000).first;
}
u64 usToClockCycles(std::chrono::microseconds ns) {
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
return Common::Divide128On32(temp, 1000000).first;
}
u64 nsToClockCycles(std::chrono::nanoseconds ns) {
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
return Common::Divide128On32(temp, 1000000000).first;
}
u64 CpuCyclesToClockCycles(u64 ticks) {
const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ);
return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
}
std::chrono::milliseconds CyclesToMs(s64 cycles) {
const u128 temporal = Common::Multiply64Into128(cycles, 1000);
u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
return std::chrono::milliseconds(ms);
}
std::chrono::nanoseconds CyclesToNs(s64 cycles) {
const u128 temporal = Common::Multiply64Into128(cycles, 1000000000);
u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
return std::chrono::nanoseconds(ns);
}
std::chrono::microseconds CyclesToUs(s64 cycles) {
const u128 temporal = Common::Multiply64Into128(cycles, 1000000);
u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
return std::chrono::microseconds(us);
}
} // namespace Core::Timing
+47 -12
View File
@@ -1,24 +1,59 @@
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
// Licensed under GPLv2+
// Copyright 2020 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"
#include "core/hardware_properties.h"
namespace Core::Timing {
s64 msToCycles(std::chrono::milliseconds ms);
s64 usToCycles(std::chrono::microseconds us);
s64 nsToCycles(std::chrono::nanoseconds ns);
u64 msToClockCycles(std::chrono::milliseconds ns);
u64 usToClockCycles(std::chrono::microseconds ns);
u64 nsToClockCycles(std::chrono::nanoseconds ns);
std::chrono::milliseconds CyclesToMs(s64 cycles);
std::chrono::nanoseconds CyclesToNs(s64 cycles);
std::chrono::microseconds CyclesToUs(s64 cycles);
namespace detail {
constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000;
constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000;
} // namespace detail
u64 CpuCyclesToClockCycles(u64 ticks);
[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) {
return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED;
}
[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) {
return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000;
}
[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) {
return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000;
}
[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) {
return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED;
}
[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) {
return us.count() * detail::CNTFREQ_ADJUSTED / 1000;
}
[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) {
return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000;
}
[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) {
return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED;
}
[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) {
return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED);
}
[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) {
return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED);
}
[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) {
return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED);
}
} // namespace Core::Timing
+4 -4
View File
@@ -148,7 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
scheduler.RescheduleCurrentCore();
@@ -245,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
scheduler.RescheduleCurrentCore();
@@ -271,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
scheduler.Unload(scheduler.GetCurrentThread());
auto& next_scheduler = kernel.Scheduler(current_core);
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
}
// May have changed scheduler
@@ -363,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
data.is_running = true;
Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
data.is_running = false;
data.is_paused = true;
data.exit_barrier->Wait();
-2
View File
@@ -105,8 +105,6 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
}
}
}
mbedtls_cipher_finish(context, nullptr, nullptr);
}
template <typename Key, std::size_t KeySize>
+1
View File
@@ -31,6 +31,7 @@ struct ControllerParameters {
bool allow_dual_joycons{};
bool allow_left_joycon{};
bool allow_right_joycon{};
bool allow_gamecube_controller{};
};
class ControllerApplet {
@@ -2,15 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#include <array>
#include "common/assert.h"
#include "core/hle/kernel/memory/address_space_info.h"
#include "core/hle/kernel/k_address_space_info.h"
namespace Kernel::Memory {
namespace Kernel {
namespace {
@@ -28,20 +25,20 @@ enum : u64 {
};
// clang-format off
constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = AddressSpaceInfo::Type::Is32Bit, },
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Is32Bit, },
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Large64Bit, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Is32Bit },
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = AddressSpaceInfo::Type::Stack, },
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall },
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, },
}};
// clang-format on
@@ -49,7 +46,8 @@ constexpr bool IsAllowedIndexForAddress(std::size_t index) {
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid;
}
using IndexArray = std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>;
using IndexArray =
std::array<std::size_t, static_cast<std::size_t>(KAddressSpaceInfo::Type::Count)>;
constexpr IndexArray AddressSpaceIndices32Bit{
0, 1, 0, 2, 0, 3,
@@ -63,23 +61,23 @@ constexpr IndexArray AddressSpaceIndices39Bit{
9, 8, 8, 10, 12, 11,
};
constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) {
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
type != AddressSpaceInfo::Type::Stack;
constexpr bool IsAllowed32BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed36BitType(AddressSpaceInfo::Type type) {
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
type != AddressSpaceInfo::Type::Stack;
constexpr bool IsAllowed36BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) {
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Small64Bit;
constexpr bool IsAllowed39BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::MapLarge;
}
} // namespace
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
@@ -99,7 +97,7 @@ u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
return 0;
}
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
@@ -116,4 +114,4 @@ std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
return 0;
}
} // namespace Kernel::Memory
} // namespace Kernel
@@ -2,20 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/common_types.h"
namespace Kernel::Memory {
namespace Kernel {
struct AddressSpaceInfo final {
struct KAddressSpaceInfo final {
enum class Type : u32 {
Is32Bit = 0,
Small64Bit = 1,
Large64Bit = 2,
MapSmall = 0,
MapLarge = 1,
Map39Bit = 2,
Heap = 3,
Stack = 4,
Alias = 5,
@@ -31,4 +28,4 @@ struct AddressSpaceInfo final {
const Type type{};
};
} // namespace Kernel::Memory
} // namespace Kernel
@@ -2,20 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/svc_types.h"
namespace Kernel::Memory {
namespace Kernel {
enum class MemoryState : u32 {
enum class KMemoryState : u32 {
None = 0,
Mask = 0xFF,
All = ~None,
@@ -97,31 +94,31 @@ enum class MemoryState : u32 {
FlagReferenceCounted | FlagCanDebug,
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000);
static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001);
static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002);
static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03);
static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04);
static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05);
static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006);
static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08);
static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D);
static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E);
static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812);
static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214);
static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015);
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00002001);
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
static_assert(static_cast<u32>(KMemoryState::Code) == 0x00DC7E03);
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x03FEBD04);
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x037EBD05);
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x00402006);
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x00DD7E08);
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
enum class MemoryPermission : u8 {
enum class KMemoryPermission : u8 {
None = 0,
Mask = static_cast<u8>(~None),
@@ -135,9 +132,9 @@ enum class MemoryPermission : u8 {
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
enum class MemoryAttribute : u8 {
enum class KMemoryAttribute : u8 {
None = 0x00,
Mask = 0x7F,
All = Mask,
@@ -152,18 +149,18 @@ enum class MemoryAttribute : u8 {
LockedAndIpcLocked = Locked | IpcLocked,
DeviceSharedAndUncached = DeviceShared | Uncached
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
static_assert((static_cast<u8>(MemoryAttribute::Mask) &
static_cast<u8>(MemoryAttribute::DontCareMask)) == 0);
static_assert((static_cast<u8>(KMemoryAttribute::Mask) &
static_cast<u8>(KMemoryAttribute::DontCareMask)) == 0);
struct MemoryInfo {
struct KMemoryInfo {
VAddr addr{};
std::size_t size{};
MemoryState state{};
MemoryPermission perm{};
MemoryAttribute attribute{};
MemoryPermission original_perm{};
KMemoryState state{};
KMemoryPermission perm{};
KMemoryAttribute attribute{};
KMemoryPermission original_perm{};
u16 ipc_lock_count{};
u16 device_use_count{};
@@ -171,9 +168,9 @@ struct MemoryInfo {
return {
addr,
size,
static_cast<Svc::MemoryState>(state & MemoryState::Mask),
static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask),
static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask),
static_cast<Svc::MemoryState>(state & KMemoryState::Mask),
static_cast<Svc::MemoryAttribute>(attribute & KMemoryAttribute::Mask),
static_cast<Svc::MemoryPermission>(perm & KMemoryPermission::UserMask),
ipc_lock_count,
device_use_count,
};
@@ -196,21 +193,21 @@ struct MemoryInfo {
}
};
class MemoryBlock final {
friend class MemoryBlockManager;
class KMemoryBlock final {
friend class KMemoryBlockManager;
private:
VAddr addr{};
std::size_t num_pages{};
MemoryState state{MemoryState::None};
KMemoryState state{KMemoryState::None};
u16 ipc_lock_count{};
u16 device_use_count{};
MemoryPermission perm{MemoryPermission::None};
MemoryPermission original_perm{MemoryPermission::None};
MemoryAttribute attribute{MemoryAttribute::None};
KMemoryPermission perm{KMemoryPermission::None};
KMemoryPermission original_perm{KMemoryPermission::None};
KMemoryAttribute attribute{KMemoryAttribute::None};
public:
static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) {
static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
@@ -221,9 +218,9 @@ public:
}
public:
constexpr MemoryBlock() = default;
constexpr MemoryBlock(VAddr addr_, std::size_t num_pages_, MemoryState state_,
MemoryPermission perm_, MemoryAttribute attribute_)
constexpr KMemoryBlock() = default;
constexpr KMemoryBlock(VAddr addr_, std::size_t num_pages_, KMemoryState state_,
KMemoryPermission perm_, KMemoryAttribute attribute_)
: addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {}
constexpr VAddr GetAddress() const {
@@ -246,40 +243,40 @@ public:
return GetEndAddress() - 1;
}
constexpr MemoryInfo GetMemoryInfo() const {
constexpr KMemoryInfo GetMemoryInfo() const {
return {
GetAddress(), GetSize(), state, perm,
attribute, original_perm, ipc_lock_count, device_use_count,
};
}
void ShareToDevice(MemoryPermission /*new_perm*/) {
ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared ||
void ShareToDevice(KMemoryPermission /*new_perm*/) {
ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared ||
device_use_count == 0);
attribute |= MemoryAttribute::DeviceShared;
attribute |= KMemoryAttribute::DeviceShared;
const u16 new_use_count{++device_use_count};
ASSERT(new_use_count > 0);
}
void UnshareToDevice(MemoryPermission /*new_perm*/) {
ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared);
void UnshareToDevice(KMemoryPermission /*new_perm*/) {
ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
const u16 prev_use_count{device_use_count--};
ASSERT(prev_use_count > 0);
if (prev_use_count == 1) {
attribute &= ~MemoryAttribute::DeviceShared;
attribute &= ~KMemoryAttribute::DeviceShared;
}
}
private:
constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
MemoryAttribute::IpcLocked |
MemoryAttribute::DeviceShared};
constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
constexpr KMemoryAttribute AttributeIgnoreMask{KMemoryAttribute::DontCareMask |
KMemoryAttribute::IpcLocked |
KMemoryAttribute::DeviceShared};
return state == s && perm == p &&
(attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
}
constexpr bool HasSameProperties(const MemoryBlock& rhs) const {
constexpr bool HasSameProperties(const KMemoryBlock& rhs) const {
return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm &&
attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count &&
device_use_count == rhs.device_use_count;
@@ -296,25 +293,25 @@ private:
num_pages += count;
}
constexpr void Update(MemoryState new_state, MemoryPermission new_perm,
MemoryAttribute new_attribute) {
ASSERT(original_perm == MemoryPermission::None);
ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None);
constexpr void Update(KMemoryState new_state, KMemoryPermission new_perm,
KMemoryAttribute new_attribute) {
ASSERT(original_perm == KMemoryPermission::None);
ASSERT((attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None);
state = new_state;
perm = new_perm;
attribute = static_cast<MemoryAttribute>(
attribute = static_cast<KMemoryAttribute>(
new_attribute |
(attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared)));
(attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)));
}
constexpr MemoryBlock Split(VAddr split_addr) {
constexpr KMemoryBlock Split(VAddr split_addr) {
ASSERT(GetAddress() < split_addr);
ASSERT(Contains(split_addr));
ASSERT(Common::IsAligned(split_addr, PageSize));
MemoryBlock block;
KMemoryBlock block;
block.addr = addr;
block.num_pages = (split_addr - GetAddress()) / PageSize;
block.state = state;
@@ -330,6 +327,6 @@ private:
return block;
}
};
static_assert(std::is_trivially_destructible<MemoryBlock>::value);
static_assert(std::is_trivially_destructible<KMemoryBlock>::value);
} // namespace Kernel::Memory
} // namespace Kernel
@@ -2,19 +2,19 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/memory/memory_block_manager.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel::Memory {
namespace Kernel {
MemoryBlockManager::MemoryBlockManager(VAddr start_addr, VAddr end_addr)
KMemoryBlockManager::KMemoryBlockManager(VAddr start_addr, VAddr end_addr)
: start_addr{start_addr}, end_addr{end_addr} {
const u64 num_pages{(end_addr - start_addr) / PageSize};
memory_block_tree.emplace_back(start_addr, num_pages, MemoryState::Free, MemoryPermission::None,
MemoryAttribute::None);
memory_block_tree.emplace_back(start_addr, num_pages, KMemoryState::Free,
KMemoryPermission::None, KMemoryAttribute::None);
}
MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) {
KMemoryBlockManager::iterator KMemoryBlockManager::FindIterator(VAddr addr) {
auto node{memory_block_tree.begin()};
while (node != end()) {
const VAddr end_addr{node->GetNumPages() * PageSize + node->GetAddress()};
@@ -26,9 +26,9 @@ MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) {
return end();
}
VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
std::size_t num_pages, std::size_t align, std::size_t offset,
std::size_t guard_pages) {
VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
std::size_t num_pages, std::size_t align,
std::size_t offset, std::size_t guard_pages) {
if (num_pages == 0) {
return {};
}
@@ -41,7 +41,7 @@ VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_nu
break;
}
if (info.state != MemoryState::Free) {
if (info.state != KMemoryState::Free) {
continue;
}
@@ -63,17 +63,17 @@ VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_nu
return {};
}
void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
MemoryPermission prev_perm, MemoryAttribute prev_attribute,
MemoryState state, MemoryPermission perm,
MemoryAttribute attribute) {
void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state,
KMemoryPermission prev_perm, KMemoryAttribute prev_attribute,
KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attribute) {
const VAddr end_addr{addr + num_pages * PageSize};
iterator node{memory_block_tree.begin()};
prev_attribute |= MemoryAttribute::IpcAndDeviceMapped;
prev_attribute |= KMemoryAttribute::IpcAndDeviceMapped;
while (node != memory_block_tree.end()) {
MemoryBlock* block{&(*node)};
KMemoryBlock* block{&(*node)};
iterator next_node{std::next(node)};
const VAddr cur_addr{block->GetAddress()};
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
@@ -106,13 +106,13 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState p
}
}
void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState state,
MemoryPermission perm, MemoryAttribute attribute) {
void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState state,
KMemoryPermission perm, KMemoryAttribute attribute) {
const VAddr end_addr{addr + num_pages * PageSize};
iterator node{memory_block_tree.begin()};
while (node != memory_block_tree.end()) {
MemoryBlock* block{&(*node)};
KMemoryBlock* block{&(*node)};
iterator next_node{std::next(node)};
const VAddr cur_addr{block->GetAddress()};
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
@@ -141,13 +141,13 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState s
}
}
void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
MemoryPermission perm) {
void KMemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
KMemoryPermission perm) {
const VAddr end_addr{addr + num_pages * PageSize};
iterator node{memory_block_tree.begin()};
while (node != memory_block_tree.end()) {
MemoryBlock* block{&(*node)};
KMemoryBlock* block{&(*node)};
iterator next_node{std::next(node)};
const VAddr cur_addr{block->GetAddress()};
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
@@ -176,9 +176,9 @@ void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&
}
}
void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
void KMemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
const_iterator it{FindIterator(start)};
MemoryInfo info{};
KMemoryInfo info{};
do {
info = it->GetMemoryInfo();
func(info);
@@ -186,8 +186,8 @@ void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& f
} while (info.addr + info.size - 1 < end - 1 && it != cend());
}
void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
MemoryBlock* block{&(*it)};
void KMemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
KMemoryBlock* block{&(*it)};
auto EraseIt = [&](const iterator it_to_erase) {
if (next_it == it_to_erase) {
@@ -197,7 +197,7 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
};
if (it != memory_block_tree.begin()) {
MemoryBlock* prev{&(*std::prev(it))};
KMemoryBlock* prev{&(*std::prev(it))};
if (block->HasSameProperties(*prev)) {
const iterator prev_it{std::prev(it)};
@@ -211,7 +211,7 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
}
if (it != cend()) {
const MemoryBlock* const next{&(*std::next(it))};
const KMemoryBlock* const next{&(*std::next(it))};
if (block->HasSameProperties(*next)) {
block->Add(next->GetNumPages());
@@ -220,4 +220,4 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
}
}
} // namespace Kernel::Memory
} // namespace Kernel
@@ -8,18 +8,18 @@
#include <list>
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/k_memory_block.h"
namespace Kernel::Memory {
namespace Kernel {
class MemoryBlockManager final {
class KMemoryBlockManager final {
public:
using MemoryBlockTree = std::list<MemoryBlock>;
using MemoryBlockTree = std::list<KMemoryBlock>;
using iterator = MemoryBlockTree::iterator;
using const_iterator = MemoryBlockTree::const_iterator;
public:
MemoryBlockManager(VAddr start_addr, VAddr end_addr);
KMemoryBlockManager(VAddr start_addr, VAddr end_addr);
iterator end() {
return memory_block_tree.end();
@@ -36,21 +36,22 @@ public:
VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
std::size_t align, std::size_t offset, std::size_t guard_pages);
void Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
MemoryPermission prev_perm, MemoryAttribute prev_attribute, MemoryState state,
MemoryPermission perm, MemoryAttribute attribute);
void Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state,
KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, KMemoryState state,
KMemoryPermission perm, KMemoryAttribute attribute);
void Update(VAddr addr, std::size_t num_pages, MemoryState state,
MemoryPermission perm = MemoryPermission::None,
MemoryAttribute attribute = MemoryAttribute::None);
void Update(VAddr addr, std::size_t num_pages, KMemoryState state,
KMemoryPermission perm = KMemoryPermission::None,
KMemoryAttribute attribute = KMemoryAttribute::None);
using LockFunc = std::function<void(iterator, MemoryPermission)>;
void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm);
using LockFunc = std::function<void(iterator, KMemoryPermission)>;
void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
KMemoryPermission perm);
using IterateFunc = std::function<void(const MemoryInfo&)>;
using IterateFunc = std::function<void(const KMemoryInfo&)>;
void IterateForRange(VAddr start, VAddr end, IterateFunc&& func);
MemoryBlock& FindBlock(VAddr addr) {
KMemoryBlock& FindBlock(VAddr addr) {
return *FindIterator(addr);
}
@@ -63,4 +64,4 @@ private:
MemoryBlockTree memory_block_tree;
};
} // namespace Kernel::Memory
} // namespace Kernel
@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/device_memory.h"
namespace Kernel::Memory {
namespace Kernel {
constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024;
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
@@ -27,8 +27,8 @@ constexpr bool IsKernelAddress(VAddr address) {
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
}
class MemoryRegion final {
friend class MemoryLayout;
class KMemoryRegion final {
friend class KMemoryLayout;
public:
constexpr PAddr StartAddress() const {
@@ -40,29 +40,29 @@ public:
}
private:
constexpr MemoryRegion() = default;
constexpr MemoryRegion(PAddr start_address, PAddr end_address)
constexpr KMemoryRegion() = default;
constexpr KMemoryRegion(PAddr start_address, PAddr end_address)
: start_address{start_address}, end_address{end_address} {}
const PAddr start_address{};
const PAddr end_address{};
};
class MemoryLayout final {
class KMemoryLayout final {
public:
constexpr const MemoryRegion& Application() const {
constexpr const KMemoryRegion& Application() const {
return application;
}
constexpr const MemoryRegion& Applet() const {
constexpr const KMemoryRegion& Applet() const {
return applet;
}
constexpr const MemoryRegion& System() const {
constexpr const KMemoryRegion& System() const {
return system;
}
static constexpr MemoryLayout GetDefaultLayout() {
static constexpr KMemoryLayout GetDefaultLayout() {
constexpr std::size_t application_size{0xcd500000};
constexpr std::size_t applet_size{0x1fb00000};
constexpr PAddr application_start_address{Core::DramMemoryMap::End - application_size};
@@ -76,15 +76,15 @@ public:
}
private:
constexpr MemoryLayout(PAddr application_start_address, std::size_t application_size,
PAddr applet_start_address, std::size_t applet_size,
PAddr system_start_address, std::size_t system_size)
constexpr KMemoryLayout(PAddr application_start_address, std::size_t application_size,
PAddr applet_start_address, std::size_t applet_size,
PAddr system_start_address, std::size_t system_size)
: application{application_start_address, application_size},
applet{applet_start_address, applet_size}, system{system_start_address, system_size} {}
const MemoryRegion application;
const MemoryRegion applet;
const MemoryRegion system;
const KMemoryRegion application;
const KMemoryRegion applet;
const KMemoryRegion system;
};
} // namespace Kernel::Memory
} // namespace Kernel
@@ -8,20 +8,20 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/memory/page_linked_list.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Memory {
namespace Kernel {
std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
const auto size{end_address - start_address};
// Calculate metadata sizes
const auto ref_count_size{(size / PageSize) * sizeof(u16)};
const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)};
const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)};
const auto page_heap_size{PageHeap::CalculateMetadataOverheadSize(size)};
const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)};
const auto total_metadata_size{manager_size + page_heap_size};
ASSERT(manager_size <= total_metadata_size);
ASSERT(Common::IsAligned(total_metadata_size, PageSize));
@@ -41,29 +41,30 @@ std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u6
return total_metadata_size;
}
void MemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
ASSERT(pool < Pool::Count);
managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address);
}
VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool,
Direction dir) {
VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages,
u32 option) {
// Early return if we're allocating no pages
if (num_pages == 0) {
return {};
}
// Lock the pool that we're allocating from
const auto [pool, dir] = DecodeOption(option);
const auto pool_index{static_cast<std::size_t>(pool)};
std::lock_guard lock{pool_locks[pool_index]};
// Choose a heap based on our page size request
const s32 heap_index{PageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
// Loop, trying to iterate from each block
// TODO (bunnei): Support multiple managers
Impl& chosen_manager{managers[pool_index]};
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index)};
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)};
// If we failed to allocate, quit now
if (!allocated_block) {
@@ -71,7 +72,7 @@ VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align
}
// If we allocated more than we need, free some
const auto allocated_pages{PageHeap::GetBlockNumPages(heap_index)};
const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)};
if (allocated_pages > num_pages) {
chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
}
@@ -79,8 +80,8 @@ VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align
return allocated_block;
}
ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
ASSERT(page_list.GetNumPages() == 0);
// Early return if we're allocating no pages
@@ -93,7 +94,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
std::lock_guard lock{pool_locks[pool_index]};
// Choose a heap based on our page size request
const s32 heap_index{PageHeap::GetBlockIndex(num_pages)};
const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)};
if (heap_index < 0) {
return ResultOutOfMemory;
}
@@ -112,11 +113,11 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
// Keep allocating until we've allocated all our pages
for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) {
const auto pages_per_alloc{PageHeap::GetBlockNumPages(index)};
const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)};
while (num_pages >= pages_per_alloc) {
// Allocate a block
VAddr allocated_block{chosen_manager.AllocateBlock(index)};
VAddr allocated_block{chosen_manager.AllocateBlock(index, false)};
if (!allocated_block) {
break;
}
@@ -148,8 +149,8 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
return RESULT_SUCCESS;
}
ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
// Early return if we're freeing no pages
if (!num_pages) {
return RESULT_SUCCESS;
@@ -172,4 +173,4 @@ ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages,
return RESULT_SUCCESS;
}
} // namespace Kernel::Memory
} // namespace Kernel
@@ -6,16 +6,18 @@
#include <array>
#include <mutex>
#include <tuple>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/page_heap.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
namespace Kernel::Memory {
namespace Kernel {
class PageLinkedList;
class KPageLinkedList;
class MemoryManager final : NonCopyable {
class KMemoryManager final : NonCopyable {
public:
enum class Pool : u32 {
Application = 0,
@@ -37,29 +39,50 @@ public:
Mask = (0xF << Shift),
};
MemoryManager() = default;
KMemoryManager() = default;
constexpr std::size_t GetSize(Pool pool) const {
return managers[static_cast<std::size_t>(pool)].GetSize();
}
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
VAddr AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool,
Direction dir = Direction::FromFront);
ResultCode Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir = Direction::FromFront);
ResultCode Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir = Direction::FromFront);
static constexpr std::size_t MaxManagerCount = 10;
public:
static constexpr u32 EncodeOption(Pool pool, Direction dir) {
return (static_cast<u32>(pool) << static_cast<u32>(Pool::Shift)) |
(static_cast<u32>(dir) << static_cast<u32>(Direction::Shift));
}
static constexpr Pool GetPool(u32 option) {
return static_cast<Pool>((static_cast<u32>(option) & static_cast<u32>(Pool::Mask)) >>
static_cast<u32>(Pool::Shift));
}
static constexpr Direction GetDirection(u32 option) {
return static_cast<Direction>(
(static_cast<u32>(option) & static_cast<u32>(Direction::Mask)) >>
static_cast<u32>(Direction::Shift));
}
static constexpr std::tuple<Pool, Direction> DecodeOption(u32 option) {
return std::make_tuple(GetPool(option), GetDirection(option));
}
private:
class Impl final : NonCopyable {
private:
using RefCount = u16;
private:
PageHeap heap;
KPageHeap heap;
Pool pool{};
public:
@@ -67,8 +90,8 @@ private:
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
VAddr AllocateBlock(s32 index) {
return heap.AllocateBlock(index);
VAddr AllocateBlock(s32 index, bool random) {
return heap.AllocateBlock(index, random);
}
void Free(VAddr addr, std::size_t num_pages) {
@@ -93,4 +116,4 @@ private:
std::array<Impl, MaxManagerCount> managers;
};
} // namespace Kernel::Memory
} // namespace Kernel
+279
View File
@@ -0,0 +1,279 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <bit>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/tiny_mt.h"
#include "core/hle/kernel/k_system_control.h"
namespace Kernel {
class KPageBitmap {
private:
class RandomBitGenerator {
private:
Common::TinyMT rng{};
u32 entropy{};
u32 bits_available{};
private:
void RefreshEntropy() {
entropy = rng.GenerateRandomU32();
bits_available = static_cast<u32>(Common::BitSize<decltype(entropy)>());
}
bool GenerateRandomBit() {
if (bits_available == 0) {
this->RefreshEntropy();
}
const bool rnd_bit = (entropy & 1) != 0;
entropy >>= 1;
--bits_available;
return rnd_bit;
}
public:
RandomBitGenerator() {
rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
}
std::size_t SelectRandomBit(u64 bitmap) {
u64 selected = 0;
u64 cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2;
u64 cur_mask = (1ULL << cur_num_bits) - 1;
while (cur_num_bits) {
const u64 low = (bitmap >> 0) & cur_mask;
const u64 high = (bitmap >> cur_num_bits) & cur_mask;
bool choose_low;
if (high == 0) {
// If only low val is set, choose low.
choose_low = true;
} else if (low == 0) {
// If only high val is set, choose high.
choose_low = false;
} else {
// If both are set, choose random.
choose_low = this->GenerateRandomBit();
}
// If we chose low, proceed with low.
if (choose_low) {
bitmap = low;
selected += 0;
} else {
bitmap = high;
selected += cur_num_bits;
}
// Proceed.
cur_num_bits /= 2;
cur_mask >>= cur_num_bits;
}
return selected;
}
};
public:
static constexpr std::size_t MaxDepth = 4;
private:
std::array<u64*, MaxDepth> bit_storages{};
RandomBitGenerator rng{};
std::size_t num_bits{};
std::size_t used_depths{};
public:
KPageBitmap() = default;
constexpr std::size_t GetNumBits() const {
return num_bits;
}
constexpr s32 GetHighestDepthIndex() const {
return static_cast<s32>(used_depths) - 1;
}
u64* Initialize(u64* storage, std::size_t size) {
// Initially, everything is un-set.
num_bits = 0;
// Calculate the needed bitmap depth.
used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
ASSERT(used_depths <= MaxDepth);
// Set the bitmap pointers.
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
bit_storages[depth] = storage;
size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>();
storage += size;
}
return storage;
}
s64 FindFreeBlock(bool random) {
uintptr_t offset = 0;
s32 depth = 0;
if (random) {
do {
const u64 v = bit_storages[depth][offset];
if (v == 0) {
// If depth is bigger than zero, then a previous level indicated a block was
// free.
ASSERT(depth == 0);
return -1;
}
offset = offset * Common::BitSize<u64>() + rng.SelectRandomBit(v);
++depth;
} while (depth < static_cast<s32>(used_depths));
} else {
do {
const u64 v = bit_storages[depth][offset];
if (v == 0) {
// If depth is bigger than zero, then a previous level indicated a block was
// free.
ASSERT(depth == 0);
return -1;
}
offset = offset * Common::BitSize<u64>() + std::countr_zero(v);
++depth;
} while (depth < static_cast<s32>(used_depths));
}
return static_cast<s64>(offset);
}
void SetBit(std::size_t offset) {
this->SetBit(this->GetHighestDepthIndex(), offset);
num_bits++;
}
void ClearBit(std::size_t offset) {
this->ClearBit(this->GetHighestDepthIndex(), offset);
num_bits--;
}
bool ClearRange(std::size_t offset, std::size_t count) {
s32 depth = this->GetHighestDepthIndex();
u64* bits = bit_storages[depth];
std::size_t bit_ind = offset / Common::BitSize<u64>();
if (count < Common::BitSize<u64>()) {
const std::size_t shift = offset % Common::BitSize<u64>();
ASSERT(shift + count <= Common::BitSize<u64>());
// Check that all the bits are set.
const u64 mask = ((u64(1) << count) - 1) << shift;
u64 v = bits[bit_ind];
if ((v & mask) != mask) {
return false;
}
// Clear the bits.
v &= ~mask;
bits[bit_ind] = v;
if (v == 0) {
this->ClearBit(depth - 1, bit_ind);
}
} else {
ASSERT(offset % Common::BitSize<u64>() == 0);
ASSERT(count % Common::BitSize<u64>() == 0);
// Check that all the bits are set.
std::size_t remaining = count;
std::size_t i = 0;
do {
if (bits[bit_ind + i++] != ~u64(0)) {
return false;
}
remaining -= Common::BitSize<u64>();
} while (remaining > 0);
// Clear the bits.
remaining = count;
i = 0;
do {
bits[bit_ind + i] = 0;
this->ClearBit(depth - 1, bit_ind + i);
i++;
remaining -= Common::BitSize<u64>();
} while (remaining > 0);
}
num_bits -= count;
return true;
}
private:
void SetBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
std::size_t ind = offset / Common::BitSize<u64>();
std::size_t which = offset % Common::BitSize<u64>();
const u64 mask = u64(1) << which;
u64* bit = std::addressof(bit_storages[depth][ind]);
u64 v = *bit;
ASSERT((v & mask) == 0);
*bit = v | mask;
if (v) {
break;
}
offset = ind;
depth--;
}
}
void ClearBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
std::size_t ind = offset / Common::BitSize<u64>();
std::size_t which = offset % Common::BitSize<u64>();
const u64 mask = u64(1) << which;
u64* bit = std::addressof(bit_storages[depth][ind]);
u64 v = *bit;
ASSERT((v & mask) != 0);
v &= ~mask;
*bit = v;
if (v) {
break;
}
offset = ind;
depth--;
}
}
private:
static constexpr s32 GetRequiredDepth(std::size_t region_size) {
s32 depth = 0;
while (true) {
region_size /= Common::BitSize<u64>();
depth++;
if (region_size == 0) {
return depth;
}
}
}
public:
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
std::size_t overhead_bits = 0;
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
region_size =
Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>();
overhead_bits += region_size;
}
return overhead_bits * sizeof(u64);
}
};
} // namespace Kernel
@@ -2,16 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#include "core/core.h"
#include "core/hle/kernel/memory/page_heap.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/memory.h"
namespace Kernel::Memory {
namespace Kernel {
void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
// Check our assumptions
ASSERT(Common::IsAligned((address), PageSize));
ASSERT(Common::IsAligned(size, PageSize));
@@ -32,11 +29,11 @@ void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_
}
}
VAddr PageHeap::AllocateBlock(s32 index) {
VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
const std::size_t needed_size{blocks[index].GetSize()};
for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) {
if (const VAddr addr{blocks[i].PopBlock()}; addr) {
if (const VAddr addr{blocks[i].PopBlock(random)}; addr) {
if (const std::size_t allocated_size{blocks[i].GetSize()};
allocated_size > needed_size) {
Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
@@ -48,13 +45,13 @@ VAddr PageHeap::AllocateBlock(s32 index) {
return 0;
}
void PageHeap::FreeBlock(VAddr block, s32 index) {
void KPageHeap::FreeBlock(VAddr block, s32 index) {
do {
block = blocks[index++].PushBlock(block);
} while (block != 0);
}
void PageHeap::Free(VAddr addr, std::size_t num_pages) {
void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
// Freeing no pages is a no-op
if (num_pages == 0) {
return;
@@ -104,16 +101,16 @@ void PageHeap::Free(VAddr addr, std::size_t num_pages) {
}
}
std::size_t PageHeap::CalculateMetadataOverheadSize(std::size_t region_size) {
std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) {
std::size_t overhead_size = 0;
for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
const std::size_t next_block_shift{
(i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
overhead_size += PageHeap::Block::CalculateMetadataOverheadSize(
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
region_size, cur_block_shift, next_block_shift);
}
return Common::AlignUp(overhead_size, PageSize);
}
} // namespace Kernel::Memory
} // namespace Kernel
+193
View File
@@ -0,0 +1,193 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <bit>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_bitmap.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
class KPageHeap final : NonCopyable {
public:
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
const auto target_pages{std::max(num_pages, align_pages)};
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <=
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
static constexpr s32 GetBlockIndex(std::size_t num_pages) {
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
static constexpr std::size_t GetBlockSize(std::size_t index) {
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
}
static constexpr std::size_t GetBlockNumPages(std::size_t index) {
return GetBlockSize(index) / PageSize;
}
private:
static constexpr std::size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
class Block final : NonCopyable {
private:
KPageBitmap bitmap;
VAddr heap_address{};
uintptr_t end_offset{};
std::size_t block_shift{};
std::size_t next_block_shift{};
public:
Block() = default;
constexpr std::size_t GetShift() const {
return block_shift;
}
constexpr std::size_t GetNextShift() const {
return next_block_shift;
}
constexpr std::size_t GetSize() const {
return static_cast<std::size_t>(1) << GetShift();
}
constexpr std::size_t GetNumPages() const {
return GetSize() / PageSize;
}
constexpr std::size_t GetNumFreeBlocks() const {
return bitmap.GetNumBits();
}
constexpr std::size_t GetNumFreePages() const {
return GetNumFreeBlocks() * GetNumPages();
}
u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
u64* bit_storage) {
// Set shifts
block_shift = bs;
next_block_shift = nbs;
// Align up the address
VAddr end{addr + size};
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
: (1ULL << block_shift)};
addr = Common::AlignDown((addr), align);
end = Common::AlignUp((end), align);
heap_address = addr;
end_offset = (end - addr) / (1ULL << block_shift);
return bitmap.Initialize(bit_storage, end_offset);
}
VAddr PushBlock(VAddr address) {
// Set the bit for the free block
std::size_t offset{(address - heap_address) >> GetShift()};
bitmap.SetBit(offset);
// If we have a next shift, try to clear the blocks below and return the address
if (GetNextShift()) {
const auto diff{1ULL << (GetNextShift() - GetShift())};
offset = Common::AlignDown(offset, diff);
if (bitmap.ClearRange(offset, diff)) {
return heap_address + (offset << GetShift());
}
}
// We couldn't coalesce, or we're already as big as possible
return 0;
}
VAddr PopBlock(bool random) {
// Find a free block
const s64 soffset{bitmap.FindFreeBlock(random)};
if (soffset < 0) {
return 0;
}
const auto offset{static_cast<std::size_t>(soffset)};
// Update our tracking and return it
bitmap.ClearBit(offset);
return heap_address + (offset << GetShift());
}
public:
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
std::size_t cur_block_shift,
std::size_t next_block_shift) {
const auto cur_block_size{(1ULL << cur_block_shift)};
const auto next_block_size{(1ULL << next_block_shift)};
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
return KPageBitmap::CalculateManagementOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
};
public:
KPageHeap() = default;
constexpr VAddr GetAddress() const {
return heap_address;
}
constexpr std::size_t GetSize() const {
return heap_size;
}
constexpr VAddr GetEndAddress() const {
return GetAddress() + GetSize();
}
constexpr std::size_t GetPageOffset(VAddr block) const {
return (block - GetAddress()) / PageSize;
}
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
VAddr AllocateBlock(s32 index, bool random);
void Free(VAddr addr, std::size_t num_pages);
void UpdateUsedSize() {
used_size = heap_size - (GetNumFreePages() * PageSize);
}
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
private:
constexpr std::size_t GetNumFreePages() const {
std::size_t num_free{};
for (const auto& block : blocks) {
num_free += block.GetNumFreePages();
}
return num_free;
}
void FreeBlock(VAddr block, s32 index);
VAddr heap_address{};
std::size_t heap_size{};
std::size_t used_size{};
std::array<Block, NumMemoryBlockPageShifts> blocks{};
std::vector<u64> metadata;
};
} // namespace Kernel
@@ -8,12 +8,12 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/result.h"
namespace Kernel::Memory {
namespace Kernel {
class PageLinkedList final {
class KPageLinkedList final {
public:
class Node final {
public:
@@ -33,8 +33,8 @@ public:
};
public:
PageLinkedList() = default;
PageLinkedList(u64 address, u64 num_pages) {
KPageLinkedList() = default;
KPageLinkedList(u64 address, u64 num_pages) {
ASSERT(AddBlock(address, num_pages).IsSuccess());
}
@@ -54,7 +54,7 @@ public:
return num_pages;
}
bool IsEqual(PageLinkedList& other) const {
bool IsEqual(KPageLinkedList& other) const {
auto this_node = nodes.begin();
auto other_node = other.nodes.begin();
while (this_node != nodes.end() && other_node != other.nodes.end()) {
@@ -89,4 +89,4 @@ private:
std::list<Node> nodes;
};
} // namespace Kernel::Memory
} // namespace Kernel
File diff suppressed because it is too large Load Diff
@@ -10,27 +10,27 @@
#include "common/common_types.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel::Memory {
namespace Kernel {
class MemoryBlockManager;
class KMemoryBlockManager;
class PageTable final : NonCopyable {
class KPageTable final : NonCopyable {
public:
explicit PageTable(Core::System& system);
explicit KPageTable(Core::System& system);
ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
VAddr code_addr, std::size_t code_size,
Memory::MemoryManager::Pool pool);
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, MemoryState state,
MemoryPermission perm);
KMemoryManager::Pool pool);
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
KMemoryPermission perm);
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -38,20 +38,20 @@ public:
ResultCode UnmapMemory(VAddr addr, std::size_t size);
ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state,
MemoryPermission perm);
ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm);
MemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm);
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
KMemoryPermission perm);
ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
KMemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask,
MemoryAttribute value);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
KMemoryAttribute value);
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
ResultVal<VAddr> SetHeapSize(std::size_t size);
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, MemoryState state,
MemoryPermission perm, PAddr map_addr = 0);
std::size_t region_num_pages, KMemoryState state,
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
@@ -72,47 +72,49 @@ private:
ChangePermissionsAndRefresh,
};
static constexpr MemoryAttribute DefaultMemoryIgnoreAttr =
MemoryAttribute::DontCareMask | MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared;
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = KMemoryAttribute::DontCareMask |
KMemoryAttribute::IpcLocked |
KMemoryAttribute::DeviceShared;
ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
ResultCode MapPages(VAddr addr, const PageLinkedList& page_linked_list, MemoryPermission perm);
void MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end);
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
KMemoryPermission perm);
void MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end);
bool IsRegionMapped(VAddr address, u64 size);
bool IsRegionContiguous(VAddr addr, u64 size) const;
void AddRegionToPages(VAddr start, std::size_t num_pages, PageLinkedList& page_linked_list);
MemoryInfo QueryInfoImpl(VAddr addr);
void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list);
KMemoryInfo QueryInfoImpl(VAddr addr);
VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages,
std::size_t align);
ResultCode Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group,
ResultCode Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
OperationType operation);
ResultCode Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm,
ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
OperationType operation, PAddr map_addr = 0);
constexpr VAddr GetRegionAddress(MemoryState state) const;
constexpr std::size_t GetRegionSize(MemoryState state) const;
constexpr bool CanContain(VAddr addr, std::size_t size, MemoryState state) const;
constexpr VAddr GetRegionAddress(KMemoryState state) const;
constexpr std::size_t GetRegionSize(KMemoryState state) const;
constexpr bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const;
constexpr ResultCode CheckMemoryState(const MemoryInfo& info, MemoryState state_mask,
MemoryState state, MemoryPermission perm_mask,
MemoryPermission perm, MemoryAttribute attr_mask,
MemoryAttribute attr) const;
ResultCode CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm,
MemoryAttribute* out_attr, VAddr addr, std::size_t size,
MemoryState state_mask, MemoryState state,
MemoryPermission perm_mask, MemoryPermission perm,
MemoryAttribute attr_mask, MemoryAttribute attr,
MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
ResultCode CheckMemoryState(VAddr addr, std::size_t size, MemoryState state_mask,
MemoryState state, MemoryPermission perm_mask,
MemoryPermission perm, MemoryAttribute attr_mask,
MemoryAttribute attr,
MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const;
ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr, ignore_attr);
}
std::recursive_mutex page_table_lock;
std::unique_ptr<MemoryBlockManager> block_manager;
std::unique_ptr<KMemoryBlockManager> block_manager;
public:
constexpr VAddr GetAddressSpaceStart() const {
@@ -212,7 +214,7 @@ public:
return !IsOutsideASLRRegion(address, size);
}
constexpr PAddr GetPhysicalAddr(VAddr addr) {
return page_table_impl.backing_addr[addr >> Memory::PageBits] + addr;
return page_table_impl.backing_addr[addr >> PageBits] + addr;
}
private:
@@ -267,11 +269,11 @@ private:
bool is_kernel{};
bool is_aslr_enabled{};
MemoryManager::Pool memory_pool{MemoryManager::Pool::Application};
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
Common::PageTable page_table_impl;
Core::System& system;
};
} // namespace Kernel::Memory
} // namespace Kernel
+6 -11
View File
@@ -734,7 +734,7 @@ void KScheduler::ScheduleImpl() {
}
guard.unlock();
Common::Fiber::YieldTo(*old_context, switch_fiber);
Common::Fiber::YieldTo(*old_context, *switch_fiber);
/// When a thread wakes up, the scheduler may have changed to other in another core.
auto& next_scheduler = *system.Kernel().CurrentScheduler();
next_scheduler.SwitchContextStep2();
@@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() {
break;
}
}
std::shared_ptr<Common::Fiber>* next_context;
if (next_thread != nullptr) {
next_context = &next_thread->GetHostContext();
} else {
next_context = &idle_thread->GetHostContext();
}
Common::Fiber::YieldTo(switch_fiber, *next_context);
auto thread = next_thread ? next_thread : idle_thread;
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
} while (!is_switch_pending());
}
}
@@ -800,9 +795,9 @@ void KScheduler::Initialize() {
std::string name = "Idle Thread Id:" + std::to_string(core_id);
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
nullptr, std::move(init_func), init_func_parameter);
auto thread_res = KThread::CreateThread(
system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
idle_thread = thread_res.Unwrap().get();
}
@@ -4,33 +4,32 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/shared_memory.h"
namespace Kernel {
SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
KSharedMemory::KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
: Object{kernel}, device_memory{device_memory} {}
SharedMemory::~SharedMemory() {
KSharedMemory::~KSharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
}
std::shared_ptr<SharedMemory> SharedMemory::Create(
std::shared_ptr<KSharedMemory> KSharedMemory::Create(
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
std::string name) {
KPageLinkedList&& page_list, KMemoryPermission owner_permission,
KMemoryPermission user_permission, PAddr physical_address, std::size_t size, std::string name) {
const auto resource_limit = kernel.GetSystemResourceLimit();
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
size);
ASSERT(memory_reservation.Succeeded());
std::shared_ptr<SharedMemory> shared_memory{
std::make_shared<SharedMemory>(kernel, device_memory)};
std::shared_ptr<KSharedMemory> shared_memory{
std::make_shared<KSharedMemory>(kernel, device_memory)};
shared_memory->owner_process = owner_process;
shared_memory->page_list = std::move(page_list);
@@ -44,22 +43,22 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
return shared_memory;
}
ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
Memory::MemoryPermission permissions) {
const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize};
ResultCode KSharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
KMemoryPermission permissions) {
const u64 page_count{(size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
UNIMPLEMENTED_MSG("Page count does not match");
}
const Memory::MemoryPermission expected =
const KMemoryPermission expected =
&target_process == owner_process ? owner_permission : user_permission;
if (permissions != expected) {
UNIMPLEMENTED_MSG("Permission does not match");
}
return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared,
return target_process.PageTable().MapPages(address, page_list, KMemoryState::Shared,
permissions);
}
@@ -9,8 +9,8 @@
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/page_linked_list.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
@@ -19,15 +19,15 @@ namespace Kernel {
class KernelCore;
class SharedMemory final : public Object {
class KSharedMemory final : public Object {
public:
explicit SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
~SharedMemory() override;
explicit KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
~KSharedMemory() override;
static std::shared_ptr<SharedMemory> Create(
static std::shared_ptr<KSharedMemory> Create(
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
KPageLinkedList&& page_list, KMemoryPermission owner_permission,
KMemoryPermission user_permission, PAddr physical_address, std::size_t size,
std::string name);
std::string GetTypeName() const override {
@@ -51,7 +51,7 @@ public:
* @param permissions Memory block map permissions (specified by SVC field)
*/
ResultCode Map(Process& target_process, VAddr address, std::size_t size,
Memory::MemoryPermission permissions);
KMemoryPermission permissions);
/**
* Gets a pointer to the shared memory block
@@ -76,9 +76,9 @@ public:
private:
Core::DeviceMemory& device_memory;
Process* owner_process{};
Memory::PageLinkedList page_list;
Memory::MemoryPermission owner_permission{};
Memory::MemoryPermission user_permission{};
KPageLinkedList page_list;
KMemoryPermission owner_permission{};
KMemoryPermission user_permission{};
PAddr physical_address{};
std::size_t size{};
std::string name;
@@ -2,9 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include <atomic>
@@ -12,17 +9,17 @@
#include "common/assert.h"
#include "common/common_types.h"
namespace Kernel::Memory {
namespace Kernel {
namespace impl {
class SlabHeapImpl final : NonCopyable {
class KSlabHeapImpl final : NonCopyable {
public:
struct Node {
Node* next{};
};
constexpr SlabHeapImpl() = default;
constexpr KSlabHeapImpl() = default;
void Initialize(std::size_t size) {
ASSERT(head == nullptr);
@@ -65,9 +62,9 @@ private:
} // namespace impl
class SlabHeapBase : NonCopyable {
class KSlabHeapBase : NonCopyable {
public:
constexpr SlabHeapBase() = default;
constexpr KSlabHeapBase() = default;
constexpr bool Contains(uintptr_t addr) const {
return start <= addr && addr < end;
@@ -126,7 +123,7 @@ public:
}
private:
using Impl = impl::SlabHeapImpl;
using Impl = impl::KSlabHeapImpl;
Impl impl;
uintptr_t peak{};
@@ -135,9 +132,9 @@ private:
};
template <typename T>
class SlabHeap final : public SlabHeapBase {
class KSlabHeap final : public KSlabHeapBase {
public:
constexpr SlabHeap() : SlabHeapBase() {}
constexpr KSlabHeap() : KSlabHeapBase() {}
void Initialize(void* memory, std::size_t memory_size) {
InitializeImpl(sizeof(T), memory, memory_size);
@@ -160,4 +157,4 @@ public:
}
};
} // namespace Kernel::Memory
} // namespace Kernel
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_spin_lock.h"
#if _MSC_VER
#include <intrin.h>
#if _M_AMD64
#define __x86_64__ 1
#endif
#if _M_ARM64
#define __aarch64__ 1
#endif
#else
#if __x86_64__
#include <xmmintrin.h>
#endif
#endif
namespace {
void ThreadPause() {
#if __x86_64__
_mm_pause();
#elif __aarch64__ && _MSC_VER
__yield();
#elif __aarch64__
asm("yield");
#endif
}
} // namespace
namespace Kernel {
void KSpinLock::Lock() {
while (lck.test_and_set(std::memory_order_acquire)) {
ThreadPause();
}
}
void KSpinLock::Unlock() {
lck.clear(std::memory_order_release);
}
bool KSpinLock::TryLock() {
if (lck.test_and_set(std::memory_order_acquire)) {
return false;
}
return true;
}
} // namespace Kernel
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include "core/hle/kernel/k_scoped_lock.h"
namespace Kernel {
class KSpinLock {
public:
KSpinLock() = default;
KSpinLock(const KSpinLock&) = delete;
KSpinLock& operator=(const KSpinLock&) = delete;
KSpinLock(KSpinLock&&) = delete;
KSpinLock& operator=(KSpinLock&&) = delete;
void Lock();
void Unlock();
[[nodiscard]] bool TryLock();
private:
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
using KScopedSpinLock = KScopedLock<KSpinLock>;
} // namespace Kernel
@@ -1,12 +1,13 @@
// Copyright 2020 yuzu Emulator Project
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <random>
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/k_system_control.h"
namespace Kernel {
namespace Kernel::Memory::SystemControl {
namespace {
template <typename F>
u64 GenerateUniformRange(u64 min, u64 max, F f) {
@@ -25,16 +26,17 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
}
}
u64 GenerateRandomU64ForInit() {
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
static std::random_device device;
static std::mt19937 gen(device());
static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return distribution(gen);
}
} // Anonymous namespace
u64 GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64);
}
} // namespace Kernel::Memory::SystemControl
} // namespace Kernel
+19
View File
@@ -0,0 +1,19 @@
// 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"
namespace Kernel {
class KSystemControl {
public:
KSystemControl() = default;
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
};
} // namespace Kernel
+35 -23
View File
@@ -20,13 +20,13 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
@@ -782,7 +782,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
}
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters++) >= 0);
}
@@ -795,7 +795,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
}
@@ -870,7 +870,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
KThread* thread = std::addressof(*it);
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
}
it = waiter_list.erase(it);
@@ -995,22 +995,11 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
return host_context;
}
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, Process* owner_process) {
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
owner_process, std::move(init_func), init_func_parameter);
}
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func,
void* thread_start_parameter) {
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system,
ThreadType type_flags, std::string name,
VAddr entry_point, u32 priority, u64 arg,
s32 processor_id, VAddr stack_top,
Process* owner_process) {
auto& kernel = system.Kernel();
std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel);
@@ -1027,12 +1016,35 @@ ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, Thread
auto& scheduler = kernel.GlobalSchedulerContext();
scheduler.AddThread(thread);
thread->host_context =
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
}
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) {
auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg,
processor_id, stack_top, owner_process);
if (thread_result.Succeeded()) {
(*thread_result)->host_context =
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
}
return thread_result;
}
ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) {
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id,
stack_top, owner_process, std::move(init_func), init_func_parameter);
}
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
return kernel.GetCurrentEmuThread();
}
+20 -4
View File
@@ -116,7 +116,7 @@ public:
using WaiterList = boost::intrusive::list<KThread>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* Creates and returns a new thread.
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
@@ -127,12 +127,12 @@ public:
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* Creates and returns a new thread, with a specified entry point.
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
@@ -145,11 +145,27 @@ public:
* @param thread_start_parameter The parameter which will passed to host context on init
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
/**
* Creates and returns a new thread for the emulated "user" process.
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
[[nodiscard]] std::string GetName() const override {
return name;
}
+56 -52
View File
@@ -27,17 +27,17 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/service_thread.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/lock.h"
@@ -101,8 +101,6 @@ struct KernelCore::Impl {
current_process = nullptr;
system_resource_limit = nullptr;
global_handle_table.Clear();
preemption_event = nullptr;
@@ -111,6 +109,13 @@ struct KernelCore::Impl {
exclusive_monitor.reset();
hid_shared_mem = nullptr;
font_shared_mem = nullptr;
irs_shared_mem = nullptr;
time_shared_mem = nullptr;
system_resource_limit = nullptr;
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
}
@@ -176,9 +181,9 @@ struct KernelCore::Impl {
std::string name = "Suspend Thread Id:" + std::to_string(i);
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
0, 0, static_cast<u32>(i), 0, nullptr,
std::move(init_func), init_func_parameter);
auto thread_res = KThread::CreateThread(
system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
nullptr, std::move(init_func), init_func_parameter);
suspend_threads[i] = std::move(thread_res).Unwrap();
}
@@ -216,10 +221,9 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
const thread_local auto thread =
KThread::Create(
KThread::CreateThread(
system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)
.Unwrap();
return thread.get();
}
@@ -266,7 +270,7 @@ struct KernelCore::Impl {
void InitializeMemoryLayout() {
// Initialize memory layout
constexpr Memory::MemoryLayout layout{Memory::MemoryLayout::GetDefaultLayout()};
constexpr KMemoryLayout layout{KMemoryLayout::GetDefaultLayout()};
constexpr std::size_t hid_size{0x40000};
constexpr std::size_t font_size{0x1100000};
constexpr std::size_t irs_size{0x8000};
@@ -277,36 +281,36 @@ struct KernelCore::Impl {
constexpr PAddr time_addr{layout.System().StartAddress() + hid_size + font_size + irs_size};
// Initialize memory manager
memory_manager = std::make_unique<Memory::MemoryManager>();
memory_manager->InitializeManager(Memory::MemoryManager::Pool::Application,
memory_manager = std::make_unique<KMemoryManager>();
memory_manager->InitializeManager(KMemoryManager::Pool::Application,
layout.Application().StartAddress(),
layout.Application().EndAddress());
memory_manager->InitializeManager(Memory::MemoryManager::Pool::Applet,
memory_manager->InitializeManager(KMemoryManager::Pool::Applet,
layout.Applet().StartAddress(),
layout.Applet().EndAddress());
memory_manager->InitializeManager(Memory::MemoryManager::Pool::System,
memory_manager->InitializeManager(KMemoryManager::Pool::System,
layout.System().StartAddress(),
layout.System().EndAddress());
hid_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{hid_addr, hid_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, hid_addr, hid_size, "HID:SharedMemory");
font_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{font_pa, font_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, font_pa, font_size, "Font:SharedMemory");
irs_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{irs_addr, irs_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, irs_addr, irs_size, "IRS:SharedMemory");
time_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{time_addr, time_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, time_addr, time_size, "Time:SharedMemory");
hid_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {hid_addr, hid_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, hid_addr, hid_size,
"HID:SharedMemory");
font_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {font_pa, font_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, font_pa, font_size,
"Font:SharedMemory");
irs_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {irs_addr, irs_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, irs_addr, irs_size,
"IRS:SharedMemory");
time_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {time_addr, time_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, time_addr, time_size,
"Time:SharedMemory");
// Allocate slab heaps
user_slab_heap_pages = std::make_unique<Memory::SlabHeap<Memory::Page>>();
user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>();
constexpr u64 user_slab_heap_size{0x1ef000};
// Reserve slab heaps
@@ -348,14 +352,14 @@ struct KernelCore::Impl {
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
// Kernel memory management
std::unique_ptr<Memory::MemoryManager> memory_manager;
std::unique_ptr<Memory::SlabHeap<Memory::Page>> user_slab_heap_pages;
std::unique_ptr<KMemoryManager> memory_manager;
std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
// Shared memory for services
std::shared_ptr<Kernel::SharedMemory> hid_shared_mem;
std::shared_ptr<Kernel::SharedMemory> font_shared_mem;
std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> hid_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> font_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> irs_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> time_shared_mem;
// Threads used for services
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
@@ -573,51 +577,51 @@ KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThread();
}
Memory::MemoryManager& KernelCore::MemoryManager() {
KMemoryManager& KernelCore::MemoryManager() {
return *impl->memory_manager;
}
const Memory::MemoryManager& KernelCore::MemoryManager() const {
const KMemoryManager& KernelCore::MemoryManager() const {
return *impl->memory_manager;
}
Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() {
KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
return *impl->user_slab_heap_pages;
}
const Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() const {
const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
return *impl->user_slab_heap_pages;
}
Kernel::SharedMemory& KernelCore::GetHidSharedMem() {
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
return *impl->hid_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetHidSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetHidSharedMem() const {
return *impl->hid_shared_mem;
}
Kernel::SharedMemory& KernelCore::GetFontSharedMem() {
Kernel::KSharedMemory& KernelCore::GetFontSharedMem() {
return *impl->font_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetFontSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetFontSharedMem() const {
return *impl->font_shared_mem;
}
Kernel::SharedMemory& KernelCore::GetIrsSharedMem() {
Kernel::KSharedMemory& KernelCore::GetIrsSharedMem() {
return *impl->irs_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetIrsSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetIrsSharedMem() const {
return *impl->irs_shared_mem;
}
Kernel::SharedMemory& KernelCore::GetTimeSharedMem() {
Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() {
return *impl->time_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
return *impl->time_shared_mem;
}
+21 -23
View File
@@ -11,7 +11,7 @@
#include <vector>
#include "core/arm/cpu_interrupt_handler.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/object.h"
namespace Core {
@@ -27,25 +27,23 @@ struct EventType;
namespace Kernel {
namespace Memory {
class MemoryManager;
template <typename T>
class SlabHeap;
} // namespace Memory
class ClientPort;
class GlobalSchedulerContext;
class HandleTable;
class PhysicalCore;
class Process;
class KMemoryManager;
class KResourceLimit;
class KScheduler;
class SharedMemory;
class KSharedMemory;
class KThread;
class PhysicalCore;
class Process;
class ServiceThread;
class Synchronization;
class KThread;
class TimeManager;
template <typename T>
class KSlabHeap;
using EmuThreadHandle = uintptr_t;
constexpr EmuThreadHandle EmuThreadHandleInvalid{};
constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
@@ -178,40 +176,40 @@ public:
void RegisterHostThread();
/// Gets the virtual memory manager for the kernel.
Memory::MemoryManager& MemoryManager();
KMemoryManager& MemoryManager();
/// Gets the virtual memory manager for the kernel.
const Memory::MemoryManager& MemoryManager() const;
const KMemoryManager& MemoryManager() const;
/// Gets the slab heap allocated for user space pages.
Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages();
KSlabHeap<Page>& GetUserSlabHeapPages();
/// Gets the slab heap allocated for user space pages.
const Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages() const;
const KSlabHeap<Page>& GetUserSlabHeapPages() const;
/// Gets the shared memory object for HID services.
Kernel::SharedMemory& GetHidSharedMem();
Kernel::KSharedMemory& GetHidSharedMem();
/// Gets the shared memory object for HID services.
const Kernel::SharedMemory& GetHidSharedMem() const;
const Kernel::KSharedMemory& GetHidSharedMem() const;
/// Gets the shared memory object for font services.
Kernel::SharedMemory& GetFontSharedMem();
Kernel::KSharedMemory& GetFontSharedMem();
/// Gets the shared memory object for font services.
const Kernel::SharedMemory& GetFontSharedMem() const;
const Kernel::KSharedMemory& GetFontSharedMem() const;
/// Gets the shared memory object for IRS services.
Kernel::SharedMemory& GetIrsSharedMem();
Kernel::KSharedMemory& GetIrsSharedMem();
/// Gets the shared memory object for IRS services.
const Kernel::SharedMemory& GetIrsSharedMem() const;
const Kernel::KSharedMemory& GetIrsSharedMem() const;
/// Gets the shared memory object for Time services.
Kernel::SharedMemory& GetTimeSharedMem();
Kernel::KSharedMemory& GetTimeSharedMem();
/// Gets the shared memory object for Time services.
const Kernel::SharedMemory& GetTimeSharedMem() const;
const Kernel::KSharedMemory& GetTimeSharedMem() const;
/// Suspend/unsuspend the OS.
void Suspend(bool in_suspention);
-370
View File
@@ -1,370 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include <array>
#include <bit>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_types.h"
namespace Kernel::Memory {
class PageHeap final : NonCopyable {
public:
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
const auto target_pages{std::max(num_pages, align_pages)};
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <=
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
static constexpr s32 GetBlockIndex(std::size_t num_pages) {
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
static constexpr std::size_t GetBlockSize(std::size_t index) {
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
}
static constexpr std::size_t GetBlockNumPages(std::size_t index) {
return GetBlockSize(index) / PageSize;
}
private:
static constexpr std::size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
class Block final : NonCopyable {
private:
class Bitmap final : NonCopyable {
public:
static constexpr std::size_t MaxDepth{4};
private:
std::array<u64*, MaxDepth> bit_storages{};
std::size_t num_bits{};
std::size_t used_depths{};
public:
constexpr Bitmap() = default;
constexpr std::size_t GetNumBits() const {
return num_bits;
}
constexpr s32 GetHighestDepthIndex() const {
return static_cast<s32>(used_depths) - 1;
}
constexpr u64* Initialize(u64* storage, std::size_t size) {
//* Initially, everything is un-set
num_bits = 0;
// Calculate the needed bitmap depth
used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
ASSERT(used_depths <= MaxDepth);
// Set the bitmap pointers
for (s32 depth{GetHighestDepthIndex()}; depth >= 0; depth--) {
bit_storages[depth] = storage;
size = Common::AlignUp(size, 64) / 64;
storage += size;
}
return storage;
}
s64 FindFreeBlock() const {
uintptr_t offset{};
s32 depth{};
do {
const u64 v{bit_storages[depth][offset]};
if (v == 0) {
// Non-zero depth indicates that a previous level had a free block
ASSERT(depth == 0);
return -1;
}
offset = offset * 64 + static_cast<u32>(std::countr_zero(v));
++depth;
} while (depth < static_cast<s32>(used_depths));
return static_cast<s64>(offset);
}
constexpr void SetBit(std::size_t offset) {
SetBit(GetHighestDepthIndex(), offset);
num_bits++;
}
constexpr void ClearBit(std::size_t offset) {
ClearBit(GetHighestDepthIndex(), offset);
num_bits--;
}
constexpr bool ClearRange(std::size_t offset, std::size_t count) {
const s32 depth{GetHighestDepthIndex()};
const auto bit_ind{offset / 64};
u64* bits{bit_storages[depth]};
if (count < 64) {
const auto shift{offset % 64};
ASSERT(shift + count <= 64);
// Check that all the bits are set
const u64 mask{((1ULL << count) - 1) << shift};
u64 v{bits[bit_ind]};
if ((v & mask) != mask) {
return false;
}
// Clear the bits
v &= ~mask;
bits[bit_ind] = v;
if (v == 0) {
ClearBit(depth - 1, bit_ind);
}
} else {
ASSERT(offset % 64 == 0);
ASSERT(count % 64 == 0);
// Check that all the bits are set
std::size_t remaining{count};
std::size_t i = 0;
do {
if (bits[bit_ind + i++] != ~u64(0)) {
return false;
}
remaining -= 64;
} while (remaining > 0);
// Clear the bits
remaining = count;
i = 0;
do {
bits[bit_ind + i] = 0;
ClearBit(depth - 1, bit_ind + i);
i++;
remaining -= 64;
} while (remaining > 0);
}
num_bits -= count;
return true;
}
private:
constexpr void SetBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
const auto ind{offset / 64};
const auto which{offset % 64};
const u64 mask{1ULL << which};
u64* bit{std::addressof(bit_storages[depth][ind])};
const u64 v{*bit};
ASSERT((v & mask) == 0);
*bit = v | mask;
if (v) {
break;
}
offset = ind;
depth--;
}
}
constexpr void ClearBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
const auto ind{offset / 64};
const auto which{offset % 64};
const u64 mask{1ULL << which};
u64* bit{std::addressof(bit_storages[depth][ind])};
u64 v{*bit};
ASSERT((v & mask) != 0);
v &= ~mask;
*bit = v;
if (v) {
break;
}
offset = ind;
depth--;
}
}
private:
static constexpr s32 GetRequiredDepth(std::size_t region_size) {
s32 depth = 0;
while (true) {
region_size /= 64;
depth++;
if (region_size == 0) {
return depth;
}
}
}
public:
static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size) {
std::size_t overhead_bits = 0;
for (s32 depth{GetRequiredDepth(region_size) - 1}; depth >= 0; depth--) {
region_size = Common::AlignUp(region_size, 64) / 64;
overhead_bits += region_size;
}
return overhead_bits * sizeof(u64);
}
};
private:
Bitmap bitmap;
VAddr heap_address{};
uintptr_t end_offset{};
std::size_t block_shift{};
std::size_t next_block_shift{};
public:
constexpr Block() = default;
constexpr std::size_t GetShift() const {
return block_shift;
}
constexpr std::size_t GetNextShift() const {
return next_block_shift;
}
constexpr std::size_t GetSize() const {
return static_cast<std::size_t>(1) << GetShift();
}
constexpr std::size_t GetNumPages() const {
return GetSize() / PageSize;
}
constexpr std::size_t GetNumFreeBlocks() const {
return bitmap.GetNumBits();
}
constexpr std::size_t GetNumFreePages() const {
return GetNumFreeBlocks() * GetNumPages();
}
constexpr u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
u64* bit_storage) {
// Set shifts
block_shift = bs;
next_block_shift = nbs;
// Align up the address
VAddr end{addr + size};
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
: (1ULL << block_shift)};
addr = Common::AlignDown((addr), align);
end = Common::AlignUp((end), align);
heap_address = addr;
end_offset = (end - addr) / (1ULL << block_shift);
return bitmap.Initialize(bit_storage, end_offset);
}
constexpr VAddr PushBlock(VAddr address) {
// Set the bit for the free block
std::size_t offset{(address - heap_address) >> GetShift()};
bitmap.SetBit(offset);
// If we have a next shift, try to clear the blocks below and return the address
if (GetNextShift()) {
const auto diff{1ULL << (GetNextShift() - GetShift())};
offset = Common::AlignDown(offset, diff);
if (bitmap.ClearRange(offset, diff)) {
return heap_address + (offset << GetShift());
}
}
// We couldn't coalesce, or we're already as big as possible
return 0;
}
VAddr PopBlock() {
// Find a free block
const s64 soffset{bitmap.FindFreeBlock()};
if (soffset < 0) {
return 0;
}
const auto offset{static_cast<std::size_t>(soffset)};
// Update our tracking and return it
bitmap.ClearBit(offset);
return heap_address + (offset << GetShift());
}
public:
static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size,
std::size_t cur_block_shift,
std::size_t next_block_shift) {
const auto cur_block_size{(1ULL << cur_block_shift)};
const auto next_block_size{(1ULL << next_block_shift)};
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
return Bitmap::CalculateMetadataOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
};
public:
PageHeap() = default;
constexpr VAddr GetAddress() const {
return heap_address;
}
constexpr std::size_t GetSize() const {
return heap_size;
}
constexpr VAddr GetEndAddress() const {
return GetAddress() + GetSize();
}
constexpr std::size_t GetPageOffset(VAddr block) const {
return (block - GetAddress()) / PageSize;
}
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
VAddr AllocateBlock(s32 index);
void Free(VAddr addr, std::size_t num_pages);
void UpdateUsedSize() {
used_size = heap_size - (GetNumFreePages() * PageSize);
}
static std::size_t CalculateMetadataOverheadSize(std::size_t region_size);
private:
constexpr std::size_t GetNumFreePages() const {
std::size_t num_free{};
for (const auto& block : blocks) {
num_free += block.GetNumFreePages();
}
return num_free;
}
void FreeBlock(VAddr block, s32 index);
VAddr heap_address{};
std::size_t heap_size{};
std::size_t used_size{};
std::array<Block, NumMemoryBlockPageShifts> blocks{};
std::vector<u64> metadata;
};
} // namespace Kernel::Memory
@@ -1,13 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Kernel::Memory::SystemControl {
u64 GenerateRandomRange(u64 min, u64 max);
} // namespace Kernel::Memory::SystemControl
@@ -8,11 +8,11 @@
#include "common/common_types.h"
namespace Kernel::Memory {
namespace Kernel {
constexpr std::size_t PageBits{12};
constexpr std::size_t PageSize{1 << PageBits};
using Page = std::array<u8, PageSize>;
} // namespace Kernel::Memory
} // namespace Kernel
+34 -29
View File
@@ -14,14 +14,14 @@
#include "core/device_memory.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block_manager.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/lock.h"
@@ -40,8 +40,9 @@ namespace {
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
owner_process.GetIdealCoreId(), stack_top, &owner_process);
auto thread_res =
KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0,
owner_process.GetIdealCoreId(), stack_top, &owner_process);
std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
@@ -274,7 +275,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
// Set initial resource limits
resource_limit->SetLimitValue(
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
code_size + system_resource_size);
if (!memory_reservation.Succeeded()) {
@@ -285,15 +286,15 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
// Initialize proces address space
if (const ResultCode result{
page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000,
code_size, Memory::MemoryManager::Pool::Application)};
code_size, KMemoryManager::Pool::Application)};
result.IsError()) {
return result;
}
// Map process code region
if (const ResultCode result{page_table->MapProcessCode(
page_table->GetCodeRegionStart(), code_size / Memory::PageSize,
Memory::MemoryState::Code, Memory::MemoryPermission::None)};
if (const ResultCode result{page_table->MapProcessCode(page_table->GetCodeRegionStart(),
code_size / PageSize, KMemoryState::Code,
KMemoryPermission::None)};
result.IsError()) {
return result;
}
@@ -323,6 +324,11 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
UNREACHABLE();
}
// Set initial resource limits
resource_limit->SetLimitValue(
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
resource_limit->SetLimitValue(LimitableResource::Threads, 608);
resource_limit->SetLimitValue(LimitableResource::Events, 700);
resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128);
@@ -400,22 +406,22 @@ VAddr Process::CreateTLSRegion() {
return *tls_page_iter->ReserveSlot();
}
Memory::Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
ASSERT(tls_page_ptr);
const VAddr start{page_table->GetKernelMapRegionStart()};
const VAddr size{page_table->GetKernelMapRegionEnd() - start};
const PAddr tls_map_addr{system.DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
const VAddr tls_page_addr{
page_table
->AllocateAndMapMemory(1, Memory::PageSize, true, start, size / Memory::PageSize,
Memory::MemoryState::ThreadLocal,
Memory::MemoryPermission::ReadAndWrite, tls_map_addr)
.ValueOr(0)};
const VAddr tls_page_addr{page_table
->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
KMemoryState::ThreadLocal,
KMemoryPermission::ReadAndWrite,
tls_map_addr)
.ValueOr(0)};
ASSERT(tls_page_addr);
std::memset(tls_page_ptr, 0, Memory::PageSize);
std::memset(tls_page_ptr, 0, PageSize);
tls_pages.emplace_back(tls_page_addr);
const auto reserve_result{tls_pages.back().ReserveSlot()};
@@ -442,15 +448,15 @@ void Process::FreeTLSRegion(VAddr tls_address) {
void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
std::lock_guard lock{HLE::g_hle_lock};
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
Memory::MemoryPermission permission) {
KMemoryPermission permission) {
page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);
};
system.Memory().WriteBlock(*this, base_addr, code_set.memory.data(), code_set.memory.size());
ReprotectSegment(code_set.CodeSegment(), Memory::MemoryPermission::ReadAndExecute);
ReprotectSegment(code_set.RODataSegment(), Memory::MemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite);
ReprotectSegment(code_set.CodeSegment(), KMemoryPermission::ReadAndExecute);
ReprotectSegment(code_set.RODataSegment(), KMemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), KMemoryPermission::ReadAndWrite);
}
bool Process::IsSignaled() const {
@@ -459,9 +465,9 @@ bool Process::IsSignaled() const {
}
Process::Process(Core::System& system)
: KSynchronizationObject{system.Kernel()},
page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {}
: KSynchronizationObject{system.Kernel()}, page_table{std::make_unique<KPageTable>(system)},
handle_table{system.Kernel()}, address_arbiter{system}, condition_var{system},
state_lock{system.Kernel()}, system{system} {}
Process::~Process() = default;
@@ -479,16 +485,15 @@ ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) {
ASSERT(stack_size);
// The kernel always ensures that the given stack size is page aligned.
main_thread_stack_size = Common::AlignUp(stack_size, Memory::PageSize);
main_thread_stack_size = Common::AlignUp(stack_size, PageSize);
const VAddr start{page_table->GetStackRegionStart()};
const std::size_t size{page_table->GetStackRegionEnd() - start};
CASCADE_RESULT(main_thread_stack_top,
page_table->AllocateAndMapMemory(
main_thread_stack_size / Memory::PageSize, Memory::PageSize, false, start,
size / Memory::PageSize, Memory::MemoryState::Stack,
Memory::MemoryPermission::ReadAndWrite));
main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize,
KMemoryState::Stack, KMemoryPermission::ReadAndWrite));
main_thread_stack_top += main_thread_stack_size;
+4 -7
View File
@@ -29,16 +29,13 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
class KPageTable;
class KResourceLimit;
class KThread;
class TLSPage;
struct CodeSet;
namespace Memory {
class PageTable;
}
enum class MemoryRegion : u16 {
APPLICATION = 1,
SYSTEM = 2,
@@ -104,12 +101,12 @@ public:
}
/// Gets a reference to the process' page table.
Memory::PageTable& PageTable() {
KPageTable& PageTable() {
return *page_table;
}
/// Gets const a reference to the process' page table.
const Memory::PageTable& PageTable() const {
const KPageTable& PageTable() const {
return *page_table;
}
@@ -385,7 +382,7 @@ private:
ResultCode AllocateMainThreadStack(std::size_t stack_size);
/// Memory manager for this process
std::unique_ptr<Memory::PageTable> page_table;
std::unique_ptr<KPageTable> page_table;
/// Current status of the process
ProcessStatus status{};
+7 -7
View File
@@ -7,7 +7,7 @@
#include "common/bit_util.h"
#include "common/logging/log.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/svc_results.h"
@@ -69,7 +69,7 @@ u32 GetFlagBitOffset(CapabilityType type) {
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
std::size_t num_capabilities,
Memory::PageTable& page_table) {
KPageTable& page_table) {
Clear();
// Allow all cores and priorities.
@@ -82,7 +82,7 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
std::size_t num_capabilities,
Memory::PageTable& page_table) {
KPageTable& page_table) {
Clear();
return ParseCapabilities(capabilities, num_capabilities, page_table);
@@ -108,7 +108,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
std::size_t num_capabilities,
Memory::PageTable& page_table) {
KPageTable& page_table) {
u32 set_flags = 0;
u32 set_svc_bits = 0;
@@ -155,7 +155,7 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
}
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
u32 flag, Memory::PageTable& page_table) {
u32 flag, KPageTable& page_table) {
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
@@ -293,12 +293,12 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
}
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
Memory::PageTable& page_table) {
KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, Memory::PageTable& page_table) {
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}
+7 -9
View File
@@ -12,9 +12,7 @@ union ResultCode;
namespace Kernel {
namespace Memory {
class PageTable;
}
class KPageTable;
/// The possible types of programs that may be indicated
/// by the program type capability descriptor.
@@ -90,7 +88,7 @@ public:
/// otherwise, an error code upon failure.
///
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Initializes this process capabilities instance for a userland process.
///
@@ -103,7 +101,7 @@ public:
/// otherwise, an error code upon failure.
///
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Initializes this process capabilities instance for a process that does not
/// have any metadata to parse.
@@ -189,7 +187,7 @@ private:
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
///
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Attempts to parse a capability descriptor that is only represented by a
/// single flag set.
@@ -204,7 +202,7 @@ private:
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
///
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Clears the internal state of this process capability instance. Necessary,
/// to have a sane starting point due to us allowing running executables without
@@ -228,10 +226,10 @@ private:
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
/// Handles flags related to mapping physical memory pages.
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, Memory::PageTable& page_table);
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
/// Handles flags related to mapping IO pages.
ResultCode HandleMapIOFlags(u32 flags, Memory::PageTable& page_table);
ResultCode HandleMapIOFlags(u32 flags, KPageTable& page_table);
/// Handles flags related to the interrupt capability flags.
ResultCode HandleInterruptFlags(u32 flags);
+28 -28
View File
@@ -27,21 +27,21 @@
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
@@ -67,8 +67,8 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr dst_addr,
VAddr src_addr, u64 size) {
ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
if (!Common::Is4KBAligned(dst_addr)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
return ResultInvalidAddress;
@@ -230,9 +230,9 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
return ResultInvalidCurrentMemory;
}
const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)};
if (attributes != static_cast<Memory::MemoryAttribute>(mask) ||
(attributes | Memory::MemoryAttribute::Uncached) != Memory::MemoryAttribute::Uncached) {
const auto attributes{static_cast<MemoryAttribute>(mask | attribute)};
if (attributes != static_cast<MemoryAttribute>(mask) ||
(attributes | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
LOG_ERROR(Kernel_SVC,
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
attribute, mask);
@@ -241,8 +241,8 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
return page_table.SetMemoryAttribute(address, size, static_cast<Memory::MemoryAttribute>(mask),
static_cast<Memory::MemoryAttribute>(attribute));
return page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
static_cast<KMemoryAttribute>(attribute));
}
static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
@@ -508,7 +508,7 @@ static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAdd
thread_handle, address, tag);
// Validate the input address.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
address);
return ResultInvalidCurrentMemory;
@@ -531,8 +531,7 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
// Validate the input address.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC,
"Attempting to arbitrate an unlock on a kernel address (address={:08X})",
address);
@@ -1232,9 +1231,8 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
return ResultInvalidCurrentMemory;
}
const auto permission_type = static_cast<Memory::MemoryPermission>(permissions);
if ((permission_type | Memory::MemoryPermission::Write) !=
Memory::MemoryPermission::ReadAndWrite) {
const auto permission_type = static_cast<MemoryPermission>(permissions);
if ((permission_type | MemoryPermission::Write) != MemoryPermission::ReadWrite) {
LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
permissions);
return ResultInvalidMemoryPermissions;
@@ -1267,14 +1265,15 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
return ResultInvalidMemoryRange;
}
auto shared_memory{current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle)};
auto shared_memory{current_process->GetHandleTable().Get<KSharedMemory>(shared_memory_handle)};
if (!shared_memory) {
LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
shared_memory_handle);
return ResultInvalidHandle;
}
return shared_memory->Map(*current_process, addr, size, permission_type);
return shared_memory->Map(*current_process, addr, size,
static_cast<KMemoryPermission>(permission_type));
}
static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr,
@@ -1533,8 +1532,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
std::shared_ptr<KThread> thread;
{
KScopedLightLock lk{process.GetStateLock()};
CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority,
arg, core_id, stack_bottom, &process));
CASCADE_RESULT(thread,
KThread::CreateUserThread(system, ThreadType::User, "", entry_point,
priority, arg, core_id, stack_bottom, &process));
}
const auto new_thread_handle = process.GetHandleTable().Create(thread);
@@ -1638,7 +1638,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address,
cv_key, tag, timeout_ns);
// Validate input.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
return ResultInvalidCurrentMemory;
}
@@ -1720,7 +1720,7 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::Arbit
address, arb_type, value, timeout_ns);
// Validate input.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
return ResultInvalidCurrentMemory;
}
@@ -1765,7 +1765,7 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
address, signal_type, value, count);
// Validate input.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
return ResultInvalidCurrentMemory;
}
@@ -1887,9 +1887,8 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
return ResultInvalidCurrentMemory;
}
const auto perms{static_cast<Memory::MemoryPermission>(permissions)};
if (perms > Memory::MemoryPermission::ReadAndWrite ||
perms == Memory::MemoryPermission::Write) {
const auto perms{static_cast<MemoryPermission>(permissions)};
if (perms > MemoryPermission::ReadWrite || perms == MemoryPermission::Write) {
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
permissions);
return ResultInvalidMemoryPermissions;
@@ -1903,7 +1902,8 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
LOG_ERROR(Kernel_SVC, "Could not reserve a new transfer memory");
return ResultResourceLimitedExceeded;
}
auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size,
static_cast<KMemoryPermission>(perms));
if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
return reserve_result;
+2 -2
View File
@@ -2,9 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/result.h"
@@ -24,7 +24,7 @@ TransferMemory::~TransferMemory() {
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel,
Core::Memory::Memory& memory,
VAddr base_address, std::size_t size,
Memory::MemoryPermission permissions) {
KMemoryPermission permissions) {
std::shared_ptr<TransferMemory> transfer_memory{
std::make_shared<TransferMemory>(kernel, memory)};
+3 -3
View File
@@ -6,7 +6,7 @@
#include <memory>
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/physical_memory.h"
@@ -36,7 +36,7 @@ public:
static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Core::Memory::Memory& memory,
VAddr base_address, std::size_t size,
Memory::MemoryPermission permissions);
KMemoryPermission permissions);
TransferMemory(const TransferMemory&) = delete;
TransferMemory& operator=(const TransferMemory&) = delete;
@@ -82,7 +82,7 @@ private:
std::size_t size{};
/// The memory permissions that are applied to this instance.
Memory::MemoryPermission owner_permissions{};
KMemoryPermission owner_permissions{};
/// The process that this transfer memory instance was created under.
Process* owner_process{};
+17 -1
View File
@@ -508,7 +508,7 @@ public:
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
{2, nullptr, "EnsureIdTokenCacheAsync"},
{3, nullptr, "LoadIdTokenCache"},
{130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
{130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
{150, nullptr, "CreateAuthorizationRequest"},
{160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
{170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
@@ -534,6 +534,22 @@ private:
rb.PushRaw<u64>(user_id.GetNintendoID());
}
void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
std::vector<u8> nas_user_base_for_application(0x68);
ctx.WriteBuffer(nas_user_base_for_application, 0);
if (ctx.CanWriteBuffer(1)) {
std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1));
ctx.WriteBuffer(unknown_out_buffer, 1);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(user_id.GetNintendoID());
}
void StoreOpenContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
+7 -6
View File
@@ -1047,20 +1047,21 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
const u64 offset{rp.Pop<u64>()};
const std::vector<u8> data{ctx.ReadBuffer()};
const std::size_t size{std::min(data.size(), backing.GetSize() - offset)};
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
if (data.size() > backing.GetSize() - offset) {
if (offset > backing.GetSize()) {
LOG_ERROR(Service_AM,
"offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
backing.GetSize(), data.size(), offset);
backing.GetSize(), size, offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
return;
}
std::memcpy(backing.GetData().data() + offset, data.data(), data.size());
std::memcpy(backing.GetData().data() + offset, data.data(), size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -1070,11 +1071,11 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
const std::size_t size{ctx.GetWriteBufferSize()};
const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)};
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
if (size > backing.GetSize() - offset) {
if (offset > backing.GetSize()) {
LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
backing.GetSize(), size, offset);
@@ -5,15 +5,25 @@
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hle/service/hid/controllers/gesture.h"
#include "core/settings.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
constexpr f32 angle_threshold = 0.08f;
constexpr f32 pinch_threshold = 100.0f;
Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {}
void Controller_Gesture::OnInit() {
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
mouse_finger_id[id] = MAX_FINGERS;
keyboard_finger_id[id] = MAX_FINGERS;
udp_finger_id[id] = MAX_FINGERS;
}
}
void Controller_Gesture::OnRelease() {}
@@ -35,10 +45,153 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
// TODO(ogniK): Update gesture states
// TODO(german77): Implement all gesture types
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
}
if (Settings::values.use_touch_from_button) {
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
keyboard_finger_id[id] =
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
}
}
TouchType type = TouchType::Idle;
Attribute attributes{};
GestureProperties gesture = GetGestureProperties();
if (last_gesture.active_points != gesture.active_points) {
++last_gesture.detection_count;
}
if (gesture.active_points > 0) {
if (last_gesture.active_points == 0) {
attributes.is_new_touch.Assign(true);
last_gesture.average_distance = gesture.average_distance;
last_gesture.angle = gesture.angle;
}
type = TouchType::Touch;
if (gesture.mid_point.x != last_entry.x || gesture.mid_point.y != last_entry.y) {
type = TouchType::Pan;
}
if (std::abs(gesture.average_distance - last_gesture.average_distance) > pinch_threshold) {
type = TouchType::Pinch;
}
if (std::abs(gesture.angle - last_gesture.angle) > angle_threshold) {
type = TouchType::Rotate;
}
cur_entry.delta_x = gesture.mid_point.x - last_entry.x;
cur_entry.delta_y = gesture.mid_point.y - last_entry.y;
// TODO: Find how velocities are calculated
cur_entry.vel_x = static_cast<float>(cur_entry.delta_x) * 150.1f;
cur_entry.vel_y = static_cast<float>(cur_entry.delta_y) * 150.1f;
// Slowdown the rate of change for less flapping
last_gesture.average_distance =
(last_gesture.average_distance * 0.9f) + (gesture.average_distance * 0.1f);
last_gesture.angle = (last_gesture.angle * 0.9f) + (gesture.angle * 0.1f);
} else {
cur_entry.delta_x = 0;
cur_entry.delta_y = 0;
cur_entry.vel_x = 0;
cur_entry.vel_y = 0;
}
last_gesture.active_points = gesture.active_points;
cur_entry.detection_count = last_gesture.detection_count;
cur_entry.type = type;
cur_entry.attributes = attributes;
cur_entry.x = gesture.mid_point.x;
cur_entry.y = gesture.mid_point.y;
cur_entry.point_count = static_cast<s32>(gesture.active_points);
for (size_t id = 0; id < MAX_POINTS; id++) {
cur_entry.points[id].x = gesture.points[id].x;
cur_entry.points[id].y = gesture.points[id].y;
}
cur_entry.rotation_angle = 0;
cur_entry.scale = 0;
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_Gesture::OnLoadInputDevices() {}
void Controller_Gesture::OnLoadInputDevices() {
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
}
std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
std::size_t first_free_id = 0;
while (first_free_id < MAX_POINTS) {
if (!fingers[first_free_id].pressed) {
return first_free_id;
} else {
first_free_id++;
}
}
return std::nullopt;
}
std::size_t Controller_Gesture::UpdateTouchInputEvent(
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
const auto& [x, y, pressed] = touch_input;
if (pressed) {
if (finger_id == MAX_POINTS) {
const auto first_free_id = GetUnusedFingerID();
if (!first_free_id) {
// Invalid finger id do nothing
return MAX_POINTS;
}
finger_id = first_free_id.value();
fingers[finger_id].pressed = true;
}
fingers[finger_id].x = x;
fingers[finger_id].y = y;
return finger_id;
}
if (finger_id != MAX_POINTS) {
fingers[finger_id].pressed = false;
}
return MAX_POINTS;
}
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
GestureProperties gesture;
std::array<Finger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
gesture.active_points =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
for (size_t id = 0; id < gesture.active_points; ++id) {
gesture.points[id].x =
static_cast<int>(active_fingers[id].x * Layout::ScreenUndocked::Width);
gesture.points[id].y =
static_cast<int>(active_fingers[id].y * Layout::ScreenUndocked::Height);
gesture.mid_point.x += static_cast<int>(gesture.points[id].x / gesture.active_points);
gesture.mid_point.y += static_cast<int>(gesture.points[id].y / gesture.active_points);
}
for (size_t id = 0; id < gesture.active_points; ++id) {
const double distance =
std::pow(static_cast<float>(gesture.mid_point.x - gesture.points[id].x), 2) +
std::pow(static_cast<float>(gesture.mid_point.y - gesture.points[id].y), 2);
gesture.average_distance +=
static_cast<float>(distance) / static_cast<float>(gesture.active_points);
}
gesture.angle = std::atan2(static_cast<float>(gesture.mid_point.y - gesture.points[0].y),
static_cast<float>(gesture.mid_point.x - gesture.points[0].x));
return gesture;
}
} // namespace Service::HID
+84 -8
View File
@@ -5,8 +5,10 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
@@ -28,29 +30,64 @@ public:
void OnLoadInputDevices() override;
private:
struct Locations {
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
enum class TouchType : u32 {
Idle, // Nothing touching the screen
Complete, // Unknown. End of touch?
Cancel, // Never triggered
Touch, // Pressing without movement
Press, // Never triggered
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
enum class Direction : u32 {
None,
Left,
Up,
Right,
Down,
};
struct Attribute {
union {
u32_le raw{};
BitField<0, 1, u32> is_new_touch;
BitField<1, 1, u32> is_double_tap;
};
};
static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
struct Points {
s32_le x;
s32_le y;
};
static_assert(sizeof(Points) == 8, "Points is an invalid size");
struct GestureState {
s64_le sampling_number;
s64_le sampling_number2;
s64_le detection_count;
s32_le type;
s32_le dir;
TouchType type;
Direction dir;
s32_le x;
s32_le y;
s32_le delta_x;
s32_le delta_y;
f32 vel_x;
f32 vel_y;
s32_le attributes;
f32 scale;
f32 rotation;
s32_le location_count;
std::array<Locations, 4> locations;
Attribute attributes;
u32 scale;
u32 rotation_angle;
s32_le point_count;
std::array<Points, 4> points;
};
static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
@@ -58,6 +95,45 @@ private:
CommonHeader header;
std::array<GestureState, 17> gesture_states;
};
static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
struct Finger {
f32 x{};
f32 y{};
bool pressed{};
};
struct GestureProperties {
std::array<Points, MAX_POINTS> points{};
std::size_t active_points{};
Points mid_point{};
s64_le detection_count{};
u64_le delta_time{};
float average_distance{};
float angle{};
};
// Returns an unused finger id, if there is no fingers avaliable MAX_FINGERS will be returned
std::optional<size_t> GetUnusedFingerID() const;
/** If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
* changes will be made. Updates the coordinates if the finger id it's already set. If the touch
* ends delays the output by one frame to set the end_touch flag before finally freeing the
* finger id */
size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
size_t finger_id);
// Returns the average distance, angle and middle point of the active fingers
GestureProperties GetGestureProperties();
SharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_mouse_device;
std::unique_ptr<Input::TouchDevice> touch_udp_device;
std::unique_ptr<Input::TouchDevice> touch_btn_device;
std::array<size_t, MAX_FINGERS> mouse_finger_id;
std::array<size_t, MAX_FINGERS> keyboard_finger_id;
std::array<size_t, MAX_FINGERS> udp_finger_id;
std::array<Finger, MAX_POINTS> fingers;
GestureProperties last_gesture;
};
} // namespace Service::HID
@@ -21,6 +21,7 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_TRIGGER_MAX = 0x7fff;
[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
@@ -48,6 +49,8 @@ Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
return NPadControllerType::JoyRight;
case Settings::ControllerType::Handheld:
return NPadControllerType::Handheld;
case Settings::ControllerType::GameCube:
return NPadControllerType::GameCube;
default:
UNREACHABLE();
return NPadControllerType::ProController;
@@ -67,6 +70,8 @@ Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
return Settings::ControllerType::RightJoycon;
case NPadControllerType::Handheld:
return Settings::ControllerType::Handheld;
case NPadControllerType::GameCube:
return Settings::ControllerType::GameCube;
default:
UNREACHABLE();
return Settings::ControllerType::ProController;
@@ -209,6 +214,13 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.assignment_mode = NpadAssignments::Single;
controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
break;
case NPadControllerType::GameCube:
controller.style_set.gamecube.Assign(1);
// The GC Controller behaves like a wired Pro Controller
controller.device_type.fullkey.Assign(1);
controller.system_properties.is_vertical.Assign(1);
controller.system_properties.use_plus.Assign(1);
break;
case NPadControllerType::Pokeball:
controller.style_set.palma.Assign(1);
controller.device_type.palma.Assign(1);
@@ -259,6 +271,7 @@ void Controller_NPad::OnInit() {
style.joycon_right.Assign(1);
style.joycon_dual.Assign(1);
style.fullkey.Assign(1);
style.gamecube.Assign(1);
style.palma.Assign(1);
}
@@ -339,6 +352,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
auto& pad_state = npad_pad_states[controller_idx].pad_states;
auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
auto& trigger_entry = npad_trigger_states[controller_idx];
const auto& button_state = buttons[controller_idx];
const auto& analog_state = sticks[controller_idx];
const auto [stick_l_x_f, stick_l_y_f] =
@@ -404,6 +418,17 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
}
if (controller_type == NPadControllerType::GameCube) {
trigger_entry.l_analog = static_cast<s32>(
button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
trigger_entry.r_analog = static_cast<s32>(
button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
pad_state.zl.Assign(false);
pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
}
}
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -418,6 +443,11 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
&npad.joy_left_states, &npad.joy_right_states, &npad.palma_states,
&npad.system_ext_states};
// There is the posibility to have more controllers with analog triggers
const std::array<TriggerGeneric*, 1> controller_triggers{
&npad.gc_trigger_states,
};
for (auto* main_controller : controller_npads) {
main_controller->common.entry_count = 16;
main_controller->common.total_entry_count = 17;
@@ -435,6 +465,21 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
cur_entry.timestamp2 = cur_entry.timestamp;
}
for (auto* analog_trigger : controller_triggers) {
analog_trigger->entry_count = 16;
analog_trigger->total_entry_count = 17;
const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
analog_trigger->timestamp = core_timing.GetCPUTicks();
analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
cur_entry.timestamp = last_entry.timestamp + 1;
cur_entry.timestamp2 = cur_entry.timestamp;
}
const auto& controller_type = connected_controllers[i].type;
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
@@ -444,6 +489,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
RequestPadStateUpdate(npad_index);
auto& pad_state = npad_pad_states[npad_index];
auto& trigger_state = npad_trigger_states[npad_index];
auto& main_controller =
npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
@@ -456,6 +502,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
auto& libnx_entry =
npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
auto& trigger_entry =
npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
libnx_entry.connection_status.raw = 0;
libnx_entry.connection_status.is_connected.Assign(1);
@@ -524,6 +572,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_entry.connection_status.is_right_connected.Assign(1);
break;
case NPadControllerType::GameCube:
main_controller.connection_status.raw = 0;
main_controller.connection_status.is_connected.Assign(1);
main_controller.connection_status.is_wired.Assign(1);
main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
main_controller.pad.l_stick = pad_state.l_stick;
main_controller.pad.r_stick = pad_state.r_stick;
trigger_entry.l_analog = trigger_state.l_analog;
trigger_entry.r_analog = trigger_state.r_analog;
libnx_entry.connection_status.is_wired.Assign(1);
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
pokeball_entry.connection_status.is_connected.Assign(1);
@@ -674,6 +734,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
right_sixaxis_entry.orientation = motion_devices[1].orientation;
}
break;
case NPadControllerType::GameCube:
case NPadControllerType::Pokeball:
break;
}
@@ -1135,6 +1196,8 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return style.joycon_left;
case NPadControllerType::JoyRight:
return style.joycon_right;
case NPadControllerType::GameCube:
return style.gamecube;
case NPadControllerType::Pokeball:
return style.palma;
default:
+25 -1
View File
@@ -51,6 +51,7 @@ public:
JoyDual,
JoyLeft,
JoyRight,
GameCube,
Pokeball,
};
@@ -60,6 +61,7 @@ public:
JoyconDual = 5,
JoyconLeft = 6,
JoyconRight = 7,
GameCube = 8,
Pokeball = 9,
MaxNpadType = 10,
};
@@ -389,6 +391,25 @@ private:
};
static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
struct TriggerState {
s64_le timestamp{};
s64_le timestamp2{};
s32_le l_analog{};
s32_le r_analog{};
};
static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size");
struct TriggerGeneric {
INSERT_PADDING_BYTES(0x4);
s64_le timestamp;
INSERT_PADDING_BYTES(0x4);
s64_le total_entry_count;
s64_le last_entry_index;
s64_le entry_count;
std::array<TriggerState, 17> trigger{};
};
static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
struct NPadSystemProperties {
union {
s64_le raw{};
@@ -509,7 +530,9 @@ private:
AppletFooterUiType footer_type;
// nfc_states needs to be checked switchbrew does not match with HW
NfcXcdHandle nfc_states;
INSERT_PADDING_BYTES(0xdef);
INSERT_PADDING_BYTES(0x8); // Mutex
TriggerGeneric gc_trigger_states;
INSERT_PADDING_BYTES(0xc1f);
};
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
@@ -560,6 +583,7 @@ private:
f32 sixaxis_fusion_parameter2{};
bool sixaxis_at_rest{true};
std::array<ControllerPad, 10> npad_pad_states{};
std::array<TriggerState, 10> npad_trigger_states{};
bool is_in_lr_assignment_mode{false};
Core::System& system;
};
+121 -4
View File
@@ -15,9 +15,9 @@
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/irs.h"
@@ -273,8 +273,8 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
{204, &Hid::PermitVibration, "PermitVibration"},
{205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"},
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
{207, nullptr, "SendVibrationGcErmCommand"},
{208, nullptr, "GetActualVibrationGcErmCommand"},
{207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
{208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
{209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
{211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
@@ -1093,7 +1093,22 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
VibrationDeviceInfo vibration_device_info;
vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
switch (vibration_device_handle.npad_type) {
case Controller_NPad::NpadType::ProController:
case Controller_NPad::NpadType::Handheld:
case Controller_NPad::NpadType::JoyconDual:
case Controller_NPad::NpadType::JoyconLeft:
case Controller_NPad::NpadType::JoyconRight:
default:
vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
break;
case Controller_NPad::NpadType::GameCube:
vibration_device_info.type = VibrationDeviceType::GcErm;
break;
case Controller_NPad::NpadType::Pokeball:
vibration_device_info.type = VibrationDeviceType::Unknown;
break;
}
switch (vibration_device_handle.device_index) {
case Controller_NPad::DeviceIndex::Left:
@@ -1215,6 +1230,108 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle vibration_device_handle;
u64 applet_resource_user_id;
VibrationGcErmCommand gc_erm_command;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
/**
* Note: This uses yuzu-specific behavior such that the StopHard command produces
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
* in order to differentiate between Stop and StopHard commands.
* This is done to reuse the controller vibration functions made for regular controllers.
*/
const auto vibration_value = [parameters] {
switch (parameters.gc_erm_command) {
case VibrationGcErmCommand::Stop:
return Controller_NPad::VibrationValue{
.amp_low = 0.0f,
.freq_low = 160.0f,
.amp_high = 0.0f,
.freq_high = 320.0f,
};
case VibrationGcErmCommand::Start:
return Controller_NPad::VibrationValue{
.amp_low = 1.0f,
.freq_low = 160.0f,
.amp_high = 1.0f,
.freq_high = 320.0f,
};
case VibrationGcErmCommand::StopHard:
return Controller_NPad::VibrationValue{
.amp_low = 0.0f,
.freq_low = 0.0f,
.amp_high = 0.0f,
.freq_high = 0.0f,
};
default:
return Controller_NPad::DEFAULT_VIBRATION_VALUE;
}
}();
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateController(parameters.vibration_device_handle, vibration_value);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
"gc_erm_command={}",
parameters.vibration_device_handle.npad_type,
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
parameters.gc_erm_command);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetLastVibration(parameters.vibration_device_handle);
const auto gc_erm_command = [last_vibration] {
if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) {
return VibrationGcErmCommand::Start;
}
/**
* Note: This uses yuzu-specific behavior such that the StopHard command produces
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
* SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
* This is done to reuse the controller vibration functions made for regular controllers.
*/
if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) {
return VibrationGcErmCommand::StopHard;
}
return VibrationGcErmCommand::Stop;
}();
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.vibration_device_handle.npad_type,
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(gc_erm_command);
}
void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+12 -2
View File
@@ -14,7 +14,7 @@ struct EventType;
}
namespace Kernel {
class SharedMemory;
class KSharedMemory;
}
namespace Service::SM {
@@ -69,7 +69,7 @@ private:
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
std::shared_ptr<Kernel::SharedMemory> shared_mem;
std::shared_ptr<Kernel::KSharedMemory> shared_mem;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
@@ -136,6 +136,8 @@ private:
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
@@ -154,7 +156,9 @@ private:
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
enum class VibrationDeviceType : u32 {
Unknown = 0,
LinearResonantActuator = 1,
GcErm = 2,
};
enum class VibrationDevicePosition : u32 {
@@ -163,6 +167,12 @@ private:
Right = 2,
};
enum class VibrationGcErmCommand : u64 {
Stop = 0,
Start = 1,
StopHard = 2,
};
struct VibrationDeviceInfo {
VibrationDeviceType type{};
VibrationDevicePosition position{};
+1 -1
View File
@@ -6,8 +6,8 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/irs.h"
namespace Service::HID {
+2 -2
View File
@@ -12,7 +12,7 @@ class System;
}
namespace Kernel {
class SharedMemory;
class KSharedMemory;
}
namespace Service::HID {
@@ -42,7 +42,7 @@ private:
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
std::shared_ptr<Kernel::SharedMemory> shared_mem;
std::shared_ptr<Kernel::KSharedMemory> shared_mem;
const u32 device_handle{0xABCD};
};
+13
View File
@@ -0,0 +1,13 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/result.h"
namespace Service::LDN {
constexpr ResultCode ERROR_DISABLED{ErrorModule::LDN, 22};
} // namespace Service::LDN
+32 -6
View File
@@ -6,6 +6,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/result.h"
#include "core/hle/service/ldn/errors.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/sm/sm.h"
@@ -103,7 +104,7 @@ public:
: ServiceFramework{system_, "IUserLocalCommunicationService"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetState"},
{0, &IUserLocalCommunicationService::GetState, "GetState"},
{1, nullptr, "GetNetworkInfo"},
{2, nullptr, "GetIpv4Address"},
{3, nullptr, "GetDisconnectReason"},
@@ -138,13 +139,38 @@ public:
RegisterHandlers(functions);
}
void Initialize2(Kernel::HLERequestContext& ctx) {
void GetState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
// Result success seem make this services start network and continue.
// If we just pass result error then it will stop and maybe try again and again.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_UNKNOWN);
IPC::ResponseBuilder rb{ctx, 3};
// Indicate a network error, as we do not actually emulate LDN
rb.Push(static_cast<u32>(State::Error));
rb.Push(RESULT_SUCCESS);
}
void Initialize2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LDN, "called");
is_initialized = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_DISABLED);
}
private:
enum class State {
None,
Initialized,
AccessPointOpened,
AccessPointCreated,
StationOpened,
StationConnected,
Error,
};
bool is_initialized{};
};
class LDNS final : public ServiceFramework<LDNS> {
+14 -16
View File
@@ -11,8 +11,8 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/service/ldr/ldr.h"
@@ -287,12 +287,11 @@ public:
rb.Push(RESULT_SUCCESS);
}
bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start,
std::size_t size) const {
constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize};
bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
constexpr std::size_t padding_size{4 * Kernel::PageSize};
const auto start_info{page_table.QueryInfo(start - 1)};
if (start_info.state != Kernel::Memory::MemoryState::Free) {
if (start_info.state != Kernel::KMemoryState::Free) {
return {};
}
@@ -302,21 +301,20 @@ public:
const auto end_info{page_table.QueryInfo(start + size)};
if (end_info.state != Kernel::Memory::MemoryState::Free) {
if (end_info.state != Kernel::KMemoryState::Free) {
return {};
}
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
}
VAddr GetRandomMapRegion(const Kernel::Memory::PageTable& page_table, std::size_t size) const {
VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const {
VAddr addr{};
const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >>
Kernel::Memory::PageBits};
Kernel::PageBits};
do {
addr = page_table.GetAliasCodeRegionStart() +
(Kernel::Memory::SystemControl::GenerateRandomRange(0, end_pages)
<< Kernel::Memory::PageBits);
(Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits);
} while (!page_table.IsInsideAddressSpace(addr, size) ||
page_table.IsInsideHeapRegion(addr, size) ||
page_table.IsInsideAliasRegion(addr, size));
@@ -387,7 +385,7 @@ public:
const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size};
const VAddr bss_end_addr{
Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)};
Common::AlignUp(bss_start + nro_header.bss_size, Kernel::PageSize)};
auto CopyCode{[&](VAddr src_addr, VAddr dst_addr, u64 size) {
std::vector<u8> source_data(size);
@@ -402,12 +400,12 @@ public:
nro_header.segment_headers[DATA_INDEX].memory_size);
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute));
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
ro_start, data_start - ro_start, Kernel::Memory::MemoryPermission::Read));
text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start,
Kernel::KMemoryPermission::Read));
return process->PageTable().SetCodeMemoryPermission(
data_start, bss_end_addr - data_start, Kernel::Memory::MemoryPermission::ReadAndWrite);
data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
}
void LoadNro(Kernel::HLERequestContext& ctx) {
+2 -2
View File
@@ -19,9 +19,9 @@
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/pl_u.h"
@@ -131,7 +131,7 @@ struct PL_U::Impl {
}
/// Handle to shared memory region designated for a shared font
std::shared_ptr<Kernel::SharedMemory> shared_font_mem;
std::shared_ptr<Kernel::KSharedMemory> shared_font_mem;
/// Backing memory for the shared font data
std::shared_ptr<Kernel::PhysicalMemory> shared_font;
@@ -34,8 +34,7 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
case 0xa: {
if (command.length == 0x1c) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
Tegra::ChCommandHeaderList cmdlist(1);
cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}};
system.GPU().PushCommandBuffer(cmdlist);
}
return UnmapBuffer(input, output);
@@ -28,8 +28,13 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
return GetWaitbase(input, output);
case 0x9:
return MapBuffer(input, output);
case 0xa:
case 0xa: {
if (command.length == 0x1c) {
Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}};
system.GPU().PushCommandBuffer(cmdlist);
}
return UnmapBuffer(input, output);
}
default:
break;
}
+9 -3
View File
@@ -341,12 +341,18 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::RequestParser rp{ctx};
const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>();
const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>();
Clock::ClockSnapshot snapshot_a;
Clock::ClockSnapshot snapshot_b;
const auto snapshot_a_data = ctx.ReadBuffer(0);
const auto snapshot_b_data = ctx.ReadBuffer(1);
std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot));
std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot));
Clock::TimeSpanType time_span_type{};
s64 span{};
if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween(
snapshot_b.steady_clock_time_point, span)};
result != RESULT_SUCCESS) {
@@ -279,6 +279,10 @@ const SharedMemory& TimeManager::GetSharedMemory() const {
return impl->shared_memory;
}
void TimeManager::Shutdown() {
impl.reset();
}
void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
impl->UpdateLocalSystemClockTime(system, posix_time);
}
+2
View File
@@ -61,6 +61,8 @@ public:
const SharedMemory& GetSharedMemory() const;
void Shutdown();
void SetupTimeZoneManager(std::string location_name,
Clock::SteadyClockTimePoint time_zone_updated_time_point,
std::size_t total_location_name_count, u128 time_zone_rule_version,
@@ -22,7 +22,7 @@ SharedMemory::SharedMemory(Core::System& system) : system(system) {
SharedMemory::~SharedMemory() = default;
std::shared_ptr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() const {
std::shared_ptr<Kernel::KSharedMemory> SharedMemory::GetSharedMemoryHolder() const {
return shared_memory_holder;
}

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