Compare commits

...

122 Commits

Author SHA1 Message Date
Kelebek1
d31dbb1bc1 Implement glDepthRangeIndexeddNV 2021-02-24 22:26:53 +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
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
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
c7325c6a4c gl_texture_cache: Lazily create non-sRGB texture views for sRGB formats
This creates non-sRGB texture views for sRGB texture formats to allow for interfacing with these views in compute shaders using imageLoad and imageStore.

Co-Authored-By: Rodrigo Locatti <reinuseslisp@airmail.cc>
2021-02-13 13:27:50 -05: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
Morph
83227ad981 Merge pull request #5919 from ReinUsesLisp/stream-buffer-tragic
gl_stream_buffer/vk_staging_buffer_pool: Fix size check
2021-02-13 21:25:45 +08:00
ReinUsesLisp
dd9caf9aa0 vk_master_semaphore: Mark gpu_tick atomic operations with relaxed order 2021-02-13 05:57:28 -03:00
ReinUsesLisp
6171566296 vk_staging_buffer_pool: Inline tick tests
Load the current tick to a local variable, moving it out of an atomic
and allowing us to compare the value without going through a pointer
each time. This should make the loop more optimizable.
2021-02-13 05:14:11 -03:00
ReinUsesLisp
682d82faf3 gl_stream_buffer/vk_staging_buffer_pool: Fix size check
Fix a tragic off-by-one condition that causes Vulkan's stream buffer to
think it's always full, using fallback memory. The OpenGL was also
affected by this bug to a lesser extent.
2021-02-13 05:11:48 -03:00
LC
710aa22f7c Merge pull request #5915 from lat9nq/screenshots-dir-fix
yuzu: Create screenshot path before capture
2021-02-13 02:56:23 -05:00
LC
6f1ad6aa9f Merge pull request #5916 from ameerj/maxwell-gl-unused
maxwell_to_gl: Remove unused code
2021-02-13 02:55:59 -05:00
LC
06e3d3a658 Merge pull request #5917 from ReinUsesLisp/require-robustness2
vulkan_device: Require VK_EXT_robustness2
2021-02-13 02:55:31 -05:00
ReinUsesLisp
757fd1e917 vulkan_device: Require VK_EXT_robustness2
We are already using robustness2 features without requiring it
explicitly, causing potential crashes on drivers without the extension.
Requiring this at boot allows better diagnostics for it and formalizes
our usage on the extension.
2021-02-13 03:31:50 -03:00
bunnei
d3c7a7e7cf Merge pull request #5741 from ReinUsesLisp/new-bufcache
video_core: Reimplement the buffer cache
2021-02-12 22:22:18 -08:00
ReinUsesLisp
13becdf18a config: Make high GPU accuracy the default
This is a better default for most games, yielding better performance and
less graphical issues.
2021-02-13 02:38:05 -03:00
ReinUsesLisp
5b35b01070 video_core: Fix clang build issues 2021-02-13 02:26:47 -03:00
ReinUsesLisp
025fe458ae vk_staging_buffer_pool: Fix softlock when stream buffer overflows
There was still a code path that could wait on a timeline semaphore tick
that would never be signalled.

While we are at it, make use of more STL algorithms.
2021-02-13 02:18:38 -03:00
ReinUsesLisp
3a2eefb16c vk_buffer_cache: Add support for null index buffers
Games can bind a null index buffer (size=0) where all indices are
evaluated as zero. VK_EXT_robustness2 doesn't support this and all
drivers segfault when a null index buffer is passed to
vkCmdBindIndexBuffer.

Workaround this by creating a 4 byte buffer and filling it with zeroes.
If it's read out of bounds, robustness takes care of returning zeroes as
indices.
2021-02-13 02:18:38 -03:00
ReinUsesLisp
0b8b961442 buffer_cache: Add extra bytes to guest SSBOs
Bind extra bytes beyond the guest API's bound range.
This is due to some games like Astral Chain operating out of bounds.
Binding the whole map range would be technically correct, but games
have large maps that make this approach unaffordable for now.
2021-02-13 02:18:38 -03:00
ReinUsesLisp
93a69b6cc8 Merge branch 'bytes-to-map-end' into new-bufcache-wip 2021-02-13 02:18:35 -03:00
ReinUsesLisp
7402442442 vk_staging_buffer_pool: Get a staging buffer instead of waiting
Avoids waiting idle while the GPU finishes to do work, and fixes an
issue where we'd wait forever if a single command buffer (logic tick)
all the data.
2021-02-13 02:18:05 -03:00
ReinUsesLisp
75fd3f95a3 yuzu/config: Disable assembly shaders by default
Due to BindBufferRangeNV limitations and poor quality code emission from
our side, assembly shaders are currently slower than GLSL. Their build
time and feature advantages are still relevant, but they are outweighted
by their runtime performance.
2021-02-13 02:18:05 -03:00
ReinUsesLisp
0b631f22fc renderer_opengl: Remove interop
Remove unused interop code from the OpenGL backend.
2021-02-13 02:18:04 -03:00
ReinUsesLisp
3da87d3f12 gl_buffer_cache: Drop interop based parameter buffer workarounds
Sacrify runtime performance to avoid generating kernel exceptions on
Windows due to our abusive aliasing of interop buffer objects.
2021-02-13 02:17:24 -03:00
ReinUsesLisp
2b95c137ff buffer_cache: Heuristically detect stream buffers
Detect when a memory region has been joined several times and increase
the size of the created buffer on those instances. The buffer is assumed
to be a "stream buffer", increasing its size should stop us from
constantly recreating it and fragmenting memory.
2021-02-13 02:17:24 -03:00
ReinUsesLisp
ec9354d6d9 buffer_cache: Split CreateBuffer in separate functions
Allow adding functionality to each function without making CreateBuffer
more complex.
2021-02-13 02:17:24 -03:00
ReinUsesLisp
a02b4e1df6 buffer_cache: Skip cache on small uploads on Vulkan
Ports from OpenGL the optimization to skip small 3D uniform buffer
uploads. This will take advantage of the previously introduced stream
buffer.

Fixes instances where the staging buffer offset was being ignored.
2021-02-13 02:17:24 -03:00
ReinUsesLisp
35df1d1864 vk_staging_buffer_pool: Add stream buffer for small uploads
This uses a ring buffer similar to OpenGL's stream buffer for small
uploads. This stops us from allocating several small buffers, reducing
memory fragmentation and cache locality.

It uses dedicated allocations when possible.
2021-02-13 02:17:24 -03:00
ReinUsesLisp
8fd518ec40 vulkan_device: Enable robustBufferAccess
Fix regression on Pascal on Animal Crossing: New Horizons, fixing a
validation error.
2021-02-13 02:17:23 -03:00
ReinUsesLisp
82c2601555 video_core: Reimplement the buffer cache
Reimplement the buffer cache using cached bindings and page level
granularity for modification tracking. This also drops the usage of
shared pointers and virtual functions from the cache.

- Bindings are cached, allowing to skip work when the game changes few
  bits between draws.
- OpenGL Assembly shaders no longer copy when a region has been modified
  from the GPU to emulate constant buffers, instead GL_EXT_memory_object
  is used to alias sub-buffers within the same allocation.
- OpenGL Assembly shaders stream constant buffer data using
  glProgramBufferParametersIuivNV, from NV_parameter_buffer_object. In
  theory this should save one hash table resolve inside the driver
  compared to glBufferSubData.
- A new OpenGL stream buffer is implemented based on fences for drivers
  that are not Nvidia's proprietary, due to their low performance on
  partial glBufferSubData calls synchronized with 3D rendering (that
  some games use a lot).
- Most optimizations are shared between APIs now, allowing Vulkan to
  cache more bindings than before, skipping unnecesarry work.

This commit adds the necessary infrastructure to use Vulkan object from
OpenGL. Overall, it improves performance and fixes some bugs present on
the old cache. There are still some edge cases hit by some games that
harm performance on some vendors, this are planned to be fixed in later
commits.
2021-02-13 02:17:22 -03:00
ReinUsesLisp
a39d9c5194 vulkan_common: Expose interop and headless devices 2021-02-13 02:16:21 -03:00
ReinUsesLisp
47d5ec6cfc vulkan_common: Make interop extensions mandatory 2021-02-13 02:16:21 -03:00
ReinUsesLisp
40ed0cb920 vulkan_device: Enable robust buffers 2021-02-13 02:16:21 -03:00
ReinUsesLisp
1a987054c5 vulkan_device: Use designated initializers for features 2021-02-13 02:16:21 -03:00
ReinUsesLisp
79afdeaf08 vulkan_wrapper: Add memory barrier pipeline barrier helper 2021-02-13 02:16:21 -03:00
ReinUsesLisp
004a8d6a7a vulkan_device: Fix formatting of constants 2021-02-13 02:16:21 -03:00
ReinUsesLisp
16f97ded21 vulkan_wrapper: Add interop functions 2021-02-13 02:16:21 -03:00
ReinUsesLisp
9735c34f5d vulkan_instance: Initialize Vulkan instance in a separate thread
Workaround an issue on Nvidia where creating a Vulkan instance from an
active OpenGL thread disables threaded optimization on the driver.
This optimization is important to have good performance on Nvidia
OpenGL.
2021-02-13 02:16:21 -03:00
ReinUsesLisp
dde19e7d75 vulkan_wrapper: Pull Windows symbols 2021-02-13 02:16:21 -03:00
ReinUsesLisp
75ccd9959c gpu: Report renderer errors with exceptions
Instead of using a two step initialization to report errors, initialize
the GPU renderer and rasterizer on the constructor and report errors
through std::runtime_error.
2021-02-13 02:16:19 -03:00
ReinUsesLisp
19156292a3 tests/buffer_base: Add cached CPU writes tests
Ensure the behavior of the previous commit in tests.
2021-02-13 02:15:29 -03:00
ReinUsesLisp
9d8ca6cc4a buffer_base: Add support for cached CPU writes
Some games usually write memory pages currently used by the GPU, causing
rendering issues (e.g. flashing geometry and shadows on Link's
Awakening). To workaround this issue, Guest CPU writes are delayed until
the command buffer finishes processing, but the pages are updated
immediately.

The overall behavior is:
- CPU writes are cached until they are flushed, they update the page
  state, but don't change the modification state. Cached writes stop
  pages from being flushed, in case games have meaningful data in it.
- Command processing writes (e.g. push constants) update the page state
  and are marked to the command processor as dirty. They don't remove
  the state of cached writes.
2021-02-13 02:15:29 -03:00
ameerj
069afcc633 maxwell_to_gl: Remove unused code
Removes unused declarations in maxwell_to_gl.h
2021-02-12 23:01:09 -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
bunnei
c86d770af9 Merge pull request #5877 from ameerj/res-limit-usage
kernel: More accurately utilize resource_limit
2021-02-12 18:21:30 -08:00
ameerj
ec9b6641b1 kernel: More accurately reserve and release resources 2021-02-12 19:05:24 -05:00
ameerj
5fa6b15215 kernel: KScopedReservation implementation
This implements KScopedReservation, allowing resource limit reservations to be more HW accurate, and release upon failure without requiring too many conditionals.
2021-02-12 18:57:34 -05:00
Chloe
37939482fb kernel: Unify result codes (#5890)
* kernel: Unify result codes

Drop the usage of ERR_NAME convention in kernel for ResultName. Removed seperation between svc_results.h & errors.h as we mainly include both most of the time anyways.

* oops

* rename errors to svc_results
2021-02-12 15:43:01 -08:00
lat9nq
dcc0617cc2 yuzu: Create screenshot path before capture
Allows screenshots in cases where the screenshots path doesn't already
exist.
2021-02-12 17:26:01 -05:00
bunnei
a0379c2db5 Merge pull request #5902 from lioncash/core-warn
core: Silence various warnings on Clang 12
2021-02-11 18:57:23 -08:00
bunnei
e53b6ecc76 Merge pull request #5869 from german77/mousePanning
input_common: Add mouse panning
2021-02-11 09:58:23 -08:00
bunnei
f06c3f4907 Merge pull request #5908 from Morph1984/swkbd-finalize
software_keyboard: Implement Finalize request command
2021-02-10 21:49:53 -08:00
Morph
886043a6d2 software_keyboard: Implement Finalize request command 2021-02-10 21:42:49 -05:00
bunnei
3e6e0d8f13 Merge pull request #5893 from lioncash/input
configure_input_player_widget: Minor cleanup
2021-02-10 10:55:59 -08:00
LC
dee133ab3d Merge pull request #5904 from lat9nq/common-sized-dealloc
common: Add -fsized-deallocation as a Clang flag
2021-02-09 23:45:52 -05:00
LC
3c22ce035b Merge pull request #5905 from lat9nq/core-sized-dealloc
core: Add -fsized-dealloction as a Clang flag
2021-02-09 23:45:36 -05:00
LC
a5e184e948 Merge pull request #5903 from lat9nq/config-silence-warns
configure_input_player_widget: Silence unused variable warnings
2021-02-09 23:44:58 -05: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
lat9nq
0e004269a9 configure_input_player_widget: Silence unused variable warnings
Prevents clang 11 from throwing an error since these variables are
unused.
2021-02-09 22:09:23 -05:00
lat9nq
68f718943e core: Add -fsized-dealloction as a Clang flag
Prevents a operator delete error when compiling with Clang 11.
2021-02-09 21:27:12 -05:00
Lioncash
0cd40fb523 bsd: Remove usage of optional emplace() with no arguments
Clang 12 currently falls over in the face of this.
2021-02-09 17:50:29 -05:00
Lioncash
1dab8acf5f am/controller: Remove [[fallthrough]] from unreachable path
Prevents warnings on clang 12. This path is reachable on other
variations of the build that disable the unreachable macro.
2021-02-09 17:44:14 -05:00
Lioncash
d64ba58759 nfp: Correct uninitialized size being used within GetTagInfo()
We were previously the name of the object being initialized within its
own initializer, which results in uninitialized data being read.
2021-02-09 17:42:02 -05:00
Lioncash
864762cac9 configure_input_player_widget: Reduce duplication of array accessors where applicable
Reduces the amount of code to read in expressions a little bit by
separating constituents out a little.
2021-02-09 13:15:05 -05:00
Lioncash
5c7c212f61 configure_input_player_widget: Avoid nontrivial copies where applicable
Previously a function was copying an array of 20 std::string instances
by value.
2021-02-09 13:15:03 -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
german
52b79ac009 Add mouse panning 2021-02-07 20:31:58 -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
215 changed files with 5711 additions and 4358 deletions

18
.ci/scripts/clang/docker.sh Executable file
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

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 ./

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

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
.gitmodules vendored
View File

@@ -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

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)

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)

View File

@@ -1,7 +1,7 @@
[Icon Theme]
Name=colorful_dark
Comment=Colorful theme (Dark style)
Inherits=default
Inherits=colorful
Directories=16x16
[16x16]

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]

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
dist/yuzu.bmp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

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

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;

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

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);

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);

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

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

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

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;

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> {

View File

@@ -168,7 +168,6 @@ add_library(common STATIC
time_zone.cpp
time_zone.h
tree.h
uint128.cpp
uint128.h
uuid.cpp
uuid.h

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

View File

@@ -61,50 +61,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;
}

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

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

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"

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() {

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
@@ -148,7 +147,7 @@ add_library(core STATIC
hle/kernel/client_session.h
hle/kernel/code_set.cpp
hle/kernel/code_set.h
hle/kernel/errors.h
hle/kernel/svc_results.h
hle/kernel/global_scheduler_context.cpp
hle/kernel/global_scheduler_context.h
hle/kernel/handle_table.cpp
@@ -174,6 +173,7 @@ add_library(core STATIC
hle/kernel/k_scheduler.h
hle/kernel/k_scheduler_lock.h
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_synchronization_object.cpp
hle/kernel/k_synchronization_object.h
@@ -223,7 +223,6 @@ add_library(core STATIC
hle/kernel/svc.cpp
hle/kernel/svc.h
hle/kernel/svc_common.h
hle/kernel/svc_results.h
hle/kernel/svc_types.h
hle/kernel/svc_wrap.h
hle/kernel/time_manager.cpp
@@ -266,6 +265,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 +400,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
@@ -653,6 +654,8 @@ else()
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
-Wno-sign-conversion
)
endif()

View File

@@ -308,6 +308,9 @@ struct System::Impl {
// Close all CPU/threading state
cpu_manager.Shutdown();
// Release the Time Manager's resources
time_manager.Shutdown();
// Shutdown kernel and core timing
core_timing.Shutdown();
kernel.Shutdown();

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

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

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 {

View File

@@ -4,11 +4,11 @@
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
@@ -21,7 +21,7 @@ std::shared_ptr<ServerPort> ClientPort::GetServerPort() const {
ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
if (active_sessions >= max_sessions) {
return ERR_MAX_CONNECTIONS_REACHED;
return ResultMaxConnectionsReached;
}
active_sessions++;

View File

@@ -3,11 +3,11 @@
// Refer to the license.txt file included.
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -43,7 +43,7 @@ ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> thread,
Core::Timing::CoreTiming& core_timing) {
// Keep ServerSession alive until we're done working with it.
if (!parent->Server()) {
return ERR_SESSION_CLOSED_BY_REMOTE;
return ResultSessionClosedByRemote;
}
// Signal the server session that new data is available

View File

@@ -1,43 +0,0 @@
// Copyright 2018 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 Kernel {
// Confirmed Switch kernel error codes
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59};
constexpr ResultCode ERR_TERMINATION_REQUESTED{ErrorModule::Kernel, 59};
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103};
constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
constexpr ResultCode ERR_INVALID_CURRENT_MEMORY{ErrorModule::Kernel, 106};
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
constexpr ResultCode ERR_CANCELLED{ErrorModule::Kernel, 118};
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
} // namespace Kernel

View File

@@ -6,12 +6,12 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
@@ -33,7 +33,7 @@ HandleTable::~HandleTable() = default;
ResultCode HandleTable::SetSize(s32 handle_table_size) {
if (static_cast<u32>(handle_table_size) > MAX_COUNT) {
LOG_ERROR(Kernel, "Handle table size {} is greater than {}", handle_table_size, MAX_COUNT);
return ERR_OUT_OF_MEMORY;
return ResultOutOfMemory;
}
// Values less than or equal to zero indicate to use the maximum allowable
@@ -53,7 +53,7 @@ ResultVal<Handle> HandleTable::Create(std::shared_ptr<Object> obj) {
const u16 slot = next_free_slot;
if (slot >= table_size) {
LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
return ERR_HANDLE_TABLE_FULL;
return ResultHandleTableFull;
}
next_free_slot = generations[slot];
@@ -76,7 +76,7 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
std::shared_ptr<Object> object = GetGeneric(handle);
if (object == nullptr) {
LOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
return Create(std::move(object));
}
@@ -84,7 +84,7 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
ResultCode HandleTable::Close(Handle handle) {
if (!IsValid(handle)) {
LOG_ERROR(Kernel, "Handle is not valid! handle={:08X}", handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
const u16 slot = GetSlot(handle);

View File

@@ -14,7 +14,6 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_readable_event.h"
@@ -26,6 +25,7 @@
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/memory.h"

View File

@@ -120,10 +120,10 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
s32 user_value{};
if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) {
LOG_ERROR(Kernel, "Invalid current memory!");
return Svc::ResultInvalidCurrentMemory;
return ResultInvalidCurrentMemory;
}
if (user_value != value) {
return Svc::ResultInvalidState;
return ResultInvalidState;
}
auto it = thread_tree.nfind_light({addr, -1});
@@ -189,10 +189,10 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
if (!succeeded) {
LOG_ERROR(Kernel, "Invalid current memory!");
return Svc::ResultInvalidCurrentMemory;
return ResultInvalidCurrentMemory;
}
if (user_value != value) {
return Svc::ResultInvalidState;
return ResultInvalidState;
}
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
@@ -221,11 +221,11 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
@@ -238,19 +238,19 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
if (!succeeded) {
slp.CancelSleep();
return Svc::ResultInvalidCurrentMemory;
return ResultInvalidCurrentMemory;
}
// Check that the value is less than the specified one.
if (user_value >= value) {
slp.CancelSleep();
return Svc::ResultInvalidState;
return ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return Svc::ResultTimedOut;
return ResultTimedOut;
}
// Set the arbiter.
@@ -288,29 +288,29 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
slp.CancelSleep();
return Svc::ResultInvalidCurrentMemory;
return ResultInvalidCurrentMemory;
}
// Check that the value is equal.
if (value != user_value) {
slp.CancelSleep();
return Svc::ResultInvalidState;
return ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return Svc::ResultTimedOut;
return ResultTimedOut;
}
// Set the arbiter.

View File

@@ -92,10 +92,10 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
if (next_owner_thread) {
next_owner_thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory);
next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
}
return Svc::ResultInvalidCurrentMemory;
return ResultInvalidCurrentMemory;
}
}
@@ -114,20 +114,20 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), Svc::ResultTerminationRequested);
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
{
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
Svc::ResultInvalidCurrentMemory);
ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle);
R_UNLESS(owner_thread, Svc::ResultInvalidHandle);
R_UNLESS(owner_thread, ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
@@ -191,13 +191,13 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
thread_to_close = owner_thread.get();
} else {
// The lock was tagged with a thread that doesn't exist.
thread->SetSyncedObject(nullptr, Svc::ResultInvalidState);
thread->SetSyncedObject(nullptr, ResultInvalidState);
thread->Wakeup();
}
}
} else {
// If the address wasn't accessible, note so.
thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory);
thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
thread->Wakeup();
}
@@ -263,12 +263,12 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
return ResultTerminationRequested;
}
// Update the value and process for the next owner.
@@ -302,7 +302,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
slp.CancelSleep();
return Svc::ResultInvalidCurrentMemory;
return ResultInvalidCurrentMemory;
}
}

View File

@@ -6,7 +6,6 @@
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
@@ -47,7 +46,7 @@ ResultCode KReadableEvent::Reset() {
KScopedSchedulerLock lk{kernel};
if (!is_signaled) {
return Svc::ResultInvalidState;
return ResultInvalidState;
}
is_signaled = false;

View File

@@ -75,7 +75,7 @@ s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
R_UNLESS(current_values[index] <= value, Svc::ResultInvalidState);
R_UNLESS(current_values[index] <= value, ResultInvalidState);
limit_values[index] = value;

View File

@@ -0,0 +1,67 @@
// Copyright 2021 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 "common/common_types.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/process.h"
namespace Kernel {
class KScopedResourceReservation {
public:
explicit KScopedResourceReservation(std::shared_ptr<KResourceLimit> l, LimitableResource r,
s64 v, s64 timeout)
: resource_limit(std::move(l)), value(v), resource(r) {
if (resource_limit && value) {
success = resource_limit->Reserve(resource, value, timeout);
} else {
success = true;
}
}
explicit KScopedResourceReservation(std::shared_ptr<KResourceLimit> l, LimitableResource r,
s64 v = 1)
: resource_limit(std::move(l)), value(v), resource(r) {
if (resource_limit && value) {
success = resource_limit->Reserve(resource, value);
} else {
success = true;
}
}
explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v, s64 t)
: KScopedResourceReservation(p->GetResourceLimit(), r, v, t) {}
explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v = 1)
: KScopedResourceReservation(p->GetResourceLimit(), r, v) {}
~KScopedResourceReservation() noexcept {
if (resource_limit && value && success) {
// resource was not committed, release the reservation.
resource_limit->Release(resource, value);
}
}
/// Commit the resource reservation, destruction of this object does not release the resource
void Commit() {
resource_limit = nullptr;
}
[[nodiscard]] bool Succeeded() const {
return success;
}
private:
std::shared_ptr<KResourceLimit> resource_limit;
s64 value;
LimitableResource resource;
bool success;
};
} // namespace Kernel

View File

@@ -40,20 +40,20 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
// Check if the timeout is zero.
if (timeout == 0) {
slp.CancelSleep();
return Svc::ResultTimedOut;
return ResultTimedOut;
}
// Check if the thread should terminate.
if (thread->IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
return ResultTerminationRequested;
}
// Check if waiting was canceled.
if (thread->IsWaitCancelled()) {
slp.CancelSleep();
thread->ClearWaitCancelled();
return Svc::ResultCancelled;
return ResultCancelled;
}
// Add the waiters.
@@ -75,7 +75,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
// Mark the thread as waiting.
thread->SetCancellable();
thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
thread->SetSyncedObject(nullptr, ResultTimedOut);
thread->SetState(ThreadState::Waiting);
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
}

View File

@@ -18,7 +18,6 @@
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -127,7 +126,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
// Set core ID and wait result.
core_id = phys_core;
wait_result = Svc::ResultNoSynchronizationObject;
wait_result = ResultNoSynchronizationObject;
// Set priorities.
priority = prio;
@@ -238,7 +237,7 @@ void KThread::Finalize() {
while (it != waiter_list.end()) {
// The thread shouldn't be a kernel waiter.
it->SetLockOwner(nullptr);
it->SetSyncedObject(nullptr, Svc::ResultInvalidState);
it->SetSyncedObject(nullptr, ResultInvalidState);
it->Wakeup();
it = waiter_list.erase(it);
}
@@ -447,7 +446,7 @@ ResultCode KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) {
// If the core id is no-update magic, preserve the ideal core id.
if (core_id == Svc::IdealCoreNoUpdate) {
core_id = virtual_ideal_core_id;
R_UNLESS(((1ULL << core_id) & v_affinity_mask) != 0, Svc::ResultInvalidCombination);
R_UNLESS(((1ULL << core_id) & v_affinity_mask) != 0, ResultInvalidCombination);
}
// Set the virtual core/affinity mask.
@@ -526,7 +525,7 @@ ResultCode KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) {
if (GetStackParameters().is_pinned) {
// Verify that the current thread isn't terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
Svc::ResultTerminationRequested);
ResultTerminationRequested);
// Note that the thread was pinned.
thread_is_pinned = true;
@@ -604,7 +603,7 @@ void KThread::WaitCancel() {
sleeping_queue->WakeupThread(this);
wait_cancelled = true;
} else {
SetSyncedObject(nullptr, Svc::ResultCancelled);
SetSyncedObject(nullptr, ResultCancelled);
SetState(ThreadState::Runnable);
wait_cancelled = false;
}
@@ -663,12 +662,12 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Verify our state.
const auto cur_state = GetState();
R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
Svc::ResultInvalidState);
ResultInvalidState);
// Either pause or resume.
if (activity == Svc::ThreadActivity::Paused) {
// Verify that we're not suspended.
R_UNLESS(!IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState);
R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Suspend.
RequestSuspend(SuspendType::Thread);
@@ -676,7 +675,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
ASSERT(activity == Svc::ThreadActivity::Runnable);
// Verify that we're suspended.
R_UNLESS(IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState);
R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Resume.
Resume(SuspendType::Thread);
@@ -698,7 +697,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
if (GetStackParameters().is_pinned) {
// Verify that the current thread isn't terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
Svc::ResultTerminationRequested);
ResultTerminationRequested);
// Note that the thread was pinned and not current.
thread_is_pinned = true;
@@ -745,7 +744,7 @@ ResultCode KThread::GetThreadContext3(std::vector<u8>& out) {
KScopedSchedulerLock sl{kernel};
// Verify that we're suspended.
R_UNLESS(IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState);
R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// If we're not terminating, get the thread's user context.
if (!IsTerminationRequested()) {
@@ -905,12 +904,11 @@ ResultCode KThread::Run() {
KScopedSchedulerLock lk{kernel};
// If either this thread or the current thread are requesting termination, note it.
R_UNLESS(!IsTerminationRequested(), Svc::ResultTerminationRequested);
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
Svc::ResultTerminationRequested);
R_UNLESS(!IsTerminationRequested(), ResultTerminationRequested);
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
// Ensure our thread state is correct.
R_UNLESS(GetState() == ThreadState::Initialized, Svc::ResultInvalidState);
R_UNLESS(GetState() == ThreadState::Initialized, ResultInvalidState);
// If the current thread has been asked to suspend, suspend it and retry.
if (GetCurrentThread(kernel).IsSuspended()) {
@@ -962,7 +960,7 @@ ResultCode KThread::Sleep(s64 timeout) {
// Check if the thread should terminate.
if (IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
return ResultTerminationRequested;
}
// Mark the thread as waiting.

View File

@@ -26,7 +26,6 @@
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
@@ -39,6 +38,7 @@
#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"
#include "core/hle/result.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;
}
@@ -141,11 +146,17 @@ struct KernelCore::Impl {
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 900).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 933).IsSuccess());
if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 0x60000)) {
// Derived from recent software updates. The kernel reserves 27MB
constexpr u64 kernel_size{0x1b00000};
if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size)) {
UNREACHABLE();
}
// Reserve secure applet memory, introduced in firmware 5.0.0
constexpr u64 secure_applet_memory_size{0x400000};
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
secure_applet_memory_size));
}
void InitializePreemption(KernelCore& kernel) {
@@ -302,8 +313,11 @@ struct KernelCore::Impl {
// Allocate slab heaps
user_slab_heap_pages = std::make_unique<Memory::SlabHeap<Memory::Page>>();
constexpr u64 user_slab_heap_size{0x1ef000};
// Reserve slab heaps
ASSERT(
system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
// Initialize slab heaps
constexpr u64 user_slab_heap_size{0x3de000};
user_slab_heap_pages->Initialize(
system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
user_slab_heap_size);

View File

@@ -8,9 +8,9 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/memory/page_linked_list.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Memory {
@@ -95,7 +95,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
// Choose a heap based on our page size request
const s32 heap_index{PageHeap::GetBlockIndex(num_pages)};
if (heap_index < 0) {
return ERR_OUT_OF_MEMORY;
return ResultOutOfMemory;
}
// TODO (bunnei): Support multiple managers
@@ -140,7 +140,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
// Only succeed if we allocated as many pages as we wanted
if (num_pages) {
return ERR_OUT_OF_MEMORY;
return ResultOutOfMemory;
}
// We succeeded!

View File

@@ -6,8 +6,7 @@
#include "common/assert.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/address_space_info.h"
#include "core/hle/kernel/memory/memory_block.h"
@@ -16,6 +15,7 @@
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
#include "core/memory.h"
namespace Kernel::Memory {
@@ -141,7 +141,7 @@ ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_t
(alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
if (alloc_size < needed_size) {
UNREACHABLE();
return ERR_OUT_OF_MEMORY;
return ResultOutOfMemory;
}
const std::size_t remaining_size{alloc_size - needed_size};
@@ -277,11 +277,11 @@ ResultCode PageTable::MapProcessCode(VAddr addr, std::size_t num_pages, MemorySt
const u64 size{num_pages * PageSize};
if (!CanContain(addr, size, state)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (IsRegionMapped(addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
PageLinkedList page_linked_list;
@@ -307,7 +307,7 @@ ResultCode PageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::
MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
if (IsRegionMapped(dst_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
PageLinkedList page_linked_list;
@@ -409,27 +409,25 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
return RESULT_SUCCESS;
}
auto process{system.Kernel().CurrentProcess()};
const std::size_t remaining_size{size - mapped_size};
const std::size_t remaining_pages{remaining_size / PageSize};
if (process->GetResourceLimit() &&
!process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, remaining_size)) {
return ERR_RESOURCE_LIMIT_EXCEEDED;
// Reserve the memory from the process resource limit.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
remaining_size);
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
return ResultResourceLimitedExceeded;
}
PageLinkedList page_linked_list;
{
auto block_guard = detail::ScopeExit([&] {
system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool);
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, remaining_size);
});
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
memory_pool));
CASCADE_CODE(
system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, memory_pool));
block_guard.Cancel();
}
// We succeeded, so commit the memory reservation.
memory_reservation.Commit();
MapPhysicalMemory(page_linked_list, addr, end_addr);
@@ -454,12 +452,12 @@ ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) {
if (info.state == MemoryState::Normal) {
if (info.attribute != MemoryAttribute::None) {
result = ERR_INVALID_ADDRESS_STATE;
result = ResultInvalidCurrentMemory;
return;
}
mapped_size += GetSizeInRange(info, addr, end_addr);
} else if (info.state != MemoryState::Free) {
result = ERR_INVALID_ADDRESS_STATE;
result = ResultInvalidCurrentMemory;
}
});
@@ -526,7 +524,7 @@ ResultCode PageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
MemoryAttribute::Mask, MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
if (IsRegionMapped(dst_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
PageLinkedList page_linked_list;
@@ -577,7 +575,7 @@ ResultCode PageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
AddRegionToPages(dst_addr, num_pages, dst_pages);
if (!dst_pages.IsEqual(src_pages)) {
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
{
@@ -626,11 +624,11 @@ ResultCode PageTable::MapPages(VAddr addr, PageLinkedList& page_linked_list, Mem
const std::size_t size{num_pages * PageSize};
if (!CanContain(addr, size, state)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (IsRegionMapped(addr, num_pages * PageSize)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
CASCADE_CODE(MapPages(addr, page_linked_list, perm));
@@ -768,7 +766,7 @@ ResultCode PageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
if (size > heap_region_end - heap_region_start) {
return ERR_OUT_OF_MEMORY;
return ResultOutOfMemory;
}
const u64 previous_heap_size{GetHeapSize()};
@@ -781,10 +779,14 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
const u64 delta{size - previous_heap_size};
auto process{system.Kernel().CurrentProcess()};
if (process->GetResourceLimit() && delta != 0 &&
!process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, delta)) {
return ERR_RESOURCE_LIMIT_EXCEEDED;
// Reserve memory for the heap extension.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
delta);
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
return ResultResourceLimitedExceeded;
}
PageLinkedList page_linked_list;
@@ -794,12 +796,15 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
if (IsRegionMapped(current_heap_addr, delta)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
CASCADE_CODE(
Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
// Succeeded in allocation, commit the resource reservation
memory_reservation.Commit();
block_manager->Update(current_heap_addr, num_pages, MemoryState::Normal,
MemoryPermission::ReadAndWrite);
@@ -816,17 +821,17 @@ ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, s
std::lock_guard lock{page_table_lock};
if (!CanContain(region_start, region_num_pages * PageSize, state)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (region_num_pages <= needed_num_pages) {
return ERR_OUT_OF_MEMORY;
return ResultOutOfMemory;
}
const VAddr addr{
AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
if (!addr) {
return ERR_OUT_OF_MEMORY;
return ResultOutOfMemory;
}
if (is_map_only) {
@@ -1105,13 +1110,13 @@ constexpr ResultCode PageTable::CheckMemoryState(const MemoryInfo& info, MemoryS
MemoryAttribute attr) const {
// Validate the states match expectation
if ((info.state & state_mask) != state) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if ((info.perm & perm_mask) != perm) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if ((info.attribute & attr_mask) != attr) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
return RESULT_SUCCESS;
@@ -1138,14 +1143,14 @@ ResultCode PageTable::CheckMemoryState(MemoryState* out_state, MemoryPermission*
while (true) {
// Validate the current block
if (!(info.state == first_state)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!(info.perm == first_perm)) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!((info.attribute | static_cast<MemoryAttribute>(ignore_attr)) ==
(first_attr | static_cast<MemoryAttribute>(ignore_attr)))) {
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
// Validate against the provided masks

View File

@@ -14,9 +14,9 @@
#include "core/device_memory.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.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_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block_manager.h"
@@ -39,6 +39,7 @@ 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);
@@ -117,6 +118,9 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
std::shared_ptr<Process> process = std::make_shared<Process>(system);
process->name = std::move(name);
// TODO: This is inaccurate
// The process should hold a reference to the kernel-wide resource limit.
process->resource_limit = std::make_shared<KResourceLimit>(kernel, system);
process->status = ProcessStatus::Created;
process->program_id = 0;
@@ -155,6 +159,9 @@ void Process::DecrementThreadCount() {
}
u64 Process::GetTotalPhysicalMemoryAvailable() const {
// TODO: This is expected to always return the application memory pool size after accurately
// reserving kernel resources. The current workaround uses a process-local resource limit of
// application memory pool size, which is inaccurate.
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
main_thread_stack_size};
@@ -248,8 +255,8 @@ ResultCode Process::Reset() {
KScopedSchedulerLock sl{kernel};
// Validate that we're in a state that we can reset.
R_UNLESS(status != ProcessStatus::Exited, Svc::ResultInvalidState);
R_UNLESS(is_signaled, Svc::ResultInvalidState);
R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState);
R_UNLESS(is_signaled, ResultInvalidState);
// Clear signaled.
is_signaled = false;
@@ -264,6 +271,17 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
system_resource_size = metadata.GetSystemResourceSize();
image_size = code_size;
// Set initial resource limits
resource_limit->SetLimitValue(
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
code_size + system_resource_size);
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
code_size + system_resource_size);
return ResultResourceLimitedExceeded;
}
// Initialize proces address space
if (const ResultCode result{
page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000,
@@ -305,24 +323,22 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
UNREACHABLE();
}
// Set initial resource limits
resource_limit->SetLimitValue(
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
resource_limit->SetLimitValue(LimitableResource::Threads, 608);
resource_limit->SetLimitValue(LimitableResource::Events, 700);
resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128);
resource_limit->SetLimitValue(LimitableResource::Sessions, 894);
ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemory, code_size));
// Create TLS region
tls_region_address = CreateTLSRegion();
memory_reservation.Commit();
return handle_table.SetSize(capabilities.GetHandleTableSize());
}
void Process::Run(s32 main_thread_priority, u64 stack_size) {
AllocateMainThreadStack(stack_size);
resource_limit->Reserve(LimitableResource::Threads, 1);
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
@@ -330,8 +346,6 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
ChangeStatus(ProcessStatus::Running);
SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top);
resource_limit->Reserve(LimitableResource::Threads, 1);
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
}
void Process::PrepareForTermination() {
@@ -358,6 +372,11 @@ void Process::PrepareForTermination() {
FreeTLSRegion(tls_region_address);
tls_region_address = 0;
if (resource_limit) {
resource_limit->Release(LimitableResource::PhysicalMemory,
main_thread_stack_size + image_size);
}
ChangeStatus(ProcessStatus::Exited);
}

View File

@@ -6,10 +6,10 @@
#include "common/bit_util.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
@@ -123,13 +123,13 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
// If there's only one, then there's a problem.
if (i >= num_capabilities) {
LOG_ERROR(Kernel, "Invalid combination! i={}", i);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
const auto size_flags = capabilities[i];
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
LOG_ERROR(Kernel, "Invalid capability type! size_flags={}", size_flags);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table);
@@ -159,7 +159,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
return ResultInvalidCapabilityDescriptor;
}
// Bail early on ignorable entries, as one would expect,
@@ -176,7 +176,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
LOG_ERROR(Kernel,
"Attempted to initialize flags that may only be initialized once. set_flags={}",
set_flags);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
set_flags |= set_flag;
@@ -202,7 +202,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
}
LOG_ERROR(Kernel, "Invalid capability type! type={}", type);
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
return ResultInvalidCapabilityDescriptor;
}
void ProcessCapabilities::Clear() {
@@ -225,7 +225,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
if (priority_mask != 0 || core_mask != 0) {
LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
priority_mask, core_mask);
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
return ResultInvalidCapabilityDescriptor;
}
const u32 core_num_min = (flags >> 16) & 0xFF;
@@ -233,7 +233,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
if (core_num_min > core_num_max) {
LOG_ERROR(Kernel, "Core min is greater than core max! core_num_min={}, core_num_max={}",
core_num_min, core_num_max);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
const u32 priority_min = (flags >> 10) & 0x3F;
@@ -242,13 +242,13 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
LOG_ERROR(Kernel,
"Priority min is greater than priority max! priority_min={}, priority_max={}",
core_num_min, priority_max);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
// The switch only has 4 usable cores.
if (core_num_max >= 4) {
LOG_ERROR(Kernel, "Invalid max cores specified! core_num_max={}", core_num_max);
return ERR_INVALID_PROCESSOR_ID;
return ResultInvalidCoreId;
}
const auto make_mask = [](u64 min, u64 max) {
@@ -269,7 +269,7 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
// If we've already set this svc before, bail.
if ((set_svc_bits & svc_bit) != 0) {
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
set_svc_bits |= svc_bit;
@@ -283,7 +283,7 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
if (svc_number >= svc_capabilities.size()) {
LOG_ERROR(Kernel, "Process svc capability is out of range! svc_number={}", svc_number);
return ERR_OUT_OF_RANGE;
return ResultOutOfRange;
}
svc_capabilities[svc_number] = true;
@@ -321,7 +321,7 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
if (interrupt >= interrupt_capabilities.size()) {
LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}",
interrupt);
return ERR_OUT_OF_RANGE;
return ResultOutOfRange;
}
interrupt_capabilities[interrupt] = true;
@@ -334,7 +334,7 @@ ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
const u32 reserved = flags >> 17;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
return ERR_RESERVED_VALUE;
return ResultReservedValue;
}
program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
@@ -354,7 +354,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
LOG_ERROR(Kernel,
"Kernel version is non zero or flags are too small! major_version={}, flags={}",
major_version, flags);
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
return ResultInvalidCapabilityDescriptor;
}
kernel_version = flags;
@@ -365,7 +365,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
const u32 reserved = flags >> 26;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
return ERR_RESERVED_VALUE;
return ResultReservedValue;
}
handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
@@ -376,7 +376,7 @@ ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
const u32 reserved = flags >> 19;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
return ERR_RESERVED_VALUE;
return ResultReservedValue;
}
is_debuggable = (flags & 0x20000) != 0;

View File

@@ -5,11 +5,11 @@
#include <tuple>
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
if (pending_sessions.empty()) {
return ERR_NOT_FOUND;
return ResultNotFound;
}
auto session = std::move(pending_sessions.back());

View File

@@ -4,15 +4,23 @@
#include "common/assert.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
namespace Kernel {
Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {}
Session::~Session() = default;
Session::~Session() {
// Release reserved resource when the Session pair was created.
kernel.GetSystemResourceLimit()->Release(LimitableResource::Sessions, 1);
}
Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.GetSystemResourceLimit(),
LimitableResource::Sessions);
ASSERT(session_reservation.Succeeded());
auto session{std::make_shared<Session>(kernel)};
auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()};
auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()};
@@ -21,6 +29,7 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
session->client = client_session;
session->server = server_session;
session_reservation.Commit();
return std::make_pair(std::move(client_session), std::move(server_session));
}

View File

@@ -4,6 +4,7 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/shared_memory.h"
@@ -13,7 +14,9 @@ namespace Kernel {
SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
: Object{kernel}, device_memory{device_memory} {}
SharedMemory::~SharedMemory() = default;
SharedMemory::~SharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
}
std::shared_ptr<SharedMemory> SharedMemory::Create(
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
@@ -21,6 +24,11 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
Memory::MemoryPermission 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)};
@@ -32,6 +40,7 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
shared_memory->size = size;
shared_memory->name = name;
memory_reservation.Commit();
return shared_memory;
}

View File

@@ -23,7 +23,6 @@
#include "core/cpu_manager.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_condition_variable.h"
@@ -31,6 +30,7 @@
#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_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
@@ -71,49 +71,49 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
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 ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(src_addr)) {
LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is 0");
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!IsValidAddressRange(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
dst_addr, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!IsValidAddressRange(src_addr, size)) {
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!manager.IsInsideAddressSpace(src_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (manager.IsOutsideStackRegion(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
if (manager.IsInsideHeapRegion(dst_addr, size)) {
@@ -121,7 +121,7 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
"Destination does not fit within the heap region, addr=0x{:016X}, "
"size=0x{:016X}",
dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
if (manager.IsInsideAliasRegion(dst_addr, size)) {
@@ -129,7 +129,7 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
"Destination does not fit within the map region, addr=0x{:016X}, "
"size=0x{:016X}",
dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
return RESULT_SUCCESS;
@@ -138,6 +138,7 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
enum class ResourceLimitValueType {
CurrentValue,
LimitValue,
PeakValue,
};
ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
@@ -146,7 +147,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_
const auto type = static_cast<LimitableResource>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
return ERR_INVALID_ENUM_VALUE;
return ResultInvalidEnumValue;
}
const auto* const current_process = system.Kernel().CurrentProcess();
@@ -157,14 +158,20 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_
if (!resource_limit_object) {
LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
resource_limit);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
if (value_type == ResourceLimitValueType::CurrentValue) {
switch (value_type) {
case ResourceLimitValueType::CurrentValue:
return MakeResult(resource_limit_object->GetCurrentValue(type));
case ResourceLimitValueType::LimitValue:
return MakeResult(resource_limit_object->GetLimitValue(type));
case ResourceLimitValueType::PeakValue:
return MakeResult(resource_limit_object->GetPeakValue(type));
default:
LOG_ERROR(Kernel_SVC, "Invalid resource value_type: '{}'", value_type);
return ResultInvalidEnumValue;
}
return MakeResult(resource_limit_object->GetLimitValue(type));
}
} // Anonymous namespace
@@ -177,12 +184,12 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s
if ((heap_size % 0x200000) != 0) {
LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
heap_size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (heap_size >= 0x200000000) {
LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
@@ -208,19 +215,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
if (!Common::Is4KBAligned(address)) {
LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
size);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!IsValidAddressRange(address, size)) {
LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
address, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)};
@@ -229,7 +236,7 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
LOG_ERROR(Kernel_SVC,
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
attribute, mask);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
@@ -293,7 +300,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
LOG_ERROR(Kernel_SVC,
"Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
port_name_address);
return ERR_NOT_FOUND;
return ResultNotFound;
}
static constexpr std::size_t PortNameMaxLength = 11;
@@ -302,7 +309,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
if (port_name.size() > PortNameMaxLength) {
LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
port_name.size());
return ERR_OUT_OF_RANGE;
return ResultOutOfRange;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -311,11 +318,9 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
const auto it = kernel.FindNamedPort(port_name);
if (!kernel.IsValidNamedPort(it)) {
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
return ERR_NOT_FOUND;
return ResultNotFound;
}
ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(LimitableResource::Sessions, 1));
auto client_port = it->second;
std::shared_ptr<ClientSession> client_session;
@@ -340,7 +345,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
std::shared_ptr<ClientSession> session = handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
@@ -405,7 +410,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
const Process* const owner_process = thread->GetOwnerProcess();
if (!owner_process) {
LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
*process_id = owner_process->GetProcessID();
@@ -415,7 +420,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
// NOTE: This should also handle debug objects before returning.
LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high,
@@ -438,7 +443,7 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha
LOG_ERROR(Kernel_SVC,
"Handle address is not a valid virtual address, handle_address=0x{:016X}",
handles_address);
return ERR_INVALID_POINTER;
return ResultInvalidPointer;
}
static constexpr u64 MaxHandles = 0x40;
@@ -446,7 +451,7 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha
if (handle_count > MaxHandles) {
LOG_ERROR(Kernel_SVC, "Handle count specified is too large, expected {} but got {}",
MaxHandles, handle_count);
return ERR_OUT_OF_RANGE;
return ResultOutOfRange;
}
auto& kernel = system.Kernel();
@@ -459,7 +464,7 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha
if (object == nullptr) {
LOG_ERROR(Kernel_SVC, "Object is a nullptr");
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
objects[i] = object.get();
@@ -481,6 +486,7 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
return ResultInvalidHandle;
@@ -525,6 +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)) {
LOG_ERROR(Kernel_SVC,
"Attempting to arbitrate an unlock on a kernel address (address={:08X})",
@@ -735,7 +742,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (info_sub_id != 0) {
LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
info_sub_id);
return ERR_INVALID_ENUM_VALUE;
return ResultInvalidEnumValue;
}
const auto& current_process_handle_table =
@@ -744,7 +751,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (!process) {
LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
info_id, info_sub_id, handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
switch (info_id_type) {
@@ -826,7 +833,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
}
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
return ERR_INVALID_ENUM_VALUE;
return ResultInvalidEnumValue;
}
case GetInfoType::IsCurrentProcessBeingDebugged:
@@ -836,13 +843,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
case GetInfoType::RegisterResourceLimit: {
if (handle != 0) {
LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
if (info_sub_id != 0) {
LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
info_sub_id);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
Process* const current_process = system.Kernel().CurrentProcess();
@@ -867,13 +874,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (handle != 0) {
LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
Process::RANDOM_ENTROPY_SIZE, info_sub_id);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
*result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
@@ -890,7 +897,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
info_sub_id);
return ERR_INVALID_COMBINATION;
return ResultInvalidCombination;
}
const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<KThread>(
@@ -898,7 +905,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
static_cast<Handle>(handle));
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
const auto& core_timing = system.CoreTiming();
@@ -922,7 +929,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
default:
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
return ERR_INVALID_ENUM_VALUE;
return ResultInvalidEnumValue;
}
}
@@ -945,22 +952,22 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is zero");
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!(addr < addr + size)) {
LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
Process* const current_process{system.Kernel().CurrentProcess()};
@@ -968,21 +975,21 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
if (current_process->GetSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
return ERR_INVALID_STATE;
return ResultInvalidState;
}
if (!page_table.IsInsideAddressSpace(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
if (page_table.IsOutsideAliasRegion(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
return page_table.MapPhysicalMemory(addr, size);
@@ -999,22 +1006,22 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is zero");
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!(addr < addr + size)) {
LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
Process* const current_process{system.Kernel().CurrentProcess()};
@@ -1022,21 +1029,21 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
if (current_process->GetSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
return ERR_INVALID_STATE;
return ResultInvalidState;
}
if (!page_table.IsInsideAddressSpace(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
if (page_table.IsOutsideAliasRegion(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
return page_table.UnmapPhysicalMemory(addr, size);
@@ -1206,23 +1213,23 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is 0");
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!IsValidAddressRange(addr, size)) {
LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
addr, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
const auto permission_type = static_cast<Memory::MemoryPermission>(permissions);
@@ -1230,7 +1237,7 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
Memory::MemoryPermission::ReadAndWrite) {
LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
permissions);
return ERR_INVALID_MEMORY_PERMISSIONS;
return ResultInvalidMemoryPermissions;
}
auto* const current_process{system.Kernel().CurrentProcess()};
@@ -1241,7 +1248,7 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
"Addr does not fit within the valid region, addr=0x{:016X}, "
"size=0x{:016X}",
addr, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
if (page_table.IsInsideHeapRegion(addr, size)) {
@@ -1249,7 +1256,7 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
"Addr does not fit within the heap region, addr=0x{:016X}, "
"size=0x{:016X}",
addr, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
if (page_table.IsInsideAliasRegion(addr, size)) {
@@ -1257,14 +1264,14 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
"Address does not fit within the map region, addr=0x{:016X}, "
"size=0x{:016X}",
addr, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
auto shared_memory{current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle)};
if (!shared_memory) {
LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
shared_memory_handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
return shared_memory->Map(*current_process, addr, size, permission_type);
@@ -1285,7 +1292,7 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
if (!process) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
process_handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
auto& memory{system.Memory()};
@@ -1332,18 +1339,18 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
if (!Common::Is4KBAligned(src_address)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
src_address);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(dst_address)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
dst_address);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!IsValidAddressRange(dst_address, size)) {
@@ -1351,7 +1358,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!IsValidAddressRange(src_address, size)) {
@@ -1359,7 +1366,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
@@ -1367,7 +1374,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
if (!process) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
process_handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
auto& page_table = process->PageTable();
@@ -1376,7 +1383,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!page_table.IsInsideASLRRegion(dst_address, size)) {
@@ -1384,7 +1391,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
return page_table.MapProcessCodeMemory(dst_address, src_address, size);
@@ -1400,18 +1407,18 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
if (!Common::Is4KBAligned(dst_address)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
dst_address);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(src_address)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
src_address);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (size == 0 || Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
return ERR_INVALID_SIZE;
return ResultInvalidSize;
}
if (!IsValidAddressRange(dst_address, size)) {
@@ -1419,7 +1426,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!IsValidAddressRange(src_address, size)) {
@@ -1427,7 +1434,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
@@ -1435,7 +1442,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
if (!process) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
process_handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
auto& page_table = process->PageTable();
@@ -1444,7 +1451,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
if (!page_table.IsInsideASLRRegion(dst_address, size)) {
@@ -1452,7 +1459,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
return ERR_INVALID_MEMORY_RANGE;
return ResultInvalidMemoryRange;
}
return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
@@ -1515,8 +1522,13 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
return ResultInvalidPriority;
}
ASSERT(process.GetResourceLimit()->Reserve(
LimitableResource::Threads, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000));
KScopedResourceReservation thread_reservation(
kernel.CurrentProcess(), LimitableResource::Threads, 1,
system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
if (!thread_reservation.Succeeded()) {
LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
return ResultResourceLimitedExceeded;
}
std::shared_ptr<KThread> thread;
{
@@ -1536,6 +1548,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
// Set the thread name for debugging purposes.
thread->SetName(
fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
thread_reservation.Commit();
return RESULT_SUCCESS;
}
@@ -1844,7 +1857,7 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) {
LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
return Svc::ResultInvalidHandle;
return ResultInvalidHandle;
}
static ResultCode ResetSignal32(Core::System& system, Handle handle) {
@@ -1860,18 +1873,18 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(size) || size == 0) {
LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
return ERR_INVALID_ADDRESS;
return ResultInvalidAddress;
}
if (!IsValidAddressRange(addr, size)) {
LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
addr, size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
const auto perms{static_cast<Memory::MemoryPermission>(permissions)};
@@ -1879,10 +1892,17 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
perms == Memory::MemoryPermission::Write) {
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
permissions);
return ERR_INVALID_MEMORY_PERMISSIONS;
return ResultInvalidMemoryPermissions;
}
auto& kernel = system.Kernel();
// Reserve a new transfer memory from the process resource limit.
KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
LimitableResource::TransferMemory);
if (!trmem_reservation.Succeeded()) {
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);
if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
@@ -1894,6 +1914,7 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
if (result.Failed()) {
return result.Code();
}
trmem_reservation.Commit();
*handle = *result;
return RESULT_SUCCESS;
@@ -1989,7 +2010,6 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
LOG_ERROR(Kernel_SVC, "Unable to successfully set core mask (result={})", set_result.raw);
return set_result;
}
return RESULT_SUCCESS;
}
@@ -2002,8 +2022,17 @@ static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle
static ResultCode SignalEvent(Core::System& system, Handle event_handle) {
LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
auto& kernel = system.Kernel();
// Get the current handle table.
const HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
// Reserve a new event from the process resource limit.
KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
LimitableResource::Events);
if (!event_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve a new event");
return ResultResourceLimitedExceeded;
}
// Get the writable event.
auto writable_event = handle_table.Get<KWritableEvent>(event_handle);
@@ -2012,6 +2041,9 @@ static ResultCode SignalEvent(Core::System& system, Handle event_handle) {
return ResultInvalidHandle;
}
// Commit the successfuly reservation.
event_reservation.Commit();
return writable_event->Signal();
}
@@ -2043,7 +2075,7 @@ static ResultCode ClearEvent(Core::System& system, Handle event_handle) {
LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
return Svc::ResultInvalidHandle;
return ResultInvalidHandle;
}
static ResultCode ClearEvent32(Core::System& system, Handle event_handle) {
@@ -2106,13 +2138,13 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
if (!process) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
process_handle);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
const auto info_type = static_cast<InfoType>(type);
if (info_type != InfoType::Status) {
LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
return ERR_INVALID_ENUM_VALUE;
return ResultInvalidEnumValue;
}
*out = static_cast<u64>(process->GetStatus());
@@ -2174,7 +2206,7 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
const auto type = static_cast<LimitableResource>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
return ERR_INVALID_ENUM_VALUE;
return ResultInvalidEnumValue;
}
auto* const current_process = system.Kernel().CurrentProcess();
@@ -2185,16 +2217,16 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
if (!resource_limit_object) {
LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
resource_limit);
return ERR_INVALID_HANDLE;
return ResultInvalidHandle;
}
const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value));
if (set_result.IsError()) {
LOG_ERROR(
Kernel_SVC,
"Attempted to lower resource limit ({}) for category '{}' below its current value ({})",
resource_limit_object->GetLimitValue(type), resource_type,
resource_limit_object->GetCurrentValue(type));
LOG_ERROR(Kernel_SVC,
"Attempted to lower resource limit ({}) for category '{}' below its current "
"value ({})",
resource_limit_object->GetLimitValue(type), resource_type,
resource_limit_object->GetCurrentValue(type));
return set_result;
}
@@ -2211,7 +2243,7 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
LOG_ERROR(Kernel_SVC,
"Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
out_process_ids_size);
return ERR_OUT_OF_RANGE;
return ResultOutOfRange;
}
const auto& kernel = system.Kernel();
@@ -2221,7 +2253,7 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
out_process_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
out_process_ids, out_process_ids + total_copy_size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
auto& memory = system.Memory();
@@ -2250,7 +2282,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
if ((out_thread_ids_size & 0xF0000000) != 0) {
LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
out_thread_ids_size);
return ERR_OUT_OF_RANGE;
return ResultOutOfRange;
}
const auto* const current_process = system.Kernel().CurrentProcess();
@@ -2260,7 +2292,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
!current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
out_thread_ids, out_thread_ids + total_copy_size);
return ERR_INVALID_ADDRESS_STATE;
return ResultInvalidCurrentMemory;
}
auto& memory = system.Memory();

View File

@@ -1,4 +1,4 @@
// Copyright 2020 yuzu emulator team
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -6,21 +6,36 @@
#include "core/hle/result.h"
namespace Kernel::Svc {
namespace Kernel {
// Confirmed Switch kernel error codes
constexpr ResultCode ResultMaxConnectionsReached{ErrorModule::Kernel, 7};
constexpr ResultCode ResultInvalidCapabilityDescriptor{ErrorModule::Kernel, 14};
constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
constexpr ResultCode ResultInvalidSize{ErrorModule::Kernel, 101};
constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103};
constexpr ResultCode ResultOutOfMemory{ErrorModule::Kernel, 104};
constexpr ResultCode ResultHandleTableFull{ErrorModule::Kernel, 105};
constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
constexpr ResultCode ResultInvalidMemoryPermissions{ErrorModule::Kernel, 108};
constexpr ResultCode ResultInvalidMemoryRange{ErrorModule::Kernel, 110};
constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112};
constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113};
constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
constexpr ResultCode ResultInvalidPointer{ErrorModule::Kernel, 115};
constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116};
constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
constexpr ResultCode ResultOutOfRange{ErrorModule::Kernel, 119};
constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
constexpr ResultCode ResultNotFound{ErrorModule::Kernel, 121};
constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
constexpr ResultCode ResultSessionClosedByRemote{ErrorModule::Kernel, 123};
constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
constexpr ResultCode ResultReservedValue{ErrorModule::Kernel, 126};
constexpr ResultCode ResultResourceLimitedExceeded{ErrorModule::Kernel, 132};
} // namespace Kernel::Svc
} // namespace Kernel

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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"
@@ -17,6 +18,7 @@ TransferMemory::TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory)
TransferMemory::~TransferMemory() {
// Release memory region when transfer memory is destroyed
Reset();
owner_process->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1);
}
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel,

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);

View File

@@ -211,7 +211,8 @@ void Controller::Execute() {
case ControllerSupportMode::ShowControllerFirmwareUpdate:
UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
controller_private_arg.mode);
[[fallthrough]];
ConfigurationComplete();
break;
default: {
ConfigurationComplete();
break;

View File

@@ -121,6 +121,10 @@ void SoftwareKeyboard::ExecuteInteractive() {
std::memcpy(&request, data.data(), sizeof(Request));
switch (request) {
case Request::Finalize:
complete = true;
broker.SignalStateChanged();
break;
case Request::Calc: {
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{1}));
broker.SignalStateChanged();

View File

@@ -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:

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;
};

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

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(RESULT_SUCCESS);
}
private:
enum class State {
None,
Initialized,
AccessPointOpened,
AccessPointCreated,
StationOpened,
StationConnected,
Error,
};
bool is_initialized{};
};
class LDNS final : public ServiceFramework<LDNS> {

View File

@@ -11,10 +11,10 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/service.h"
#include "core/loader/nro.h"
@@ -330,7 +330,7 @@ public:
const VAddr addr{GetRandomMapRegion(page_table, size)};
const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)};
if (result == Kernel::ERR_INVALID_ADDRESS_STATE) {
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
@@ -361,7 +361,7 @@ public:
const ResultCode result{
page_table.MapProcessCodeMemory(addr + nro_size, bss_addr, bss_size)};
if (result == Kernel::ERR_INVALID_ADDRESS_STATE) {
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}

View File

@@ -215,7 +215,7 @@ private:
const auto& amiibo = nfp_interface.GetAmiiboBuffer();
const TagInfo tag_info{
.uuid = amiibo.uuid,
.uuid_length = static_cast<u8>(tag_info.uuid.size()),
.uuid_length = static_cast<u8>(amiibo.uuid.size()),
.padding_1 = {},
.protocol = 1, // TODO(ogniK): Figure out actual values
.tag_type = 2,

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -453,7 +453,8 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
return {-1, Errno::MFILE};
}
FileDescriptor& descriptor = file_descriptors[fd].emplace();
file_descriptors[fd] = FileDescriptor{};
FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={}", fd);
@@ -548,7 +549,8 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
return {-1, Translate(bsd_errno)};
}
FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace();
file_descriptors[new_fd] = FileDescriptor{};
FileDescriptor& new_descriptor = *file_descriptors[new_fd];
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;

View File

@@ -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);
}

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,

View File

@@ -181,12 +181,13 @@ struct Values {
std::string motion_device;
std::string udp_input_servers;
bool emulate_analog_keyboard;
bool mouse_panning;
float mouse_panning_sensitivity;
bool mouse_enabled;
std::string mouse_device;
MouseButtonsRaw mouse_buttons;
bool emulate_analog_keyboard;
bool keyboard_enabled;
KeyboardKeysRaw keyboard_keys;
KeyboardModsRaw keyboard_mods;

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "core/settings.h"
#include "input_common/mouse/mouse_input.h"
namespace MouseInput {
@@ -32,10 +33,18 @@ void Mouse::UpdateThread() {
info.motion.UpdateOrientation(update_time * 1000);
info.tilt_speed = 0;
info.data.motion = info.motion.GetMotion();
if (Settings::values.mouse_panning) {
info.last_mouse_change *= 0.96f;
info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x),
static_cast<int>(16 * -info.last_mouse_change.y)};
}
}
if (configuring) {
UpdateYuzuSettings();
}
if (mouse_panning_timout++ > 20) {
StopPanning();
}
std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
}
}
@@ -65,8 +74,45 @@ void Mouse::PressButton(int x, int y, int button_) {
mouse_info[button_index].data.pressed = true;
}
void Mouse::MouseMove(int x, int y) {
void Mouse::StopPanning() {
for (MouseInfo& info : mouse_info) {
if (Settings::values.mouse_panning) {
info.data.axis = {};
info.tilt_speed = 0;
info.last_mouse_change = {};
}
}
}
void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
for (MouseInfo& info : mouse_info) {
if (Settings::values.mouse_panning) {
auto mouse_change =
(Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
mouse_panning_timout = 0;
if (mouse_change.y == 0 && mouse_change.x == 0) {
continue;
}
const auto mouse_change_length = mouse_change.Length();
if (mouse_change_length < 3.0f) {
mouse_change /= mouse_change_length / 3.0f;
}
info.last_mouse_change = (info.last_mouse_change * 0.91f) + (mouse_change * 0.09f);
const auto last_mouse_change_length = info.last_mouse_change.Length();
if (last_mouse_change_length > 8.0f) {
info.last_mouse_change /= last_mouse_change_length / 8.0f;
} else if (last_mouse_change_length < 1.0f) {
info.last_mouse_change = mouse_change / mouse_change.Length();
}
info.tilt_direction = info.last_mouse_change;
info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
continue;
}
if (info.data.pressed) {
const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position;

View File

@@ -57,8 +57,10 @@ public:
* Signals that mouse has moved.
* @param x the x-coordinate of the cursor
* @param y the y-coordinate of the cursor
* @param center_x the x-coordinate of the middle of the screen
* @param center_y the y-coordinate of the middle of the screen
*/
void MouseMove(int x, int y);
void MouseMove(int x, int y, int center_x, int center_y);
/**
* Signals that a motion sensor tilt has ended.
@@ -74,11 +76,13 @@ public:
private:
void UpdateThread();
void UpdateYuzuSettings();
void StopPanning();
struct MouseInfo {
InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
Common::Vec2<int> mouse_origin;
Common::Vec2<int> last_mouse_position;
Common::Vec2<float> last_mouse_change;
bool is_tilting = false;
float sensitivity{0.120f};
@@ -94,5 +98,6 @@ private:
Common::SPSCQueue<MouseStatus> mouse_queue;
bool configuring{false};
bool update_thread_running{true};
int mouse_panning_timout{};
};
} // namespace MouseInput

View File

@@ -6,6 +6,7 @@
#include <utility>
#include "common/threadsafe_queue.h"
#include "core/settings.h"
#include "input_common/mouse/mouse_input.h"
#include "input_common/mouse/mouse_poller.h"
@@ -71,7 +72,7 @@ public:
std::lock_guard lock{mutex};
const auto axis_value =
static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
return axis_value / (100.0f * range);
return axis_value * Settings::values.mouse_panning_sensitivity / (100.0f * range);
}
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {

View File

@@ -340,6 +340,7 @@ enum class ControllerType {
LeftJoycon,
RightJoycon,
Handheld,
GameCube,
};
struct PlayerInput {

View File

@@ -1,5 +1,6 @@
add_executable(tests
common/bit_field.cpp
common/cityhash.cpp
common/fibers.cpp
common/param_package.cpp
common/ring_buffer.cpp

View File

@@ -0,0 +1,22 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <catch2/catch.hpp>
#include "common/cityhash.h"
constexpr char msg[] = "The blue frogs are singing under the crimson sky.\n"
"It is time to run, Robert.";
using namespace Common;
TEST_CASE("CityHash", "[common]") {
// These test results were built against a known good version.
REQUIRE(CityHash64(msg, sizeof(msg)) == 0x92d5c2e9cbfbbc01);
REQUIRE(CityHash64WithSeed(msg, sizeof(msg), 0xdead) == 0xbfbe93f21a2820dd);
REQUIRE(CityHash64WithSeeds(msg, sizeof(msg), 0xbeef, 0xcafe) == 0xb343317955fc8a06);
REQUIRE(CityHash128(msg, sizeof(msg)) == u128{0x98e60d0423747eaa, 0xd8694c5b6fcaede9});
REQUIRE(CityHash128WithSeed(msg, sizeof(msg), {0xdead, 0xbeef}) ==
u128{0xf0307dba81199ebe, 0xd77764e0c4a9eb74});
}

View File

@@ -471,3 +471,79 @@ TEST_CASE("BufferBase: Unaligned page region query") {
REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1000));
REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1));
}
TEST_CASE("BufferBase: Cached write") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.CachedCpuWrite(c + PAGE, PAGE);
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
buffer.FlushCachedWrites();
REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
buffer.MarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == 0);
}
TEST_CASE("BufferBase: Multiple cached write") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.CachedCpuWrite(c + PAGE, PAGE);
buffer.CachedCpuWrite(c + PAGE * 3, PAGE);
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 3, PAGE));
buffer.FlushCachedWrites();
REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 3, PAGE));
buffer.MarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == 0);
}
TEST_CASE("BufferBase: Cached write unmarked") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.CachedCpuWrite(c + PAGE, PAGE);
buffer.UnmarkRegionAsCpuModified(c + PAGE, PAGE);
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
buffer.FlushCachedWrites();
REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
buffer.MarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == 0);
}
TEST_CASE("BufferBase: Cached write iterated") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.CachedCpuWrite(c + PAGE, PAGE);
int num = 0;
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 0);
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
buffer.FlushCachedWrites();
REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
buffer.MarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == 0);
}
TEST_CASE("BufferBase: Cached write downloads") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == 64);
buffer.CachedCpuWrite(c + PAGE, PAGE);
REQUIRE(rasterizer.Count() == 63);
buffer.MarkRegionAsGpuModified(c + PAGE, PAGE);
int num = 0;
buffer.ForEachDownloadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 0);
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
buffer.FlushCachedWrites();
REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
buffer.MarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == 0);
}

View File

@@ -2,10 +2,8 @@ add_subdirectory(host_shaders)
add_library(video_core STATIC
buffer_cache/buffer_base.h
buffer_cache/buffer_block.h
buffer_cache/buffer_cache.cpp
buffer_cache/buffer_cache.h
buffer_cache/map_interval.cpp
buffer_cache/map_interval.h
cdma_pusher.cpp
cdma_pusher.h
command_classes/codecs/codec.cpp
@@ -152,8 +150,6 @@ add_library(video_core STATIC
renderer_vulkan/vk_staging_buffer_pool.h
renderer_vulkan/vk_state_tracker.cpp
renderer_vulkan/vk_state_tracker.h
renderer_vulkan/vk_stream_buffer.cpp
renderer_vulkan/vk_stream_buffer.h
renderer_vulkan/vk_swapchain.cpp
renderer_vulkan/vk_swapchain.h
renderer_vulkan/vk_texture_cache.cpp

View File

@@ -19,6 +19,7 @@ namespace VideoCommon {
enum class BufferFlagBits {
Picked = 1 << 0,
CachedWrites = 1 << 1,
};
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
@@ -40,7 +41,7 @@ class BufferBase {
static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
/// Vector tracking modified pages tightly packed with small vector optimization
union WrittenWords {
union WordsArray {
/// Returns the pointer to the words state
[[nodiscard]] const u64* Pointer(bool is_short) const noexcept {
return is_short ? &stack : heap;
@@ -55,49 +56,59 @@ class BufferBase {
u64* heap; ///< Not-small buffers pointer to the storage
};
struct GpuCpuWords {
explicit GpuCpuWords() = default;
explicit GpuCpuWords(u64 size_bytes_) : size_bytes{size_bytes_} {
struct Words {
explicit Words() = default;
explicit Words(u64 size_bytes_) : size_bytes{size_bytes_} {
if (IsShort()) {
cpu.stack = ~u64{0};
gpu.stack = 0;
cached_cpu.stack = 0;
untracked.stack = ~u64{0};
} else {
// Share allocation between CPU and GPU pages and set their default values
const size_t num_words = NumWords();
u64* const alloc = new u64[num_words * 2];
u64* const alloc = new u64[num_words * 4];
cpu.heap = alloc;
gpu.heap = alloc + num_words;
cached_cpu.heap = alloc + num_words * 2;
untracked.heap = alloc + num_words * 3;
std::fill_n(cpu.heap, num_words, ~u64{0});
std::fill_n(gpu.heap, num_words, 0);
std::fill_n(cached_cpu.heap, num_words, 0);
std::fill_n(untracked.heap, num_words, ~u64{0});
}
// Clean up tailing bits
const u64 last_local_page =
Common::DivCeil(size_bytes % BYTES_PER_WORD, BYTES_PER_PAGE);
const u64 last_word_size = size_bytes % BYTES_PER_WORD;
const u64 last_local_page = Common::DivCeil(last_word_size, BYTES_PER_PAGE);
const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD;
u64& last_word = cpu.Pointer(IsShort())[NumWords() - 1];
last_word = (last_word << shift) >> shift;
const u64 last_word = (~u64{0} << shift) >> shift;
cpu.Pointer(IsShort())[NumWords() - 1] = last_word;
untracked.Pointer(IsShort())[NumWords() - 1] = last_word;
}
~GpuCpuWords() {
~Words() {
Release();
}
GpuCpuWords& operator=(GpuCpuWords&& rhs) noexcept {
Words& operator=(Words&& rhs) noexcept {
Release();
size_bytes = rhs.size_bytes;
cpu = rhs.cpu;
gpu = rhs.gpu;
cached_cpu = rhs.cached_cpu;
untracked = rhs.untracked;
rhs.cpu.heap = nullptr;
return *this;
}
GpuCpuWords(GpuCpuWords&& rhs) noexcept
: size_bytes{rhs.size_bytes}, cpu{rhs.cpu}, gpu{rhs.gpu} {
Words(Words&& rhs) noexcept
: size_bytes{rhs.size_bytes}, cpu{rhs.cpu}, gpu{rhs.gpu},
cached_cpu{rhs.cached_cpu}, untracked{rhs.untracked} {
rhs.cpu.heap = nullptr;
}
GpuCpuWords& operator=(const GpuCpuWords&) = delete;
GpuCpuWords(const GpuCpuWords&) = delete;
Words& operator=(const Words&) = delete;
Words(const Words&) = delete;
/// Returns true when the buffer fits in the small vector optimization
[[nodiscard]] bool IsShort() const noexcept {
@@ -118,8 +129,17 @@ class BufferBase {
}
u64 size_bytes = 0;
WrittenWords cpu;
WrittenWords gpu;
WordsArray cpu;
WordsArray gpu;
WordsArray cached_cpu;
WordsArray untracked;
};
enum class Type {
CPU,
GPU,
CachedCPU,
Untracked,
};
public:
@@ -132,68 +152,93 @@ public:
BufferBase& operator=(const BufferBase&) = delete;
BufferBase(const BufferBase&) = delete;
BufferBase& operator=(BufferBase&&) = default;
BufferBase(BufferBase&&) = default;
/// Returns the inclusive CPU modified range in a begin end pair
[[nodiscard]] std::pair<u64, u64> ModifiedCpuRegion(VAddr query_cpu_addr,
u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return ModifiedRegion<false>(offset, query_size);
return ModifiedRegion<Type::CPU>(offset, query_size);
}
/// Returns the inclusive GPU modified range in a begin end pair
[[nodiscard]] std::pair<u64, u64> ModifiedGpuRegion(VAddr query_cpu_addr,
u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return ModifiedRegion<true>(offset, query_size);
return ModifiedRegion<Type::GPU>(offset, query_size);
}
/// Returns true if a region has been modified from the CPU
[[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return IsRegionModified<false>(offset, query_size);
return IsRegionModified<Type::CPU>(offset, query_size);
}
/// Returns true if a region has been modified from the GPU
[[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return IsRegionModified<true>(offset, query_size);
return IsRegionModified<Type::GPU>(offset, query_size);
}
/// Mark region as CPU modified, notifying the rasterizer about this change
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) {
ChangeRegionState<true, true>(words.cpu, dirty_cpu_addr, size);
ChangeRegionState<Type::CPU, true>(dirty_cpu_addr, size);
}
/// Unmark region as CPU modified, notifying the rasterizer about this change
void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) {
ChangeRegionState<false, true>(words.cpu, dirty_cpu_addr, size);
ChangeRegionState<Type::CPU, false>(dirty_cpu_addr, size);
}
/// Mark region as modified from the host GPU
void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept {
ChangeRegionState<true, false>(words.gpu, dirty_cpu_addr, size);
ChangeRegionState<Type::GPU, true>(dirty_cpu_addr, size);
}
/// Unmark region as modified from the host GPU
void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept {
ChangeRegionState<false, false>(words.gpu, dirty_cpu_addr, size);
ChangeRegionState<Type::GPU, false>(dirty_cpu_addr, size);
}
/// Mark region as modified from the CPU
/// but don't mark it as modified until FlusHCachedWrites is called.
void CachedCpuWrite(VAddr dirty_cpu_addr, u64 size) {
flags |= BufferFlagBits::CachedWrites;
ChangeRegionState<Type::CachedCPU, true>(dirty_cpu_addr, size);
}
/// Flushes cached CPU writes, and notify the rasterizer about the deltas
void FlushCachedWrites() noexcept {
flags &= ~BufferFlagBits::CachedWrites;
const u64 num_words = NumWords();
const u64* const cached_words = Array<Type::CachedCPU>();
u64* const untracked_words = Array<Type::Untracked>();
u64* const cpu_words = Array<Type::CPU>();
for (u64 word_index = 0; word_index < num_words; ++word_index) {
const u64 cached_bits = cached_words[word_index];
NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits);
untracked_words[word_index] |= cached_bits;
cpu_words[word_index] |= cached_bits;
}
}
/// Call 'func' for each CPU modified range and unmark those pages as CPU modified
template <typename Func>
void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) {
ForEachModifiedRange<false, true>(query_cpu_range, size, func);
ForEachModifiedRange<Type::CPU>(query_cpu_range, size, func);
}
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
template <typename Func>
void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) {
ForEachModifiedRange<true, false>(query_cpu_range, size, func);
ForEachModifiedRange<Type::GPU>(query_cpu_range, size, func);
}
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
template <typename Func>
void ForEachDownloadRange(Func&& func) {
ForEachModifiedRange<true, false>(cpu_addr, SizeBytes(), func);
ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func);
}
/// Mark buffer as picked
@@ -206,6 +251,16 @@ public:
flags &= ~BufferFlagBits::Picked;
}
/// Increases the likeliness of this being a stream buffer
void IncreaseStreamScore(int score) noexcept {
stream_score += score;
}
/// Returns the likeliness of this being a stream buffer
[[nodiscard]] int StreamScore() const noexcept {
return stream_score;
}
/// Returns true when vaddr -> vaddr+size is fully contained in the buffer
[[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept {
return addr >= cpu_addr && addr + size <= cpu_addr + SizeBytes();
@@ -216,6 +271,11 @@ public:
return True(flags & BufferFlagBits::Picked);
}
/// Returns true when the buffer has pending cached writes
[[nodiscard]] bool HasCachedWrites() const noexcept {
return True(flags & BufferFlagBits::CachedWrites);
}
/// Returns the base CPU address of the buffer
[[nodiscard]] VAddr CpuAddr() const noexcept {
return cpu_addr;
@@ -233,26 +293,48 @@ public:
}
private:
template <Type type>
u64* Array() noexcept {
if constexpr (type == Type::CPU) {
return words.cpu.Pointer(IsShort());
} else if constexpr (type == Type::GPU) {
return words.gpu.Pointer(IsShort());
} else if constexpr (type == Type::CachedCPU) {
return words.cached_cpu.Pointer(IsShort());
} else if constexpr (type == Type::Untracked) {
return words.untracked.Pointer(IsShort());
}
}
template <Type type>
const u64* Array() const noexcept {
if constexpr (type == Type::CPU) {
return words.cpu.Pointer(IsShort());
} else if constexpr (type == Type::GPU) {
return words.gpu.Pointer(IsShort());
} else if constexpr (type == Type::CachedCPU) {
return words.cached_cpu.Pointer(IsShort());
} else if constexpr (type == Type::Untracked) {
return words.untracked.Pointer(IsShort());
}
}
/**
* Change the state of a range of pages
*
* @param written_words Pages to be marked or unmarked as modified
* @param dirty_addr Base address to mark or unmark as modified
* @param size Size in bytes to mark or unmark as modified
*
* @tparam enable True when the bits will be set to one, false for zero
* @tparam notify_rasterizer True when the rasterizer has to be notified about the changes
*/
template <bool enable, bool notify_rasterizer>
void ChangeRegionState(WrittenWords& written_words, u64 dirty_addr,
s64 size) noexcept(!notify_rasterizer) {
template <Type type, bool enable>
void ChangeRegionState(u64 dirty_addr, s64 size) noexcept(type == Type::GPU) {
const s64 difference = dirty_addr - cpu_addr;
const u64 offset = std::max<s64>(difference, 0);
size += std::min<s64>(difference, 0);
if (offset >= SizeBytes() || size < 0) {
return;
}
u64* const state_words = written_words.Pointer(IsShort());
u64* const untracked_words = Array<Type::Untracked>();
u64* const state_words = Array<type>();
const u64 offset_end = std::min(offset + size, SizeBytes());
const u64 begin_page_index = offset / BYTES_PER_PAGE;
const u64 begin_word_index = begin_page_index / PAGES_PER_WORD;
@@ -268,13 +350,19 @@ private:
u64 bits = ~u64{0};
bits = (bits >> right_offset) << right_offset;
bits = (bits << left_offset) >> left_offset;
if constexpr (notify_rasterizer) {
NotifyRasterizer<!enable>(word_index, state_words[word_index], bits);
if constexpr (type == Type::CPU || type == Type::CachedCPU) {
NotifyRasterizer<!enable>(word_index, untracked_words[word_index], bits);
}
if constexpr (enable) {
state_words[word_index] |= bits;
if constexpr (type == Type::CPU || type == Type::CachedCPU) {
untracked_words[word_index] |= bits;
}
} else {
state_words[word_index] &= ~bits;
if constexpr (type == Type::CPU || type == Type::CachedCPU) {
untracked_words[word_index] &= ~bits;
}
}
page_index = 0;
++word_index;
@@ -291,7 +379,7 @@ private:
* @tparam add_to_rasterizer True when the rasterizer should start tracking the new pages
*/
template <bool add_to_rasterizer>
void NotifyRasterizer(u64 word_index, u64 current_bits, u64 new_bits) {
void NotifyRasterizer(u64 word_index, u64 current_bits, u64 new_bits) const {
u64 changed_bits = (add_to_rasterizer ? current_bits : ~current_bits) & new_bits;
VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
while (changed_bits != 0) {
@@ -315,21 +403,20 @@ private:
* @param query_cpu_range Base CPU address to loop over
* @param size Size in bytes of the CPU range to loop over
* @param func Function to call for each turned off region
*
* @tparam gpu True for host GPU pages, false for CPU pages
* @tparam notify_rasterizer True when the rasterizer should be notified about state changes
*/
template <bool gpu, bool notify_rasterizer, typename Func>
template <Type type, typename Func>
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) {
static_assert(type != Type::Untracked);
const s64 difference = query_cpu_range - cpu_addr;
const u64 query_begin = std::max<s64>(difference, 0);
size += std::min<s64>(difference, 0);
if (query_begin >= SizeBytes() || size < 0) {
return;
}
const u64* const cpu_words = words.cpu.Pointer(IsShort());
u64* const untracked_words = Array<Type::Untracked>();
u64* const state_words = Array<type>();
const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes());
u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort());
u64* const words_begin = state_words + query_begin / BYTES_PER_WORD;
u64* const words_end = state_words + Common::DivCeil(query_end, BYTES_PER_WORD);
@@ -345,7 +432,8 @@ private:
const u64 word_index_end = std::distance(state_words, last_modified_word);
const unsigned local_page_begin = std::countr_zero(*first_modified_word);
const unsigned local_page_end = PAGES_PER_WORD - std::countl_zero(last_modified_word[-1]);
const unsigned local_page_end =
static_cast<unsigned>(PAGES_PER_WORD) - std::countl_zero(last_modified_word[-1]);
const u64 word_page_begin = word_index_begin * PAGES_PER_WORD;
const u64 word_page_end = (word_index_end - 1) * PAGES_PER_WORD;
const u64 query_page_begin = query_begin / BYTES_PER_PAGE;
@@ -371,11 +459,13 @@ private:
const u64 current_word = state_words[word_index] & bits;
state_words[word_index] &= ~bits;
// Exclude CPU modified pages when visiting GPU pages
const u64 word = current_word & ~(gpu ? cpu_words[word_index] : 0);
if constexpr (notify_rasterizer) {
NotifyRasterizer<true>(word_index, word, ~u64{0});
if constexpr (type == Type::CPU) {
const u64 current_bits = untracked_words[word_index] & bits;
untracked_words[word_index] &= ~bits;
NotifyRasterizer<true>(word_index, current_bits, ~u64{0});
}
// Exclude CPU modified pages when visiting GPU pages
const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0);
u64 page = page_begin;
page_begin = 0;
@@ -416,17 +506,20 @@ private:
* @param offset Offset in bytes from the start of the buffer
* @param size Size in bytes of the region to query for modifications
*/
template <bool gpu>
template <Type type>
[[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
const u64* const cpu_words = words.cpu.Pointer(IsShort());
const u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort());
static_assert(type != Type::Untracked);
const u64* const untracked_words = Array<Type::Untracked>();
const u64* const state_words = Array<type>();
const u64 num_query_words = size / BYTES_PER_WORD + 1;
const u64 word_begin = offset / BYTES_PER_WORD;
const u64 word_end = std::min(word_begin + num_query_words, NumWords());
const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
const u64 word = state_words[word_index] & ~(gpu ? cpu_words[word_index] : 0);
const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
const u64 word = state_words[word_index] & ~off_word;
if (word == 0) {
continue;
}
@@ -445,13 +538,13 @@ private:
*
* @param offset Offset in bytes from the start of the buffer
* @param size Size in bytes of the region to query for modifications
*
* @tparam gpu True to query GPU modified pages, false for CPU pages
*/
template <bool gpu>
template <Type type>
[[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
const u64* const cpu_words = words.cpu.Pointer(IsShort());
const u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort());
static_assert(type != Type::Untracked);
const u64* const untracked_words = Array<Type::Untracked>();
const u64* const state_words = Array<type>();
const u64 num_query_words = size / BYTES_PER_WORD + 1;
const u64 word_begin = offset / BYTES_PER_WORD;
const u64 word_end = std::min(word_begin + num_query_words, NumWords());
@@ -460,7 +553,8 @@ private:
u64 begin = std::numeric_limits<u64>::max();
u64 end = 0;
for (u64 word_index = word_begin; word_index < word_end; ++word_index) {
const u64 word = state_words[word_index] & ~(gpu ? cpu_words[word_index] : 0);
const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
const u64 word = state_words[word_index] & ~off_word;
if (word == 0) {
continue;
}
@@ -488,8 +582,9 @@ private:
RasterizerInterface* rasterizer = nullptr;
VAddr cpu_addr = 0;
GpuCpuWords words;
Words words;
BufferFlagBits flags{};
int stream_score = 0;
};
} // namespace VideoCommon

View File

@@ -1,62 +0,0 @@
// Copyright 2019 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 VideoCommon {
class BufferBlock {
public:
[[nodiscard]] bool Overlaps(VAddr start, VAddr end) const {
return (cpu_addr < end) && (cpu_addr_end > start);
}
[[nodiscard]] bool IsInside(VAddr other_start, VAddr other_end) const {
return cpu_addr <= other_start && other_end <= cpu_addr_end;
}
[[nodiscard]] std::size_t Offset(VAddr in_addr) const {
return static_cast<std::size_t>(in_addr - cpu_addr);
}
[[nodiscard]] VAddr CpuAddr() const {
return cpu_addr;
}
[[nodiscard]] VAddr CpuAddrEnd() const {
return cpu_addr_end;
}
void SetCpuAddr(VAddr new_addr) {
cpu_addr = new_addr;
cpu_addr_end = new_addr + size;
}
[[nodiscard]] std::size_t Size() const {
return size;
}
[[nodiscard]] u64 Epoch() const {
return epoch;
}
void SetEpoch(u64 new_epoch) {
epoch = new_epoch;
}
protected:
explicit BufferBlock(VAddr cpu_addr_, std::size_t size_) : size{size_} {
SetCpuAddr(cpu_addr_);
}
private:
VAddr cpu_addr{};
VAddr cpu_addr_end{};
std::size_t size{};
u64 epoch{};
};
} // namespace VideoCommon

View File

@@ -0,0 +1,13 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/microprofile.h"
namespace VideoCommon {
MICROPROFILE_DEFINE(GPU_PrepareBuffers, "GPU", "Prepare buffers", MP_RGB(224, 128, 128));
MICROPROFILE_DEFINE(GPU_BindUploadBuffers, "GPU", "Bind and upload buffers", MP_RGB(224, 128, 128));
MICROPROFILE_DEFINE(GPU_DownloadMemory, "GPU", "Download buffers", MP_RGB(224, 128, 128));
} // namespace VideoCommon

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstddef>
#include <memory>
#include "video_core/buffer_cache/map_interval.h"
namespace VideoCommon {
MapIntervalAllocator::MapIntervalAllocator() {
FillFreeList(first_chunk);
}
MapIntervalAllocator::~MapIntervalAllocator() = default;
void MapIntervalAllocator::AllocateNewChunk() {
*new_chunk = std::make_unique<Chunk>();
FillFreeList(**new_chunk);
new_chunk = &(*new_chunk)->next;
}
void MapIntervalAllocator::FillFreeList(Chunk& chunk) {
const std::size_t old_size = free_list.size();
free_list.resize(old_size + chunk.data.size());
std::transform(chunk.data.rbegin(), chunk.data.rend(), free_list.begin() + old_size,
[](MapInterval& interval) { return &interval; });
}
} // namespace VideoCommon

View File

@@ -1,93 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstddef>
#include <memory>
#include <vector>
#include <boost/intrusive/set_hook.hpp>
#include "common/common_types.h"
#include "video_core/gpu.h"
namespace VideoCommon {
struct MapInterval : public boost::intrusive::set_base_hook<boost::intrusive::optimize_size<true>> {
MapInterval() = default;
/*implicit*/ MapInterval(VAddr start_) noexcept : start{start_} {}
explicit MapInterval(VAddr start_, VAddr end_, GPUVAddr gpu_addr_) noexcept
: start{start_}, end{end_}, gpu_addr{gpu_addr_} {}
bool IsInside(VAddr other_start, VAddr other_end) const noexcept {
return start <= other_start && other_end <= end;
}
bool Overlaps(VAddr other_start, VAddr other_end) const noexcept {
return start < other_end && other_start < end;
}
void MarkAsModified(bool is_modified_, u64 ticks_) noexcept {
is_modified = is_modified_;
ticks = ticks_;
}
boost::intrusive::set_member_hook<> member_hook_;
VAddr start = 0;
VAddr end = 0;
GPUVAddr gpu_addr = 0;
u64 ticks = 0;
bool is_written = false;
bool is_modified = false;
bool is_registered = false;
bool is_memory_marked = false;
bool is_sync_pending = false;
};
struct MapIntervalCompare {
constexpr bool operator()(const MapInterval& lhs, const MapInterval& rhs) const noexcept {
return lhs.start < rhs.start;
}
};
class MapIntervalAllocator {
public:
MapIntervalAllocator();
~MapIntervalAllocator();
MapInterval* Allocate() {
if (free_list.empty()) {
AllocateNewChunk();
}
MapInterval* const interval = free_list.back();
free_list.pop_back();
return interval;
}
void Release(MapInterval* interval) {
free_list.push_back(interval);
}
private:
struct Chunk {
std::unique_ptr<Chunk> next;
std::array<MapInterval, 0x8000> data;
};
void AllocateNewChunk();
void FillFreeList(Chunk& chunk);
std::vector<MapInterval*> free_list;
Chunk first_chunk;
std::unique_ptr<Chunk>* new_chunk = &first_chunk.next;
};
} // namespace VideoCommon

View File

@@ -37,59 +37,43 @@ CDmaPusher::CDmaPusher(GPU& gpu_)
CDmaPusher::~CDmaPusher() = default;
void CDmaPusher::Push(ChCommandHeaderList&& entries) {
cdma_queue.push(std::move(entries));
}
void CDmaPusher::DispatchCalls() {
while (!cdma_queue.empty()) {
Step();
}
}
void CDmaPusher::Step() {
const auto entries{cdma_queue.front()};
cdma_queue.pop();
std::vector<u32> values(entries.size());
std::memcpy(values.data(), entries.data(), entries.size() * sizeof(u32));
for (const u32 value : values) {
void CDmaPusher::ProcessEntries(ChCommandHeaderList&& entries) {
for (const auto& value : entries) {
if (mask != 0) {
const auto lbs = static_cast<u32>(std::countr_zero(mask));
mask &= ~(1U << lbs);
ExecuteCommand(static_cast<u32>(offset + lbs), value);
ExecuteCommand(offset + lbs, value.raw);
continue;
} else if (count != 0) {
--count;
ExecuteCommand(static_cast<u32>(offset), value);
ExecuteCommand(offset, value.raw);
if (incrementing) {
++offset;
}
continue;
}
const auto mode = static_cast<ChSubmissionMode>((value >> 28) & 0xf);
const auto mode = value.submission_mode.Value();
switch (mode) {
case ChSubmissionMode::SetClass: {
mask = value & 0x3f;
offset = (value >> 16) & 0xfff;
current_class = static_cast<ChClassId>((value >> 6) & 0x3ff);
mask = value.value & 0x3f;
offset = value.method_offset;
current_class = static_cast<ChClassId>((value.value >> 6) & 0x3ff);
break;
}
case ChSubmissionMode::Incrementing:
case ChSubmissionMode::NonIncrementing:
count = value & 0xffff;
offset = (value >> 16) & 0xfff;
count = value.value;
offset = value.method_offset;
incrementing = mode == ChSubmissionMode::Incrementing;
break;
case ChSubmissionMode::Mask:
mask = value & 0xffff;
offset = (value >> 16) & 0xfff;
mask = value.value;
offset = value.method_offset;
break;
case ChSubmissionMode::Immediate: {
const u32 data = value & 0xfff;
offset = (value >> 16) & 0xfff;
ExecuteCommand(static_cast<u32>(offset), data);
const u32 data = value.value & 0xfff;
offset = value.method_offset;
ExecuteCommand(offset, data);
break;
}
default:
@@ -102,8 +86,8 @@ void CDmaPusher::Step() {
void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
switch (current_class) {
case ChClassId::NvDec:
ThiStateWrite(nvdec_thi_state, state_offset, {data});
switch (static_cast<ThiMethod>(state_offset)) {
ThiStateWrite(nvdec_thi_state, offset, data);
switch (static_cast<ThiMethod>(offset)) {
case ThiMethod::IncSyncpt: {
LOG_DEBUG(Service_NVDRV, "NVDEC Class IncSyncpt Method");
const auto syncpoint_id = static_cast<u32>(data & 0xFF);
@@ -120,7 +104,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
static_cast<u32>(nvdec_thi_state.method_0));
nvdec_processor->ProcessMethod(static_cast<Nvdec::Method>(nvdec_thi_state.method_0),
{data});
data);
break;
default:
break;
@@ -144,7 +128,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
case ThiMethod::SetMethod1:
LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
static_cast<u32>(vic_thi_state.method_0), data);
vic_processor->ProcessMethod(static_cast<Vic::Method>(vic_thi_state.method_0), {data});
vic_processor->ProcessMethod(static_cast<Vic::Method>(vic_thi_state.method_0), data);
break;
default:
break;
@@ -153,7 +137,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
case ChClassId::Host1x:
// This device is mainly for syncpoint synchronization
LOG_DEBUG(Service_NVDRV, "Host1X Class Method");
host1x_processor->ProcessMethod(static_cast<Host1x::Method>(state_offset), {data});
host1x_processor->ProcessMethod(static_cast<Host1x::Method>(offset), data);
break;
default:
UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class));
@@ -161,10 +145,9 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
}
}
void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 state_offset,
const std::vector<u32>& arguments) {
u8* const state_offset_ptr = reinterpret_cast<u8*>(&state) + sizeof(u32) * state_offset;
std::memcpy(state_offset_ptr, arguments.data(), sizeof(u32) * arguments.size());
void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 state_offset, u32 argument) {
u8* const offset_ptr = reinterpret_cast<u8*>(&state) + sizeof(u32) * state_offset;
std::memcpy(offset_ptr, &argument, sizeof(u32));
}
} // namespace Tegra

View File

@@ -5,9 +5,7 @@
#pragma once
#include <memory>
#include <unordered_map>
#include <vector>
#include <queue>
#include "common/bit_field.h"
#include "common/common_types.h"
@@ -16,9 +14,9 @@
namespace Tegra {
class GPU;
class Host1x;
class Nvdec;
class Vic;
class Host1x;
enum class ChSubmissionMode : u32 {
SetClass = 0,
@@ -48,16 +46,10 @@ enum class ChClassId : u32 {
NvDec = 0xf0
};
enum class ChMethod : u32 {
Empty = 0,
SetMethod = 0x10,
SetData = 0x11,
};
union ChCommandHeader {
u32 raw;
BitField<0, 16, u32> value;
BitField<16, 12, ChMethod> method_offset;
BitField<16, 12, u32> method_offset;
BitField<28, 4, ChSubmissionMode> submission_mode;
};
static_assert(sizeof(ChCommandHeader) == sizeof(u32), "ChCommand header is an invalid size");
@@ -99,21 +91,15 @@ public:
explicit CDmaPusher(GPU& gpu_);
~CDmaPusher();
/// Push NVDEC command buffer entries into queue
void Push(ChCommandHeaderList&& entries);
/// Process queued command buffer entries
void DispatchCalls();
/// Process one queue element
void Step();
/// Process the command entry
void ProcessEntries(ChCommandHeaderList&& entries);
private:
/// Invoke command class devices to execute the command based on the current state
void ExecuteCommand(u32 state_offset, u32 data);
private:
/// Write arguments value to the ThiRegisters member at the specified offset
void ThiStateWrite(ThiRegisters& state, u32 state_offset, const std::vector<u32>& arguments);
void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument);
GPU& gpu;
std::shared_ptr<Tegra::Nvdec> nvdec_processor;
@@ -124,13 +110,10 @@ private:
ThiRegisters vic_thi_state{};
ThiRegisters nvdec_thi_state{};
s32 count{};
s32 offset{};
u32 count{};
u32 offset{};
u32 mask{};
bool incrementing{};
// Queue of command lists to be processed
std::queue<ChCommandHeaderList> cdma_queue;
};
} // namespace Tegra

View File

@@ -44,8 +44,10 @@ Codec::~Codec() {
}
void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", codec);
current_codec = codec;
if (current_codec != codec) {
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec));
current_codec = codec;
}
}
void Codec::StateWrite(u32 offset, u64 arguments) {
@@ -55,7 +57,6 @@ void Codec::StateWrite(u32 offset, u64 arguments) {
void Codec::Decode() {
bool is_first_frame = false;
if (!initialized) {
if (current_codec == NvdecCommon::VideoCodec::H264) {
av_codec = avcodec_find_decoder(AV_CODEC_ID_H264);

View File

@@ -12,16 +12,16 @@ Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {}
Nvdec::~Nvdec() = default;
void Nvdec::ProcessMethod(Method method, const std::vector<u32>& arguments) {
void Nvdec::ProcessMethod(Method method, u32 argument) {
if (method == Method::SetVideoCodec) {
codec->StateWrite(static_cast<u32>(method), arguments[0]);
codec->StateWrite(static_cast<u32>(method), argument);
} else {
codec->StateWrite(static_cast<u32>(method), static_cast<u64>(arguments[0]) << 8);
codec->StateWrite(static_cast<u32>(method), static_cast<u64>(argument) << 8);
}
switch (method) {
case Method::SetVideoCodec:
codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(arguments[0]));
codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument));
break;
case Method::Execute:
Execute();

View File

@@ -23,7 +23,7 @@ public:
~Nvdec();
/// Writes the method into the state, Invoke Execute() if encountered
void ProcessMethod(Method method, const std::vector<u32>& arguments);
void ProcessMethod(Method method, u32 argument);
/// Return most recently decoded frame
[[nodiscard]] AVFramePtr GetFrame();

View File

@@ -18,18 +18,14 @@ extern "C" {
namespace Tegra {
Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_)
: gpu(gpu_), nvdec_processor(std::move(nvdec_processor_)) {}
: gpu(gpu_),
nvdec_processor(std::move(nvdec_processor_)), converted_frame_buffer{nullptr, av_free} {}
Vic::~Vic() = default;
void Vic::VicStateWrite(u32 offset, u32 arguments) {
u8* const state_offset = reinterpret_cast<u8*>(&vic_state) + offset * sizeof(u32);
std::memcpy(state_offset, &arguments, sizeof(u32));
}
void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", method);
VicStateWrite(static_cast<u32>(method), arguments[0]);
const u64 arg = static_cast<u64>(arguments[0]) << 8;
void Vic::ProcessMethod(Method method, u32 argument) {
LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method));
const u64 arg = static_cast<u64>(argument) << 8;
switch (method) {
case Method::Execute:
Execute();
@@ -53,8 +49,7 @@ void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
void Vic::Execute() {
if (output_surface_luma_address == 0) {
LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Received 0x{:X}",
vic_state.output_surface.luma_offset);
LOG_ERROR(Service_NVDRV, "VIC Luma address not set.");
return;
}
const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)};
@@ -89,8 +84,10 @@ void Vic::Execute() {
// Get Converted frame
const std::size_t linear_size = frame->width * frame->height * 4;
using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>;
AVMallocPtr converted_frame_buffer{static_cast<u8*>(av_malloc(linear_size)), av_free};
// Only allocate frame_buffer once per stream, as the size is not expected to change
if (!converted_frame_buffer) {
converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free};
}
const int converted_stride{frame->width * 4};
u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
@@ -104,18 +101,16 @@ void Vic::Execute() {
const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
const auto size = Tegra::Texture::CalculateSize(true, 4, frame->width, frame->height, 1,
block_height, 0);
std::vector<u8> swizzled_data(size);
luma_buffer.resize(size);
Tegra::Texture::SwizzleSubrect(frame->width, frame->height, frame->width * 4,
frame->width, 4, swizzled_data.data(),
frame->width, 4, luma_buffer.data(),
converted_frame_buffer.get(), block_height, 0, 0);
gpu.MemoryManager().WriteBlock(output_surface_luma_address, swizzled_data.data(), size);
gpu.Maxwell3D().OnMemoryWrite();
gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
} else {
// send pitch linear frame
gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr,
linear_size);
gpu.Maxwell3D().OnMemoryWrite();
}
break;
}
@@ -134,15 +129,15 @@ void Vic::Execute() {
const auto stride = frame->linesize[0];
const auto half_stride = frame->linesize[1];
std::vector<u8> luma_buffer(aligned_width * surface_height);
std::vector<u8> chroma_buffer(aligned_width * half_height);
luma_buffer.resize(aligned_width * surface_height);
chroma_buffer.resize(aligned_width * half_height);
// Populate luma buffer
for (std::size_t y = 0; y < surface_height - 1; ++y) {
std::size_t src = y * stride;
std::size_t dst = y * aligned_width;
const std::size_t src = y * stride;
const std::size_t dst = y * aligned_width;
std::size_t size = surface_width;
const std::size_t size = surface_width;
for (std::size_t offset = 0; offset < size; ++offset) {
luma_buffer[dst + offset] = luma_ptr[src + offset];
@@ -153,8 +148,8 @@ void Vic::Execute() {
// Populate chroma buffer from both channels with interleaving.
for (std::size_t y = 0; y < half_height; ++y) {
std::size_t src = y * half_stride;
std::size_t dst = y * aligned_width;
const std::size_t src = y * half_stride;
const std::size_t dst = y * aligned_width;
for (std::size_t x = 0; x < half_width; ++x) {
chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x];
@@ -163,7 +158,6 @@ void Vic::Execute() {
}
gpu.MemoryManager().WriteBlock(output_surface_chroma_u_address, chroma_buffer.data(),
chroma_buffer.size());
gpu.Maxwell3D().OnMemoryWrite();
break;
}
default:

View File

@@ -15,43 +15,6 @@ namespace Tegra {
class GPU;
class Nvdec;
struct PlaneOffsets {
u32 luma_offset{};
u32 chroma_u_offset{};
u32 chroma_v_offset{};
};
struct VicRegisters {
INSERT_PADDING_WORDS(64);
u32 nop{};
INSERT_PADDING_WORDS(15);
u32 pm_trigger{};
INSERT_PADDING_WORDS(47);
u32 set_application_id{};
u32 set_watchdog_timer{};
INSERT_PADDING_WORDS(17);
u32 context_save_area{};
u32 context_switch{};
INSERT_PADDING_WORDS(43);
u32 execute{};
INSERT_PADDING_WORDS(63);
std::array<std::array<PlaneOffsets, 8>, 8> surfacex_slots{};
u32 picture_index{};
u32 control_params{};
u32 config_struct_offset{};
u32 filter_struct_offset{};
u32 palette_offset{};
u32 hist_offset{};
u32 context_id{};
u32 fce_ucode_size{};
PlaneOffsets output_surface{};
u32 fce_ucode_offset{};
INSERT_PADDING_WORDS(4);
std::array<u32, 8> slot_context_id{};
INSERT_PADDING_WORDS(16);
};
static_assert(sizeof(VicRegisters) == 0x7A0, "VicRegisters is an invalid size");
class Vic {
public:
enum class Method : u32 {
@@ -67,14 +30,11 @@ public:
~Vic();
/// Write to the device state.
void ProcessMethod(Method method, const std::vector<u32>& arguments);
void ProcessMethod(Method method, u32 argument);
private:
void Execute();
void VicStateWrite(u32 offset, u32 arguments);
VicRegisters vic_state{};
enum class VideoPixelFormat : u64_le {
RGBA8 = 0x1f,
BGRA8 = 0x20,
@@ -88,8 +48,6 @@ private:
BitField<9, 2, u64_le> chroma_loc_vert;
BitField<11, 4, u64_le> block_linear_kind;
BitField<15, 4, u64_le> block_linear_height_log2;
BitField<19, 3, u64_le> reserved0;
BitField<22, 10, u64_le> reserved1;
BitField<32, 14, u64_le> surface_width_minus1;
BitField<46, 14, u64_le> surface_height_minus1;
};
@@ -97,6 +55,13 @@ private:
GPU& gpu;
std::shared_ptr<Tegra::Nvdec> nvdec_processor;
/// Avoid reallocation of the following buffers every frame, as their
/// size does not change during a stream
using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>;
AVMallocPtr converted_frame_buffer;
std::vector<u8> luma_buffer;
std::vector<u8> chroma_buffer;
GPUVAddr config_struct_address{};
GPUVAddr output_surface_luma_address{};
GPUVAddr output_surface_chroma_u_address{};

View File

@@ -12,13 +12,30 @@
#define NUM(field_name) (sizeof(::Tegra::Engines::Maxwell3D::Regs::field_name) / (sizeof(u32)))
namespace VideoCommon::Dirty {
namespace {
using Tegra::Engines::Maxwell3D;
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables) {
void SetupDirtyVertexBuffers(Maxwell3D::DirtyState::Tables& tables) {
static constexpr std::size_t num_array = 3;
for (std::size_t i = 0; i < Maxwell3D::Regs::NumVertexArrays; ++i) {
const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]);
const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]);
FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers);
FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers);
}
}
void SetupIndexBuffer(Maxwell3D::DirtyState::Tables& tables) {
FillBlock(tables[0], OFF(index_array), NUM(index_array), IndexBuffer);
}
void SetupDirtyDescriptors(Maxwell3D::DirtyState::Tables& tables) {
FillBlock(tables[0], OFF(tic), NUM(tic), Descriptors);
FillBlock(tables[0], OFF(tsc), NUM(tsc), Descriptors);
}
void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
static constexpr std::size_t num_per_rt = NUM(rt[0]);
static constexpr std::size_t begin = OFF(rt);
static constexpr std::size_t num = num_per_rt * Maxwell3D::Regs::NumRenderTargets;
@@ -41,5 +58,13 @@ void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tabl
FillBlock(table, OFF(zeta), NUM(zeta), flag);
}
}
} // Anonymous namespace
void SetupDirtyFlags(Maxwell3D::DirtyState::Tables& tables) {
SetupDirtyVertexBuffers(tables);
SetupIndexBuffer(tables);
SetupDirtyDescriptors(tables);
SetupDirtyRenderTargets(tables);
}
} // namespace VideoCommon::Dirty

View File

@@ -30,6 +30,12 @@ enum : u8 {
ColorBuffer7,
ZetaBuffer,
VertexBuffers,
VertexBuffer0,
VertexBuffer31 = VertexBuffer0 + 31,
IndexBuffer,
LastCommonEntry,
};
@@ -47,6 +53,6 @@ void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_
FillBlock(tables[1], begin, num, index_b);
}
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
void SetupDirtyFlags(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
} // namespace VideoCommon::Dirty

View File

@@ -23,8 +23,6 @@ void DmaPusher::DispatchCalls() {
MICROPROFILE_SCOPE(DispatchCalls);
gpu.SyncGuestHost();
// On entering GPU code, assume all memory may be touched by the ARM core.
gpu.Maxwell3D().OnMemoryWrite();
dma_pushbuffer_subindex = 0;

View File

@@ -18,8 +18,8 @@ Fermi2D::Fermi2D() {
Fermi2D::~Fermi2D() = default;
void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
rasterizer = &rasterizer_;
void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
rasterizer = rasterizer_;
}
void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {

View File

@@ -38,7 +38,7 @@ public:
~Fermi2D();
/// Binds a rasterizer to this engine.
void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
/// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;

View File

@@ -21,8 +21,8 @@ KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manage
KeplerCompute::~KeplerCompute() = default;
void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
rasterizer = &rasterizer_;
void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
rasterizer = rasterizer_;
}
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
@@ -39,7 +39,6 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
upload_state.ProcessData(method_argument, is_last_call);
if (is_last_call) {
system.GPU().Maxwell3D().OnMemoryWrite();
}
break;
}

View File

@@ -46,7 +46,7 @@ public:
~KeplerCompute();
/// Binds a rasterizer to this engine.
void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
static constexpr std::size_t NumConstBuffers = 8;

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