Compare commits

..

158 Commits

Author SHA1 Message Date
bunnei
a0d7a2732d hle: service: caps_u: Stub GetAlbumFileList3AaeAruid.
- This works similiar to GetAlbumContentsFileListForApplication.
- Since we do not implement the album, this should be safe to stub for now.
- Used by Super Smash Bros. Ultimate (newer updates) in World of Light.
2020-11-06 22:23:15 -08:00
bunnei
f6a89edb67 Merge pull request #4899 from lioncash/fiberimpl
common/fiber: Move all member variables into impl class
2020-11-06 20:01:03 -08:00
Lioncash
00fb79b2f3 common/fiber: Move all member variables into impl class
Hides all of the implementation details for users of the class. This has
the benefit of reducing includes and also making the fiber classes
movable again.
2020-11-06 20:36:32 -05:00
bunnei
91a45834fd Merge pull request #4891 from lioncash/clang2
General: Fix clang build
2020-11-06 10:33:13 -08:00
bunnei
0b75ec5316 Merge pull request #4894 from lioncash/fn
settings: Simplify initializer of resolution factor
2020-11-06 09:54:02 -08:00
Lioncash
c0ab5b79dc settings: Simplify initializer of resolution factor
This can use a braced initializer to accomplish the same thing with less
code.
2020-11-05 22:07:10 -05:00
bunnei
a111a9ae2c Merge pull request #4854 from ReinUsesLisp/cube-array-shadow
shader: Partially implement texture cube array shadow
2020-11-05 16:25:00 -08:00
Lioncash
6f006d051e General: Fix clang build
Allows building on clang to work again
2020-11-05 10:07:16 -05:00
bunnei
d62d28522b Merge pull request #4889 from lioncash/setting-global
core/settings: Move configuring_global behind an API
2020-11-04 17:09:19 -08:00
bunnei
087f52e872 Merge pull request #4858 from lioncash/initializer
General: Resolve a few missing initializer warnings
2020-11-04 12:10:10 -08:00
Lioncash
7aae6d6d2b core/settings: Move configuring_global behind an API
Rather than have directly modified global state here, we can make it an
implementation detail and have an interface that changes are queried
through.
2020-11-04 04:16:37 -05:00
Chloe
6bbbbe8f85 Merge pull request #4869 from bunnei/improve-gpu-sync
Improvements to GPU synchronization & various refactoring
2020-11-04 18:36:55 +11:00
bunnei
4bfa411ddc Merge pull request #4874 from lioncash/nodiscard2
nvdec: Make use of [[nodiscard]] where applicable
2020-11-03 16:34:07 -08:00
bunnei
46fdc94586 Merge pull request #4887 from lioncash/common-build
microprofile: Silence warning in headers
2020-11-03 13:41:29 -08:00
Lioncash
ee21b5378b microprofile: Silence warning in headers
Silences a truncation warning by making the truncation explicit and
documenting the reason for it.
2020-11-03 15:07:13 -05:00
bunnei
222fe75401 Merge pull request #4873 from lioncash/common-error
common: Enable warnings as errors
2020-11-03 11:00:23 -08:00
bunnei
448e4d5c2a Merge pull request #4878 from bunnei/unload-nrr
hle: service: ldr: Implement UnloadNrr.
2020-11-03 08:52:40 -08:00
Lioncash
4a4b685a04 common: Enable warnings as errors
Cleans up common so that we can enable warnings as errors.
2020-11-02 15:50:58 -05:00
Lioncash
4f0f481f63 nvdec: Make use of [[nodiscard]] where applicable
Prevents bugs from occurring where the results of a function are
accidentally discarded
2020-11-02 02:45:15 -05:00
bunnei
1089d76736 Merge pull request #4865 from ameerj/async-threadcount
async_shaders: Increase Async worker thread count for >8 thread cpus
2020-11-01 01:54:01 -07:00
bunnei
848bdf8a40 fixup! hle service: nvdrv: nvhost_gpu: Update to use SyncpointManager and other improvements. 2020-11-01 01:52:38 -07:00
bunnei
7d2839d7a3 core: Initialize GPU before services. 2020-11-01 01:52:38 -07:00
bunnei
e67b8678f8 hle service: nvdrv: nvhost_gpu: Update to use SyncpointManager and other improvements.
- Refactor so that SubmitGPFIFO and KickoffPB use shared functionality.
- Implement add_wait and add_increment flags.
2020-11-01 01:52:38 -07:00
bunnei
c6e1c46ac7 video_core: dma_pusher: Add support for integrity checks.
- Log corrupted command lists, rather than crash.
2020-11-01 01:52:38 -07:00
bunnei
c64545d07a video_core: dma_pusher: Add support for prefetched command lists. 2020-11-01 01:52:38 -07:00
bunnei
1d4cbb92f2 service: hle: nvflinger: Fix potential shutdown crash when GPU is destroyed. 2020-11-01 01:52:38 -07:00
bunnei
6053b95552 video_core: gpu: Implement WaitFence and IncrementSyncPoint. 2020-11-01 01:52:37 -07:00
bunnei
66edfd61c6 hle service: nvdrv: nvhost_ctrl: Update to use SyncpointManager. 2020-11-01 01:52:37 -07:00
bunnei
4a3fd97e48 hle service: nvdrv: Update to instantiate SyncpointManager. 2020-11-01 01:52:34 -07:00
bunnei
d567b7e841 hle: service: nvdrv: Implement SyncpointManager, to manage syncpoints. 2020-11-01 01:51:54 -07:00
Levi Behunin
bca9591660 Rename to align with switchbrew and remove gpu function (#4714)
* Rename to align with switchbrew

* Rename to align with switchbrew and remove gpu function that checks if clearing should be done.
2020-11-01 01:24:17 -07:00
bunnei
98f68d06f1 Merge pull request #4853 from ReinUsesLisp/fcmp-imm
shader/arithmetic: Implement FCMP immediate + register variant
2020-10-31 01:25:02 -07:00
bunnei
a0e5cccb92 hle: service: ldr: Implement UnloadNrr.
- Used by Final Fantasy X/X-2 HD Remaster.
2020-10-31 01:22:53 -07:00
LC
6db0c0d8d9 Merge pull request #4872 from jbeich/clang
video_core: unbreak -Werror in NVDEC with Clang
2020-10-30 15:11:40 -04:00
Lioncash
14a97d082e CMakeLists: Resolve MSVC build failures
Prevents the compiler tripping up about Windows headers.
2020-10-30 14:57:58 -04:00
Jan Beich
50e52ade85 video_core: unbreak -Werror in NVDEC with Clang
src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp:41:15: error: unused variable 'OutOfMemory' [-Werror,-Wunused-const-variable]
constexpr u32 OutOfMemory{static_cast<u32>(-12)};
              ^
2020-10-30 16:43:10 +00:00
bunnei
8aa9ae5ba5 Merge pull request #4868 from lioncash/discard-error
General: Make ignoring a discarded return value an error
2020-10-30 00:35:40 -07:00
bunnei
131a75b65d Merge pull request #4867 from lioncash/vp9
VP9: Minor interface changes and safety improvements
2020-10-29 21:33:27 -07:00
Lioncash
11d0a6e7b8 General: Catch more expressions with no effect on MSVC
MSVC lets us fine-tune catching expressions with no side-effects a
little more.
2020-10-30 00:13:26 -04:00
Lioncash
26547d3e3b General: Make ignoring a discarded return value an error
Allows our CI to catch more potential bugs. This also removes the
[[nodiscard]] attribute of IOFile's Open member function. There are
cases where a file may want to be opened, but have the status of it
checked at a later time.
2020-10-30 00:13:21 -04:00
Lioncash
8049b8beb6 common/stream: Be explicit with copy and move operators 2020-10-29 22:57:35 -04:00
Lioncash
12eeffcb7c vp9: Be explicit with copy and move operators
It's deprecated in the language to autogenerate these if the destructor
for a type is specified, so we can explicitly specify how we want these
to be generated.
2020-10-29 22:57:35 -04:00
Lioncash
0d713cf8eb vp9: Mark functions with [[nodiscard]] where applicable
Prevents values from mistakenly being discarded in cases where it's a
bug to do so.
2020-10-29 22:57:32 -04:00
Lioncash
badea3b301 vp9: Provide a default initializer for "hidden" member
The API of VP9 exposes a WasFrameHidden() function which accesses this
member. Given the constructor previously didn't initialize this member,
it's a potential vector for an uninitialized read.

Instead, we can initialize this to a deterministic value to prevent that
from occurring.
2020-10-29 22:35:55 -04:00
Lioncash
f8543249f0 vp9: Make some member functions internally linked
These helper functions don't directly modify any member state and can be
hidden from view.
2020-10-29 22:34:46 -04:00
Lioncash
5553bd3ba2 General: Resolve a few missing initializer warnings
Resolves a few -Wmissing-initializer warnings.
2020-10-29 19:37:07 -04:00
bunnei
7dcf4c0018 Merge pull request #4831 from lioncash/fmt
externals: Update fmt to 7.1.0
2020-10-29 14:44:07 -07:00
bunnei
ef29bf4515 Merge pull request #4837 from lioncash/nvdec-2
nvdec: Minor tidying up
2020-10-29 12:28:07 -07:00
ameerj
3620206136 async_shaders: Increase Async worker thread count for 8+ thread cpus
Adds 1 async worker thread for every 2 available threads above 8
2020-10-29 14:16:45 -04:00
bunnei
2dbb144fc6 Merge pull request #4781 from german77/GChotplug
Add hotplug, rumble and fix 3rd party adapters for the GC adapter
2020-10-29 10:28:19 -07:00
David
89199ca215 Merge pull request #4859 from Morph1984/missing-ctime-include
kernel/process: Add missing <ctime> include
2020-10-29 19:03:19 +11:00
Morph
9cfc5fee2f kernel/process: Add missing <ctime> include
Fixes compilation on MSVC
2020-10-29 03:17:20 -04:00
LC
1a6b1bf1d7 Merge pull request #4857 from liushuyu/master
web_service: follow-up fix to #4842
2020-10-29 01:54:45 -04:00
bunnei
c5134cbf3a Merge pull request #4835 from lat9nq/rng-default-time
kernel: Use the current time as the default RNG seed
2020-10-28 22:51:29 -07:00
bunnei
c6d001c94f Merge pull request #4838 from lioncash/syncmgr
sync_manager: Amend parameter order of calls to SyncptIncr constructor
2020-10-28 22:49:22 -07:00
liushuyu
cf63eacc1a web_service: follow-up fix to #4842 ...
* The web_service http request is now fixed on Windows (R) platform.
* The issue is due to a complicated race-condition in `httplib`, a detailed
  explanation is available at https://github.com/yhirose/cpp-httplib/pull/701
* A pending Pull Request on `httplib` has been applied to remedy the
  said race-condition.
* The socket availability check is removed due to a behavioral chice of
  `httplib` that a socket will not be created before any actual request
  is sent.
2020-10-28 23:16:06 -06:00
german
5333db91c1 Add hotplug, rumble and fix 3rd party adapters for the GC adapter 2020-10-28 21:12:34 -05:00
LC
c20569ebdf Merge pull request #4856 from bunnei/webservice-socket-error
web_service: web_backend: Handle socket errors with GenericRequest.
2020-10-28 20:46:28 -04:00
bunnei
156556ddd2 web_service: web_backend: Handle socket errors with GenericRequest.
- Fixes a shutdown crash when we try to submit telemetry if there is a service issue.
2020-10-28 17:19:12 -07:00
LC
475d46bb64 Merge pull request #4855 from bunnei/cdma-pusher-log-fix
video_core: cdma_pusher: Add missing LOG_DEBUG field in ExecuteCommand.
2020-10-28 20:01:29 -04:00
bunnei
94eca09cf6 video_core: cdma_pusher: Add missing LOG_DEBUG field in ExecuteCommand. 2020-10-28 16:47:08 -07:00
bunnei
7af2cb4318 Merge pull request #4846 from lioncash/service-fn
service: Update function tables
2020-10-28 13:47:56 -07:00
ReinUsesLisp
657771bdcb shader: Partially implement texture cube array shadow
This implements texture cube arrays with shadow comparisons but doesn't
fix the asserts related to it.

Fixes out of bounds reads on swizzle constructors and makes them use
bounds checked ::at instead of the unsafe operator[].
2020-10-28 17:12:40 -03:00
ReinUsesLisp
44b552be71 shader/arithmetic: Implement FCMP immediate + register variant
Trivially add the encoding for this.
2020-10-28 17:05:41 -03:00
bunnei
663e221f99 Merge pull request #4845 from lioncash/inih
externals: Track upstream inih
2020-10-28 09:58:58 -07:00
LC
725fcbb368 Merge pull request #4851 from ReinUsesLisp/core-threads-race
hle/kernel: Remove unused registered_core_threads to fix data races
2020-10-28 04:54:35 -04:00
LC
a1f176ce52 Merge pull request #4850 from ReinUsesLisp/fiber-ptr-ref
common/fiber: Take shared_ptr<Fiber> by copy in YieldTo
2020-10-28 04:54:19 -04:00
LC
1fd22823bc Merge pull request #4849 from ReinUsesLisp/fix-fiber-test
tests: Fix data race in fibers test
2020-10-28 04:26:10 -04:00
LC
978e7897a3 Merge pull request #4848 from ReinUsesLisp/type-limits
video_core: Enforce -Werror=type-limits
2020-10-28 03:16:10 -04:00
LC
55ac6f7a2b Merge pull request #4847 from ReinUsesLisp/warn-move
video_core: Enforce -Wredundant-move and -Wpessimizing-move
2020-10-28 03:14:58 -04:00
ReinUsesLisp
79da90cea8 video_core: Enforce -Wredundant-move and -Wpessimizing-move
Silence three warnings and make them errors to avoid introducing more in the future.
2020-10-28 02:44:50 -03:00
ReinUsesLisp
4a451e5849 video_core: Enforce -Werror=type-limits
Silences one warning and avoids introducing more in the future.
2020-10-28 02:37:47 -03:00
ReinUsesLisp
cdb2480d39 common/fiber: Take shared_ptr<Fiber> by copy in YieldTo
YieldTo does not intend to modify the passed shared_ptrs.
Pass it by copy to keep a reference count while this function executes.
2020-10-28 02:02:44 -03:00
ReinUsesLisp
3fdb42e0b4 tests: Fix data race in fibers test
Previous to this commit, the tests were using operator[] from
unordered_map to query elements but this silently inserts empty elements
when they don't exist. If all threads were executed without concurrency,
this wouldn't be an issue, but the same unordered_map could be written
from two threads at the same time. This is a data race and makes some
previously inserted elements invisible for a short period of time,
causing them to insert and return an empty element. This default
constructed element (a zero) was used to index an array of fibers that
asserted when one of them was nullptr, shutting the test session off.

To address this issue, lock on thread id reads and writes. This could be
a shared mutex to allow concurrent reads, but the definition of
std::this_thread::get_id is fuzzy when using non-standard techniques
like fibers. I opted to use a standard mutex.

While we are at it, fix the included headers.
2020-10-28 01:41:24 -03:00
Lioncash
020519def8 service: Update function tables
Updates function tables according to info on SwitchBrew.
2020-10-27 21:19:46 -04:00
Lioncash
9a44c1ea27 externals: Update inih to r52 2020-10-27 19:52:48 -04:00
Lioncash
65e697de59 externals: Track mainline inih project 2020-10-27 19:52:48 -04:00
LC
7d27a7a511 Merge pull request #4842 from liushuyu/fix-web-srv
web_backend: fix a regression introduced in 39c8d18
2020-10-27 19:12:27 -04:00
liushuyu
eb84e0f63a externals: auto detect system OpenSSL 2020-10-27 14:20:20 -06:00
liushuyu
8e673cbb08 web_backend: fix a regression introduced in 39c8d18
* A regression was in 39c8d18 and token verification function was
  broken.
* The reason being `httplib` now requires OpenSSL 1.1+ API while
  LibreSSL 2.x provided OpenSSL 1.0 compatible API.
* The bundled LibreSSL has been updated to 3.2.2 so it now provides
  OpenSSL 1.1 compatible API now.
* Also the path hint has been added so that it will find the correct
  path to the CA certs on *nix systems.
* An option is provided so that *nix system distributions/providers can
  use their own SSL implementations when compiling Yuzu/Citra to
  (hopefully) complies with their maintenance guidelines.
* LURLParse is also removed since `httplib` can handle
  `scheme:host:port` string itself now.
2020-10-27 02:57:19 -06:00
Lioncash
047e77e2f0 sync_manager: Amend parameter order of calls to SyncptIncr constructor
Corrects some cases where the arguments would be incorrectly swapped.
2020-10-27 03:22:57 -04:00
Lioncash
cce14b4cd7 h264: Make WriteUe take a u32
Enforces the type of the desired value in calling code.
2020-10-27 03:21:53 -04:00
Lioncash
6291975731 vp9: std::move buffer within ComposeFrameHeader()
We can move the buffer here to avoid a heap reallocation
2020-10-27 02:27:31 -04:00
Lioncash
00decfbb07 vp9: Remove dead code 2020-10-27 02:26:17 -04:00
Lioncash
111802bbbb vp9: Join declarations with assignments 2020-10-27 02:26:03 -04:00
Lioncash
3b5d5fa86f vp9: Remove pessimizing moves
The move will already occur without std::move.
2020-10-27 02:21:40 -04:00
Lioncash
dcc26c54a5 vp9: Resolve variable shadowing 2020-10-27 02:20:17 -04:00
Lioncash
c04203b786 nvdec: Tidy up header includes
Prevents a few unnecessary inclusions.
2020-10-27 02:16:42 -04:00
bunnei
cd92a94965 Merge pull request #4805 from bunnei/update-defaults
yuzu: settings: Enable multicore, asynch GPU, and assembly shaders by default.
2020-10-26 23:14:09 -07:00
bunnei
941563f981 yuzu: settings: Enable multicore, asynch GPU, and assembly shaders by default.
- In general, this is now the preferred settings for most games.

# Conflicts:
#	src/yuzu/configuration/config.cpp
2020-10-26 23:13:05 -07:00
bunnei
d33399e1f4 Merge pull request #4729 from ameerj/nvdec-prod
video_core: NVDEC Implementation
2020-10-26 23:02:42 -07:00
ReinUsesLisp
ce69ff2890 hle/kernel: Remove unused registered_core_threads to fix data races
This member was only used on asserts and it triggered data races.
Remove it to fix them.
2020-10-27 01:55:39 -03:00
bunnei
c7f32931ee Merge pull request #4832 from bunnei/cpu-manager-microprofile-fix
core: cpu_manager: Add missing call to MicroProfileOnThreadExit().
2020-10-26 21:29:09 -07:00
bunnei
1828f82000 Merge pull request #4833 from bunnei/timezonemanager-explicit
hle: services: TimeZoneContentManager: This can be made explicit.
2020-10-26 21:28:45 -07:00
ameerj
eb67a45ca8 video_core: NVDEC Implementation
This commit aims to implement the NVDEC (Nvidia Decoder) functionality, with video frame decoding being handled by the FFmpeg library.

The process begins with Ioctl commands being sent to the NVDEC and VIC (Video Image Composer) emulated devices. These allocate the necessary GPU buffers for the frame data, along with providing information on the incoming video data. A Submit command then signals the GPU to process and decode the frame data.

To decode the frame, the respective codec's header must be manually composed from the information provided by NVDEC, then sent with the raw frame data to the ffmpeg library.

Currently, H264 and VP9 are supported, with VP9 having some minor artifacting issues related mainly to the reference frame composition in its uncompressed header.

Async GPU is not properly implemented at the moment.

Co-Authored-By: David <25727384+ogniK5377@users.noreply.github.com>
2020-10-26 23:07:36 -04:00
bunnei
9f08cea2c4 Merge pull request #4834 from lioncash/copy-fn
controller: Pass ControllerParameters by reference in ReconfigureControllers()
2020-10-26 18:49:26 -07:00
lat9nq
8bd246032a kernel: Use the current time as the default RNG seed
Use the current time, not zero, as the default RNG seed.
2020-10-26 21:42:11 -04:00
Lioncash
6b5f565324 controller: Pass ControllerParameters by reference in ReconfigureControllers()
Prevents unnecessary copies and heap reallocations from occurring.
2020-10-26 21:06:15 -04:00
bunnei
3984bb6def hle: services: TimeZoneContentManager: This can be made explicit. 2020-10-26 16:11:45 -07:00
bunnei
54aabb00b0 core: cpu_manager: Add missing call to MicroProfileOnThreadExit().
- Fixes an occasional crash when trying to launch subsequent games.
2020-10-26 16:09:15 -07:00
Lioncash
1dd4132eb1 externals: Update fmt to 7.1.0
Keeps the used version of the library up to date.
2020-10-26 18:34:44 -04:00
Rodrigo Locatti
2f6ba54483 Merge pull request #4827 from lioncash/trunc
controller: Convert led_patterns integer literals to bool literals
2020-10-25 16:56:30 -03:00
Rodrigo Locatti
ae3a755d13 Merge pull request #4828 from lioncash/lockguard
general: Use template deduction guides for lock_guard
2020-10-25 16:55:54 -03:00
Lioncash
98f4c5e7b8 general: Use template deduction guides for lock_guard
Same behavior, less code.
2020-10-25 13:53:22 -04:00
Lioncash
061a63547f controller: Convert led_patterns integer literals to bool literals
'bool' isn't always guaranteed to be the same size as an int, so this
can technically cause truncation warnings if we support other platforms.
2020-10-25 13:44:12 -04:00
LC
fe53ee26ce Merge pull request #4826 from Morph1984/resolve-warning
applets/profile_select: Resolve a warning in exec()
2020-10-25 11:47:09 -04:00
Morph
9afbcd9e8a applets/profile_select: Resolve a warning in exec()
Resolves a warning where not all control paths return a value.
2020-10-25 09:16:43 -04:00
bunnei
ab052cf684 Merge pull request #4817 from Kewlan/open-single-save-location
main/profile_select: Don't ask for profile when there's only one.
2020-10-24 03:02:19 -07:00
LC
6f6d83befa Merge pull request #4816 from Morph1984/controller-disconnect-fix
sdl_impl: Fix controller reconnection issues
2020-10-23 13:53:51 -04:00
bunnei
3e46934442 Merge pull request #4706 from ReinUsesLisp/cmake-host-shaders
video_core: Fix instances where msbuild always regenerated host shaders
2020-10-23 10:01:16 -07:00
bunnei
e7042163c8 Merge pull request #4792 from bunnei/rtc-fix
service: time: Update current time with changes to RTC setting.
2020-10-22 20:46:54 -07:00
Kewlan
85b5b816cf Don't ask for profile when there's only one. 2020-10-22 11:16:56 +02:00
Lioncash
ea20b5c970 core: Fix clang build pt.3
Should finally resolve building with clang.
2020-10-21 22:14:23 -04:00
Morph
2f852f182a sdl_impl: Fix controller reconnection issues
It turns out that after a controller is disconnected, there is a chance that events from the previous controller are sent/processed after it has been disconnected.
This causes the previously disconnected controller to reappear as connected due to GetSDLJoystickBySDLID() emplacing this controller back to the map.

Fix this by only returning an SDLJoystick if and only if it exists in the map.
2020-10-21 09:41:30 -04:00
bunnei
1fc61d09d3 Merge pull request #4811 from lioncash/warn-video
video_core: Conditially activate relevant compiler warnings
2020-10-20 20:59:37 -07:00
Lioncash
e408bd3b7c core: Fix clang build pt.2
Resolves the clang build issue in a more unintrusive way.
2020-10-20 22:16:03 -04:00
LC
2e74b79e89 Merge pull request #4814 from yuzu-emu/revert-4796-clang
Revert "core: Fix clang build"
2020-10-20 22:08:53 -04:00
bunnei
3d592972dc Revert "core: Fix clang build" 2020-10-20 19:07:39 -07:00
Lioncash
678d012c2c video_core: Conditially activate relevant compiler warnings
These compiler flags aren't shared with clang, so specifying these flags
unconditionally can lead to a bit of warning spam.

While we're in the area, we can also enable -Wunused-but-set-parameter
given this is almost always a bug.
2020-10-20 20:28:25 -04:00
Lioncash
fdd9154069 kernel: Fix build with recent compiler flag changes
This slipped through the cracks due to another change being merged
before the compiler flag changes.
2020-10-20 20:23:18 -04:00
LC
536c51912d Merge pull request #4807 from ReinUsesLisp/glasm-robust-ssbo
gl_arb_decompiler: Implement robust buffer operations
2020-10-20 19:19:38 -04:00
LC
88d5140cf2 Merge pull request #4796 from lioncash/clang
core: Fix clang build
2020-10-20 19:19:12 -04:00
bunnei
940c3bf68d Merge pull request #4390 from ogniK5377/get-applet-inf-stub
nifm: GetAppletInfo stub
2020-10-20 15:59:19 -07:00
LC
ea8345cdcd Merge pull request #4809 from Morph1984/mjolnir-p3
configure_input_player: Fix modifier buttons
2020-10-20 15:00:06 -04:00
bunnei
e03dc4d569 Merge pull request #4627 from Morph1984/fix-dinput-controller-disconnect
sdl_impl: Erase the SDLJoystick entry after removing a controller
2020-10-20 11:40:06 -07:00
Morph
ff82f3894a configure_input_player: Fix modifier buttons
Fix them for real this time, now they finally work.
2020-10-20 14:23:25 -04:00
ReinUsesLisp
f21a189148 gl_arb_decompiler: Implement robust buffer operations
This emulates the behavior we get on GLSL with regular SSBOs with a
pointer + length pair. It aims to be consistent with the crashes we
might get.

Out of bounds stores are ignored. Atomics are ignored and return zero.
Reads return zero.
2020-10-20 03:34:32 -03:00
David Marcec
298b50e220 Added remaining params 2020-10-20 16:45:01 +11:00
David Marcec
acd35e1b60 nifm: GetAppletInfo stub
Fixes crash for Catherine Full Body
2020-10-20 16:42:40 +11:00
bunnei
60bd54776a Merge pull request #4788 from ReinUsesLisp/lockfree-host-thread
kernel: Implement host thread register methods without locking
2020-10-19 22:31:52 -07:00
bunnei
e7a26ecec5 Merge pull request #4785 from Morph1984/fs-hades
filesystem: Fix CreateDirectory and DeleteFile
2020-10-19 16:04:01 -07:00
bunnei
f1ead11df7 Merge pull request #4204 from ReinUsesLisp/vulkan-1.0
renderer_vulkan: Create and properly use Vulkan 1.0 instances when 1.1 is not available
2020-10-19 14:18:54 -07:00
bunnei
598ef6b0b3 Merge pull request #4802 from lioncash/bcat
core: Add boxcat sources with target_sources
2020-10-19 12:40:49 -07:00
bunnei
54b977acaa Merge pull request #4783 from bunnei/nvdrv-freespace
hle: service: nvdrv: Implement nvhost_as_gpu::FreeSpace.
2020-10-18 12:41:18 -07:00
Lioncash
0ab7bfdfce core: Add boxcat sources with target_sources
Same behavior, minus a script variable.
2020-10-18 08:09:06 -04:00
bunnei
2190f1a2b7 Merge pull request #4801 from lioncash/missing-bound
mii/manager: Make use of unused lower bound in GetRandomValue()
2020-10-17 23:32:40 -07:00
bunnei
743fe1aea3 Merge pull request #4782 from ReinUsesLisp/remove-dyn-primitive
vk_graphics_pipeline: Manage primitive topology as fixed state
2020-10-17 22:14:17 -07:00
Lioncash
be1954e04c core: Fix clang build
Recent changes to the build system that made more warnings be flagged as
errors caused building via clang to break.

Fixes #4795
2020-10-17 19:50:39 -04:00
Lioncash
c1577f3448 mii/manager: Make use of unused lower bound in GetRandomValue()
Previously, the lower bound wasn't being used and zero was being used as
the lower bound every time this function was called.

This affects the outcome of some of the randomized entries a little bit,
for example, the lower-bound for beard and mustache flags was supposed
to be 1, not 0.

Aside from these cases, the bug didn't affect anything else.
2020-10-17 09:50:04 -04:00
bunnei
1eb908bc88 Merge pull request #4797 from bunnei/bcat-errors
service: bcat: Check client connection before interacting with socket.
2020-10-16 23:28:40 -07:00
bunnei
cb708631b6 Merge pull request #4798 from lioncash/input-copy
udp/client: Take std::function by const reference with TestCommunication()
2020-10-16 21:20:01 -07:00
bunnei
363c644730 service: bcat: Check client connection before interacting with socket.
- Fixes a crash when BCAT service is offline.
2020-10-16 21:16:56 -07:00
Lioncash
30b1e71066 udp/client: Make use of designated initializers in TestCommunication()
Same behavior, but makes the callback list nicer to look at.
2020-10-16 06:23:51 -04:00
Lioncash
36cfb234d5 udp/client: Take std::function by const reference with TestCommunication()
Avoids redundant copies.
2020-10-16 06:22:29 -04:00
Morph
7b3f5845d2 sdl_impl: Erase the SDLJoystick entry after removing a controller
Previously, disconnecting a controller still leaves a null SDLJoystick entry within the vector of SDLJoysticks mapped by GUID.
When a DirectInput device of the same GUID is reconnected, it adds that device to a new port causing non-detectable input.
Furthermore, opening the "Configure" menu would cause yuzu to crash since it first tries to resolve the name of a null SDLJoystick entry that was not removed.
Resolve this by properly erasing the SDLJoystick entry from the vector.
2020-10-16 00:04:59 -04:00
bunnei
64f967fd49 Merge pull request #4790 from lioncash/input-common
input_common/CMakeLists: Make some warnings errors
2020-10-15 20:59:34 -07:00
bunnei
dbd1662ae2 Merge pull request #4784 from bunnei/cancelbuffer
hle: service: vi: Implement BufferQueue::CancelBuffer.
2020-10-15 19:57:27 -07:00
Lioncash
046c0c91a3 input_common/CMakeLists: Make some warnings errors
Makes the input_common code warnings consistent with the rest of the
codebase.
2020-10-15 19:37:51 -04:00
bunnei
046cc81938 Merge pull request #4793 from bunnei/storeopencontext
service: acc: Stub IManagerForApplication::StoreOpenContext.
2020-10-14 20:42:18 -07:00
bunnei
1f186f34a2 hle: service: vi: Implement BufferQueue::CancelBuffer.
- This is used by Super Mario 3D All-Stars.
2020-10-13 22:11:52 -07:00
ReinUsesLisp
b9a9b83bee kernel: Implement host thread register methods without locking
Locks on GetCurrentHostThreadID were causing performance issues
according to Visual Studio's profiler. It was consuming twice the time
as arm_interface.Run(). The cost was not in the function itself but in
the lockinig it required.

Reimplement these functions using atomics and static storage instead of
an unordered_map. This is a side effect to avoid locking and using linked
lists for reads.

Replace unordered_map with a linear search.
2020-10-13 18:00:25 -03:00
ReinUsesLisp
e4e0abc418 vk_graphics_pipeline: Manage primitive topology as fixed state
Vulkan has requirements for primitive topologies that don't play nicely
with yuzu's. Since it's only 4 bits, we can move it to fixed state
without changing the size of the pipeline key.

- Fixes a regression on recent Nvidia drivers on Fire Emblem: Three
  Houses.
2020-10-13 04:08:33 -03:00
Morph
8db3feae19 filesystem: Fix CreateDirectory and DeleteFile
Add a check if dir is nullptr (does not exist)

Fixes save game creation in Hades
2020-10-13 00:57:04 -04:00
bunnei
62c6c9f6a6 service: time: Update current time with changes to RTC setting.
- This can be used to advance time, e.g. for Pokemon Sword/Shield pokejobs.
2020-10-12 18:09:15 -07:00
bunnei
b260847218 hle: service: nvdrv: Implement nvhost_as_gpu::FreeSpace.
- This is used by Super Mario 3D All-Stars.
2020-10-12 17:39:14 -07:00
ReinUsesLisp
67af0323f0 video_core: Fix instances where msbuild always regenerated host shaders
When HEADER_GENERATOR was included in the DEPENDS section of custom
commands, msbuild assumed this was always modified. Changing this file
is not common so we can remove it from there.
2020-09-23 22:27:17 -03:00
ReinUsesLisp
c5a78f4480 vk_device: Use Vulkan 1.0 properly
Enable the required capabilities to use Vulkan 1.0 without validation
errors and disable those that are not compatible with it.
2020-08-20 16:55:22 -03:00
ReinUsesLisp
29a0ca2391 renderer_vulkan: Create a Vulkan 1.0 instance when 1.1 is not available
This commit doesn't make yuzu compatible with Vulkan 1.0 yet, it only
creates an 1.0 instance.
2020-08-20 16:55:22 -03:00
184 changed files with 6916 additions and 2371 deletions

2
.gitmodules vendored
View File

@@ -1,6 +1,6 @@
[submodule "inih"]
path = externals/inih/inih
url = https://github.com/svn2github/inih
url = https://github.com/benhoyt/inih.git
[submodule "cubeb"]
path = externals/cubeb
url = https://github.com/kinetiknz/cubeb.git

View File

@@ -161,7 +161,7 @@ macro(yuzu_find_packages)
# Cmake Pkg Prefix Version Conan Pkg
"Boost 1.73 boost/1.73.0"
"Catch2 2.13 catch2/2.13.0"
"fmt 7.0 fmt/7.0.3"
"fmt 7.1 fmt/7.1.0"
# can't use until https://github.com/bincrafters/community/issues/1173
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
"lz4 1.8 lz4/1.9.2"
@@ -263,6 +263,7 @@ if (CONAN_REQUIRED_LIBS)
libzip:with_openssl=False
libzip:enable_windows_crypto=False
)
conan_check(VERSION 1.24.0 REQUIRED)
# Add the bincrafters remote
conan_add_remote(NAME bincrafters
@@ -354,6 +355,19 @@ if (NOT LIBUSB_FOUND)
set(LIBUSB_LIBRARIES usb)
endif()
# Use system installed ffmpeg.
if (NOT MSVC)
find_package(FFmpeg REQUIRED)
else()
set(FFMPEG_EXT_NAME "ffmpeg-4.2.1")
set(FFMPEG_PATH "${CMAKE_BINARY_DIR}/externals/${FFMPEG_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFMPEG_EXT_NAME} "")
set(FFMPEG_FOUND YES)
set(FFMPEG_INCLUDE_DIR "${FFMPEG_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFMPEG_LIBRARY_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg library" FORCE)
set(FFMPEG_DLL_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
endif()
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

View File

@@ -0,0 +1,10 @@
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
)
endfunction(copy_yuzu_FFmpeg_deps)

View File

@@ -73,17 +73,20 @@ if (NOT LIBZIP_FOUND)
endif()
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl
DEFINITION OPENSSL_LIBS)
# lurlparser
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
else()
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl
DEFINITION OPENSSL_LIBS)
endif()
# httplib
add_library(httplib INTERFACE)

100
externals/find-modules/FindFFmpeg.cmake vendored Normal file
View File

@@ -0,0 +1,100 @@
# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
# Once done this will define
#
# FFMPEG_FOUND - system has ffmpeg or libav
# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
# FFMPEG_LIBRARIES - Link these to use ffmpeg
# FFMPEG_LIBAVCODEC
# FFMPEG_LIBAVFORMAT
# FFMPEG_LIBAVUTIL
#
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
# Modified for other libraries by Lasse Kärkkäinen <tronic>
# Modified for Hedgewars by Stepik777
# Modified for FFmpeg-example Tuukka Pasanen 2018
# Modified for yuzu toastUnlimted 2020
#
# Redistribution and use is allowed according to the terms of the New
# BSD license.
#
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FFMPEG
FOUND_VAR FFMPEG_FOUND
REQUIRED_VARS
FFMPEG_LIBRARY
FFMPEG_INCLUDE_DIR
VERSION_VAR FFMPEG_VERSION
)
if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
# in cache already
set(FFMPEG_FOUND TRUE)
else()
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
pkg_check_modules(_FFMPEG_AVUTIL libavutil)
pkg_check_modules(_FFMPEG_SWSCALE libswscale)
endif()
find_path(FFMPEG_AVCODEC_INCLUDE_DIR
NAMES libavcodec/avcodec.h
PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
/usr/include
/usr/local/include
/opt/local/include
/sw/include
PATH_SUFFIXES ffmpeg libav)
find_library(FFMPEG_LIBAVCODEC
NAMES avcodec
PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib)
find_library(FFMPEG_LIBAVUTIL
NAMES avutil
PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib)
find_library(FFMPEG_LIBSWSCALE
NAMES swscale
PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib)
if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVUTIL AND FFMPEG_LIBSWSCALE)
set(FFMPEG_FOUND TRUE)
endif()
if(FFMPEG_FOUND)
set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
set(FFMPEG_LIBRARIES
${FFMPEG_LIBAVCODEC}
${FFMPEG_LIBAVUTIL}
${FFMPEG_LIBSWSCALE})
endif()
if(FFMPEG_FOUND)
if(NOT FFMPEG_FIND_QUIETLY)
message(STATUS
"Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
endif()
else()
if(FFMPEG_FIND_REQUIRED)
message(FATAL_ERROR
"Could not find libavcodec or libavutil or libswscale")
endif()
endif()
endif()

View File

@@ -1,4 +1,4 @@
From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53
From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
MIT License

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
add_library(lurlparser
LUrlParser.cpp
LUrlParser.h
)
create_target_directory_groups(lurlparser)
target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -1,265 +0,0 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "LUrlParser.h"
#include <algorithm>
#include <cstring>
#include <stdlib.h>
// check if the scheme name is valid
static bool IsSchemeValid( const std::string& SchemeName )
{
for ( auto c : SchemeName )
{
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
}
return true;
}
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
{
if ( !IsValid() ) { return false; }
int Port = atoi( m_Port.c_str() );
if ( Port <= 0 || Port > 65535 ) { return false; }
if ( OutPort ) { *OutPort = Port; }
return true;
}
// based on RFC 1738 and RFC 3986
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
{
LUrlParser::clParseURL Result;
const char* CurrentString = URL.c_str();
/*
* <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
*/
// try to read scheme
{
const char* LocalString = strchr( CurrentString, ':' );
if ( !LocalString )
{
return clParseURL( LUrlParserError_NoUrlCharacter );
}
// save the scheme name
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
if ( !IsSchemeValid( Result.m_Scheme ) )
{
return clParseURL( LUrlParserError_InvalidSchemeName );
}
// scheme should be lowercase
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
// skip ':'
CurrentString = LocalString+1;
}
/*
* //<user>:<password>@<host>:<port>/<url-path>
* any ":", "@" and "/" must be normalized
*/
// skip "//"
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
// check if the user name and password are specified
bool bHasUserName = false;
const char* LocalString = CurrentString;
while ( *LocalString )
{
if ( *LocalString == '@' )
{
// user name and password are specified
bHasUserName = true;
break;
}
else if ( *LocalString == '/' )
{
// end of <host>:<port> specification
bHasUserName = false;
break;
}
LocalString++;
}
// user name and password
LocalString = CurrentString;
if ( bHasUserName )
{
// read user name
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
// proceed with the current pointer
CurrentString = LocalString;
if ( *CurrentString == ':' )
{
// skip ':'
CurrentString++;
// read password
LocalString = CurrentString;
while ( *LocalString && *LocalString != '@' ) LocalString++;
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// skip '@'
if ( *CurrentString != '@' )
{
return clParseURL( LUrlParserError_NoAtSign );
}
CurrentString++;
}
bool bHasBracket = ( *CurrentString == '[' );
// go ahead, read the host name
LocalString = CurrentString;
while ( *LocalString )
{
if ( bHasBracket && *LocalString == ']' )
{
// end of IPv6 address
LocalString++;
break;
}
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
{
// port number is specified
break;
}
LocalString++;
}
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
// is port number specified?
if ( *CurrentString == ':' )
{
CurrentString++;
// read port number
LocalString = CurrentString;
while ( *LocalString && *LocalString != '/' ) LocalString++;
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// end of string
if ( !*CurrentString )
{
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}
// skip '/'
if ( *CurrentString != '/' )
{
return clParseURL( LUrlParserError_NoSlash );
}
CurrentString++;
// parse the path
LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
// check for query
if ( *CurrentString == '?' )
{
// skip '?'
CurrentString++;
// read query
LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' ) LocalString++;
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// check for fragment
if ( *CurrentString == '#' )
{
// skip '#'
CurrentString++;
// read fragment
LocalString = CurrentString;
while ( *LocalString ) LocalString++;
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}

View File

@@ -1,78 +0,0 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <string>
namespace LUrlParser
{
enum LUrlParserError
{
LUrlParserError_Ok = 0,
LUrlParserError_Uninitialized = 1,
LUrlParserError_NoUrlCharacter = 2,
LUrlParserError_InvalidSchemeName = 3,
LUrlParserError_NoDoubleSlash = 4,
LUrlParserError_NoAtSign = 5,
LUrlParserError_UnexpectedEndOfLine = 6,
LUrlParserError_NoSlash = 7,
};
class clParseURL
{
public:
LUrlParserError m_ErrorCode;
std::string m_Scheme;
std::string m_Host;
std::string m_Port;
std::string m_Path;
std::string m_Query;
std::string m_Fragment;
std::string m_UserName;
std::string m_Password;
clParseURL()
: m_ErrorCode( LUrlParserError_Uninitialized )
{}
/// return 'true' if the parsing was successful
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
bool GetPort( int* OutPort ) const;
/// parse the URL
static clParseURL ParseURL( const std::string& URL );
private:
explicit clParseURL( LUrlParserError ErrorCode )
: m_ErrorCode( ErrorCode )
{}
};
} // namespace LUrlParser

View File

@@ -1,19 +0,0 @@
From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
MIT License
===
Lightweight URL & URI parser (RFC 1738, RFC 3986)
(C) Sergey Kosarevsky, 2015
@corporateshark sk@linderdaum.com
http://www.linderdaum.com
http://blog.linderdaum.com
=============================
A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.

View File

@@ -902,8 +902,10 @@ inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
#include <windows.h>
#define snprintf _snprintf
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#endif
int64_t MicroProfileTicksPerSecondCpu()
{
static int64_t nTicksPerSecond = 0;
@@ -946,7 +948,11 @@ typedef HANDLE MicroProfileThread;
DWORD _stdcall ThreadTrampoline(void* pFunc)
{
MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc;
return (uint32_t)F(0);
// The return value of F will always return a void*, however, this is for
// compatibility with pthreads. The underlying "address" of the pointer
// is always a 32-bit value, so this cast is safe to perform.
return static_cast<DWORD>(reinterpret_cast<uint64_t>(F(0)));
}
inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
@@ -1742,10 +1748,10 @@ void MicroProfileFlip()
}
}
}
for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
{
pLog->nGroupTicks[i] += nGroupTicks[i];
pFrameGroup[i] += nGroupTicks[i];
pLog->nGroupTicks[j] += nGroupTicks[j];
pFrameGroup[j] += nGroupTicks[j];
}
pLog->nStackPos = nStackPos;
}
@@ -3328,7 +3334,7 @@ bool MicroProfileIsLocalThread(uint32_t nThreadId)
#endif
#else
bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;}
bool MicroProfileIsLocalThread([[maybe_unused]] uint32_t nThreadId) { return false; }
void MicroProfileStopContextSwitchTrace(){}
void MicroProfileStartContextSwitchTrace(){}
@@ -3576,7 +3582,7 @@ int MicroProfileGetGpuTickReference(int64_t* pOutCpu, int64_t* pOutGpu)
#undef S
#ifdef _WIN32
#ifdef _MSC_VER
#pragma warning(pop)
#endif

View File

@@ -32,7 +32,6 @@ if (MSVC)
# /Zc:inline - Let codegen omit inline functions in object files
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
add_compile_options(
/W3
/MP
/Zi
/Zo
@@ -43,6 +42,13 @@ if (MSVC)
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
# Warnings
/W3
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
/we4555 # Expression has no effect; expected expression with side-effect
/we4834 # Discarding return value of function with 'nodiscard' attribute
)
# /GS- - No stack buffer overflow checks
@@ -56,6 +62,7 @@ else()
-Werror=implicit-fallthrough
-Werror=missing-declarations
-Werror=reorder
-Werror=unused-result
-Wextra
-Wmissing-declarations
-Wno-attributes

View File

@@ -51,9 +51,12 @@ if (NOT MSVC)
-Werror=implicit-fallthrough
-Werror=reorder
-Werror=sign-compare
-Werror=unused-but-set-parameter
-Werror=unused-but-set-variable
-Werror=unused-variable
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
-Wno-sign-conversion
)
endif()

View File

@@ -150,6 +150,8 @@ add_library(common STATIC
scope_exit.h
spin_lock.cpp
spin_lock.h
stream.cpp
stream.h
string_util.cpp
string_util.h
swap.h
@@ -188,6 +190,22 @@ if(ARCHITECTURE_x86_64)
)
endif()
if (MSVC)
target_compile_definitions(common PRIVATE
# The standard library doesn't provide any replacement for codecvt yet
# so we can disable this deprecation warning for the time being.
_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
)
target_compile_options(common PRIVATE
/W4
/WX
)
else()
target_compile_options(common PRIVATE
-Werror
)
endif()
create_target_directory_groups(common)
find_package(Boost 1.71 COMPONENTS context headers REQUIRED)

View File

@@ -4,6 +4,8 @@
#include "common/assert.h"
#include "common/fiber.h"
#include "common/spin_lock.h"
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
#else
@@ -14,18 +16,45 @@ namespace Common {
constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
#if defined(_WIN32) || defined(WIN32)
struct Fiber::FiberImpl {
SpinLock guard{};
std::function<void(void*)> entry_point;
std::function<void(void*)> rewind_point;
void* rewind_parameter{};
void* start_parameter{};
std::shared_ptr<Fiber> previous_fiber;
bool is_thread_fiber{};
bool released{};
#if defined(_WIN32) || defined(WIN32)
LPVOID handle = nullptr;
LPVOID rewind_handle = nullptr;
#else
alignas(64) std::array<u8, default_stack_size> stack;
alignas(64) std::array<u8, default_stack_size> rewind_stack;
u8* stack_limit;
u8* rewind_stack_limit;
boost::context::detail::fcontext_t context;
boost::context::detail::fcontext_t rewind_context;
#endif
};
void Fiber::SetStartParameter(void* new_parameter) {
impl->start_parameter = new_parameter;
}
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
impl->rewind_point = std::move(rewind_func);
impl->rewind_parameter = rewind_param;
}
#if defined(_WIN32) || defined(WIN32)
void Fiber::Start() {
ASSERT(previous_fiber != nullptr);
previous_fiber->guard.unlock();
previous_fiber.reset();
entry_point(start_parameter);
ASSERT(impl->previous_fiber != nullptr);
impl->previous_fiber->impl->guard.unlock();
impl->previous_fiber.reset();
impl->entry_point(impl->start_parameter);
UNREACHABLE();
}
@@ -34,99 +63,86 @@ void Fiber::OnRewind() {
DeleteFiber(impl->handle);
impl->handle = impl->rewind_handle;
impl->rewind_handle = nullptr;
rewind_point(rewind_parameter);
impl->rewind_point(impl->rewind_parameter);
UNREACHABLE();
}
void Fiber::FiberStartFunc(void* fiber_parameter) {
auto fiber = static_cast<Fiber*>(fiber_parameter);
auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->Start();
}
void Fiber::RewindStartFunc(void* fiber_parameter) {
auto fiber = static_cast<Fiber*>(fiber_parameter);
auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->OnRewind();
}
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
impl = std::make_unique<FiberImpl>();
: impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func);
impl->start_parameter = start_parameter;
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (released) {
if (impl->released) {
return;
}
// Make sure the Fiber is not being used
const bool locked = guard.try_lock();
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
guard.unlock();
impl->guard.unlock();
}
DeleteFiber(impl->handle);
}
void Fiber::Exit() {
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
if (!is_thread_fiber) {
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
if (!impl->is_thread_fiber) {
return;
}
ConvertFiberToThread();
guard.unlock();
released = true;
}
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
rewind_point = std::move(rewind_func);
rewind_parameter = start_parameter;
impl->guard.unlock();
impl->released = true;
}
void Fiber::Rewind() {
ASSERT(rewind_point);
ASSERT(impl->rewind_point);
ASSERT(impl->rewind_handle == nullptr);
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
SwitchToFiber(impl->rewind_handle);
}
void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->guard.lock();
to->previous_fiber = from;
to->impl->guard.lock();
to->impl->previous_fiber = from;
SwitchToFiber(to->impl->handle);
ASSERT(from->previous_fiber != nullptr);
from->previous_fiber->guard.unlock();
from->previous_fiber.reset();
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
fiber->guard.lock();
fiber->impl->guard.lock();
fiber->impl->handle = ConvertThreadToFiber(nullptr);
fiber->is_thread_fiber = true;
fiber->impl->is_thread_fiber = true;
return fiber;
}
#else
struct Fiber::FiberImpl {
alignas(64) std::array<u8, default_stack_size> stack;
alignas(64) std::array<u8, default_stack_size> rewind_stack;
u8* stack_limit;
u8* rewind_stack_limit;
boost::context::detail::fcontext_t context;
boost::context::detail::fcontext_t rewind_context;
};
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
ASSERT(previous_fiber != nullptr);
previous_fiber->impl->context = transfer.fctx;
previous_fiber->guard.unlock();
previous_fiber.reset();
entry_point(start_parameter);
ASSERT(impl->previous_fiber != nullptr);
impl->previous_fiber->impl->context = transfer.fctx;
impl->previous_fiber->impl->guard.unlock();
impl->previous_fiber.reset();
impl->entry_point(impl->start_parameter);
UNREACHABLE();
}
@@ -137,23 +153,24 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
u8* tmp = impl->stack_limit;
impl->stack_limit = impl->rewind_stack_limit;
impl->rewind_stack_limit = tmp;
rewind_point(rewind_parameter);
impl->rewind_point(impl->rewind_parameter);
UNREACHABLE();
}
void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
auto fiber = static_cast<Fiber*>(transfer.data);
auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->Start(transfer);
}
void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
auto fiber = static_cast<Fiber*>(transfer.data);
auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->OnRewind(transfer);
}
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
impl = std::make_unique<FiberImpl>();
: impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func);
impl->start_parameter = start_parameter;
impl->stack_limit = impl->stack.data();
impl->rewind_stack_limit = impl->rewind_stack.data();
u8* stack_base = impl->stack_limit + default_stack_size;
@@ -161,37 +178,31 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete
boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
}
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
rewind_point = std::move(rewind_func);
rewind_parameter = start_parameter;
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (released) {
if (impl->released) {
return;
}
// Make sure the Fiber is not being used
const bool locked = guard.try_lock();
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
guard.unlock();
impl->guard.unlock();
}
}
void Fiber::Exit() {
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
if (!is_thread_fiber) {
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
if (!impl->is_thread_fiber) {
return;
}
guard.unlock();
released = true;
impl->guard.unlock();
impl->released = true;
}
void Fiber::Rewind() {
ASSERT(rewind_point);
ASSERT(impl->rewind_point);
ASSERT(impl->rewind_context == nullptr);
u8* stack_base = impl->rewind_stack_limit + default_stack_size;
impl->rewind_context =
@@ -199,22 +210,22 @@ void Fiber::Rewind() {
boost::context::detail::jump_fcontext(impl->rewind_context, this);
}
void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->guard.lock();
to->previous_fiber = from;
to->impl->guard.lock();
to->impl->previous_fiber = from;
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
ASSERT(from->previous_fiber != nullptr);
from->previous_fiber->impl->context = transfer.fctx;
from->previous_fiber->guard.unlock();
from->previous_fiber.reset();
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
fiber->guard.lock();
fiber->is_thread_fiber = true;
fiber->impl->guard.lock();
fiber->impl->is_thread_fiber = true;
return fiber;
}

View File

@@ -7,9 +7,6 @@
#include <functional>
#include <memory>
#include "common/common_types.h"
#include "common/spin_lock.h"
#if !defined(_WIN32) && !defined(WIN32)
namespace boost::context::detail {
struct transfer_t;
@@ -46,10 +43,10 @@ public:
/// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber.
static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
void Rewind();
@@ -57,9 +54,7 @@ public:
void Exit();
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
void SetStartParameter(void* new_parameter) {
start_parameter = new_parameter;
}
void SetStartParameter(void* new_parameter);
private:
Fiber();
@@ -77,16 +72,7 @@ private:
#endif
struct FiberImpl;
SpinLock guard{};
std::function<void(void*)> entry_point;
std::function<void(void*)> rewind_point;
void* rewind_parameter{};
void* start_parameter{};
std::shared_ptr<Fiber> previous_fiber;
std::unique_ptr<FiberImpl> impl;
bool is_thread_fiber{};
bool released{};
};
} // namespace Common

View File

@@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
}
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
const auto callback = [recursion](u64* num_entries_out, const std::string& directory,
const std::string& virtual_name) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
const auto callback = [recursion](u64*, const std::string& directory,
const std::string& virtual_name) {
const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
if (IsDirectory(new_path)) {
if (recursion == 0)
if (recursion == 0) {
return false;
}
return DeleteDirRecursively(new_path, recursion - 1);
}
return Delete(new_path);
@@ -492,7 +493,8 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
return true;
}
void CopyDir(const std::string& source_path, const std::string& dest_path) {
void CopyDir([[maybe_unused]] const std::string& source_path,
[[maybe_unused]] const std::string& dest_path) {
#ifndef _WIN32
if (source_path == dest_path) {
return;
@@ -553,7 +555,7 @@ std::optional<std::string> GetCurrentDir() {
std::string strDir = dir;
#endif
free(dir);
return std::move(strDir);
return strDir;
}
bool SetCurrentDir(const std::string& directory) {
@@ -772,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension) {
const std::string forbidden_characters = ".\"/\\[]:;=, ";
static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
// On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
extension = {{' ', ' ', ' ', '\0'}};
std::string::size_type point = filename.rfind('.');
if (point == filename.size() - 1)
auto point = filename.rfind('.');
if (point == filename.size() - 1) {
point = filename.rfind('.', point);
}
// Get short name.
int j = 0;
for (char letter : filename.substr(0, point)) {
if (forbidden_characters.find(letter, 0) != std::string::npos)
if (forbidden_characters.find(letter, 0) != std::string::npos) {
continue;
}
if (j == 8) {
// TODO(Link Mauve): also do that for filenames containing a space.
// TODO(Link Mauve): handle multiple files having the same short name.
@@ -794,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
short_name[7] = '1';
break;
}
short_name[j++] = toupper(letter);
short_name[j++] = static_cast<char>(std::toupper(letter));
}
// Get extension.
if (point != std::string::npos) {
j = 0;
for (char letter : filename.substr(point + 1, 3))
extension[j++] = toupper(letter);
for (char letter : filename.substr(point + 1, 3)) {
extension[j++] = static_cast<char>(std::toupper(letter));
}
}
}

View File

@@ -232,7 +232,7 @@ public:
void Swap(IOFile& other) noexcept;
[[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0);
bool Open(const std::string& filename, const char openmode[], int flags = 0);
bool Close();
template <typename T>

View File

@@ -274,7 +274,6 @@ const char* GetLogClassName(Class log_class) {
case Class::Count:
break;
}
UNREACHABLE();
return "Invalid";
}
@@ -293,7 +292,6 @@ const char* GetLevelName(Level log_level) {
break;
}
#undef LVL
UNREACHABLE();
return "Invalid";
}

View File

@@ -20,14 +20,14 @@ struct Rectangle {
constexpr Rectangle() = default;
constexpr Rectangle(T left, T top, T right, T bottom)
: left(left), top(top), right(right), bottom(bottom) {}
constexpr Rectangle(T left_, T top_, T right_, T bottom_)
: left(left_), top(top_), right(right_), bottom(bottom_) {}
[[nodiscard]] T GetWidth() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(right - left);
} else {
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(right - left)));
}
}
@@ -35,7 +35,7 @@ struct Rectangle {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(bottom - top);
} else {
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(bottom - top)));
}
}

View File

@@ -16,16 +16,23 @@
// Call directly after the command or use the error num.
// This function might change the error code.
std::string GetLastErrorMsg() {
static const std::size_t buff_size = 255;
static constexpr std::size_t buff_size = 255;
char err_str[buff_size];
#ifdef _WIN32
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
return std::string(err_str, buff_size);
#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
// Thread safe (GNU-specific)
const char* str = strerror_r(errno, err_str, buff_size);
return std::string(str);
#else
// Thread safe (XSI-compliant)
strerror_r(errno, err_str, buff_size);
const int success = strerror_r(errno, err_str, buff_size);
if (success != 0) {
return {};
}
return std::string(err_str);
#endif
return std::string(err_str, buff_size);
}

View File

@@ -15,6 +15,14 @@ namespace Common {
*/
class SpinLock {
public:
SpinLock() = default;
SpinLock(const SpinLock&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
SpinLock(SpinLock&&) = delete;
SpinLock& operator=(SpinLock&&) = delete;
void lock();
void unlock();
[[nodiscard]] bool try_lock();

47
src/common/stream.cpp Normal file
View File

@@ -0,0 +1,47 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <stdexcept>
#include "common/common_types.h"
#include "common/stream.h"
namespace Common {
Stream::Stream() = default;
Stream::~Stream() = default;
void Stream::Seek(s32 offset, SeekOrigin origin) {
if (origin == SeekOrigin::SetOrigin) {
if (offset < 0) {
position = 0;
} else if (position >= buffer.size()) {
position = buffer.size();
} else {
position = offset;
}
} else if (origin == SeekOrigin::FromCurrentPos) {
Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
} else if (origin == SeekOrigin::FromEnd) {
Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
}
}
u8 Stream::ReadByte() {
if (position < buffer.size()) {
return buffer[position++];
} else {
throw std::out_of_range("Attempting to read a byte not within the buffer range");
}
}
void Stream::WriteByte(u8 byte) {
if (position == buffer.size()) {
buffer.push_back(byte);
position++;
} else {
buffer.insert(buffer.begin() + position, byte);
}
}
} // namespace Common

56
src/common/stream.h Normal file
View File

@@ -0,0 +1,56 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common {
enum class SeekOrigin {
SetOrigin,
FromCurrentPos,
FromEnd,
};
class Stream {
public:
/// Stream creates a bitstream and provides common functionality on the stream.
explicit Stream();
~Stream();
Stream(const Stream&) = delete;
Stream& operator=(const Stream&) = delete;
Stream(Stream&&) = default;
Stream& operator=(Stream&&) = default;
/// Reposition bitstream "cursor" to the specified offset from origin
void Seek(s32 offset, SeekOrigin origin);
/// Reads next byte in the stream buffer and increments position
u8 ReadByte();
/// Writes byte at current position
void WriteByte(u8 byte);
[[nodiscard]] std::size_t GetPosition() const {
return position;
}
[[nodiscard]] std::vector<u8>& GetBuffer() {
return buffer;
}
[[nodiscard]] const std::vector<u8>& GetBuffer() const {
return buffer;
}
private:
std::vector<u8> buffer;
std::size_t position{0};
};
} // namespace Common

View File

@@ -8,6 +8,7 @@
#include <cstdlib>
#include <locale>
#include <sstream>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -21,14 +22,14 @@ namespace Common {
/// Make a string lowercase
std::string ToLower(std::string str) {
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c) { return std::tolower(c); });
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return str;
}
/// Make a string uppercase
std::string ToUpper(std::string str) {
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c) { return std::toupper(c); });
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
return str;
}

View File

@@ -142,20 +142,18 @@ std::string Timer::GetTimeFormatted() {
// ----------------
double Timer::GetDoubleTime() {
// Get continuous timestamp
u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count());
double ms = static_cast<u64>(GetTimeMs().count()) % 1000;
auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
// Remove a few years. We only really want enough seconds to make
// sure that we are detecting actual actions, perhaps 60 seconds is
// enough really, but I leave a year of seconds anyway, in case the
// user's clock is incorrect or something like that.
TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
// Make a smaller integer that fits in the double
u32 Seconds = static_cast<u32>(TmpSeconds);
double TmpTime = Seconds + ms;
return TmpTime;
const auto seconds = static_cast<u32>(tmp_seconds);
return seconds + ms;
}
} // Namespace Common

View File

@@ -87,7 +87,13 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f};
using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
};
}
template <typename V>
@@ -98,7 +104,13 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f};
using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
};
}
template <typename V>
@@ -168,7 +180,10 @@ public:
template <typename T, typename V>
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
return Vec2<T>(f * vec.x, f * vec.y);
using C = std::common_type_t<T, V>;
return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
}
using Vec2f = Vec2<float>;
@@ -237,7 +252,14 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f};
using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
};
}
template <typename V>
@@ -247,7 +269,14 @@ public:
}
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f};
using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
};
}
template <typename V>
@@ -367,7 +396,11 @@ public:
template <typename T, typename V>
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
using C = std::common_type_t<T, V>;
return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
}
template <>
@@ -446,7 +479,15 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f, w * f};
using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
};
}
template <typename V>
@@ -457,7 +498,15 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f, w / f};
using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
};
}
template <typename V>
@@ -582,7 +631,15 @@ public:
template <typename T, typename V>
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
using TV = decltype(V{} * T{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
};
}
using Vec4f = Vec4<float>;

View File

@@ -53,7 +53,7 @@ public:
return Common::Divide128On32(temporary, 1000000000).first;
}
void Pause(bool is_paused) override {
void Pause([[maybe_unused]] bool is_paused) override {
// Do nothing in this clock type.
}

View File

@@ -34,7 +34,7 @@ private:
/// value used to reduce the native clocks accuracy as some apss rely on
/// undefined behavior where the level of accuracy in the clock shouldn't
/// be higher.
static constexpr u64 inaccuracy_mask = ~(0x400 - 1);
static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
SpinLock rtsc_serialize{};
u64 last_measure{};

View File

@@ -1,9 +1,3 @@
if (YUZU_ENABLE_BOXCAT)
set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h)
else()
set(BCAT_BOXCAT_ADDITIONAL_SOURCES)
endif()
add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp
@@ -303,7 +297,6 @@ add_library(core STATIC
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
${BCAT_BOXCAT_ADDITIONAL_SOURCES}
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/module.cpp
@@ -446,6 +439,8 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvhost_gpu.h
hle/service/nvdrv/devices/nvhost_nvdec.cpp
hle/service/nvdrv/devices/nvhost_nvdec.h
hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
hle/service/nvdrv/devices/nvhost_nvdec_common.h
hle/service/nvdrv/devices/nvhost_nvjpg.cpp
hle/service/nvdrv/devices/nvhost_nvjpg.h
hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -459,6 +454,8 @@ add_library(core STATIC
hle/service/nvdrv/nvdrv.h
hle/service/nvdrv/nvmemp.cpp
hle/service/nvdrv/nvmemp.h
hle/service/nvdrv/syncpoint_manager.cpp
hle/service/nvdrv/syncpoint_manager.h
hle/service/nvflinger/buffer_queue.cpp
hle/service/nvflinger/buffer_queue.h
hle/service/nvflinger/nvflinger.cpp
@@ -608,6 +605,13 @@ add_library(core STATIC
tools/freezer.h
)
if (YUZU_ENABLE_BOXCAT)
target_sources(core PRIVATE
hle/service/bcat/backend/boxcat.cpp
hle/service/bcat/backend/boxcat.h
)
endif()
if (MSVC)
target_compile_options(core PRIVATE
# 'expression' : signed/unsigned mismatch
@@ -630,9 +634,12 @@ else()
-Werror=implicit-fallthrough
-Werror=reorder
-Werror=sign-compare
-Werror=unused-but-set-parameter
-Werror=unused-but-set-variable
-Werror=unused-variable
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
-Wno-sign-conversion
)
endif()

View File

@@ -147,10 +147,18 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
auto fp = ctx.cpu_registers[29];
auto lr = ctx.cpu_registers[30];
while (true) {
out.push_back({"", 0, lr, 0});
if (!fp) {
out.push_back({
.module = "",
.address = 0,
.original_address = lr,
.offset = 0,
.name = {},
});
if (fp == 0) {
break;
}
lr = memory.Read64(fp + 8) - 4;
fp = memory.Read64(fp);
}

View File

@@ -40,6 +40,7 @@
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
@@ -121,7 +122,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system},
cpu_manager{system}, reporter{system}, applet_manager{system} {}
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
ResultStatus Run() {
status = ResultStatus::Success;
@@ -178,16 +179,21 @@ struct System::Impl {
arp_manager.ResetAll();
telemetry_session = std::make_unique<Core::TelemetrySession>();
gpu_core = VideoCore::CreateGPU(emu_window, system);
if (!gpu_core) {
return ResultStatus::ErrorVideoCore;
}
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
Service::Init(service_manager, system);
GDBStub::DeferStart();
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(emu_window, system);
if (!gpu_core) {
return ResultStatus::ErrorVideoCore;
}
// Initialize time manager, which must happen after kernel is created
time_manager.Initialize();
is_powered_on = true;
exit_lock = false;
@@ -387,6 +393,7 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::LM::Manager lm_manager{reporter};
Service::Time::TimeManager time_manager;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -717,6 +724,14 @@ const Service::LM::Manager& System::GetLogManager() const {
return impl->lm_manager;
}
Service::Time::TimeManager& System::GetTimeManager() {
return impl->time_manager;
}
const Service::Time::TimeManager& System::GetTimeManager() const {
return impl->time_manager;
}
void System::SetExitLock(bool locked) {
impl->exit_lock = locked;
}

View File

@@ -69,6 +69,10 @@ namespace SM {
class ServiceManager;
} // namespace SM
namespace Time {
class TimeManager;
} // namespace Time
} // namespace Service
namespace Tegra {
@@ -361,6 +365,10 @@ public:
const Service::LM::Manager& GetLogManager() const;
Service::Time::TimeManager& GetTimeManager();
const Service::Time::TimeManager& GetTimeManager() const;
void SetExitLock(bool locked);
bool GetExitLock() const;

View File

@@ -365,6 +365,8 @@ void CpuManager::RunThread(std::size_t core) {
data.enter_barrier.reset();
data.exit_barrier.reset();
data.initialized = false;
MicroProfileOnThreadExit();
}
} // namespace Core

View File

@@ -19,7 +19,7 @@ DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& se
DefaultControllerApplet::~DefaultControllerApplet() = default;
void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
ControllerParameters parameters) const {
const ControllerParameters& parameters) const {
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
auto& npad =

View File

@@ -38,7 +38,7 @@ public:
virtual ~ControllerApplet();
virtual void ReconfigureControllers(std::function<void()> callback,
ControllerParameters parameters) const = 0;
const ControllerParameters& parameters) const = 0;
};
class DefaultControllerApplet final : public ControllerApplet {
@@ -47,7 +47,7 @@ public:
~DefaultControllerApplet() override;
void ReconfigureControllers(std::function<void()> callback,
ControllerParameters parameters) const override;
const ControllerParameters& parameters) const override;
private:
Service::SM::ServiceManager& service_manager;

View File

@@ -7,7 +7,6 @@
#include <bitset>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <utility>
@@ -87,8 +86,6 @@ struct KernelCore::Impl {
}
cores.clear();
registered_core_threads.reset();
process_list.clear();
current_process = nullptr;
@@ -107,7 +104,11 @@ struct KernelCore::Impl {
cores.clear();
exclusive_monitor.reset();
host_thread_ids.clear();
num_host_threads = 0;
std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(),
std::thread::id{});
std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
}
void InitializePhysicalCores() {
@@ -177,54 +178,58 @@ struct KernelCore::Impl {
void MakeCurrentProcess(Process* process) {
current_process = process;
if (process == nullptr) {
return;
}
u32 core_id = GetCurrentHostThreadID();
const u32 core_id = GetCurrentHostThreadID();
if (core_id < Core::Hardware::NUM_CPU_CORES) {
system.Memory().SetCurrentPageTable(*process, core_id);
}
}
void RegisterCoreThread(std::size_t core_id) {
std::unique_lock lock{register_thread_mutex};
if (!is_multicore) {
single_core_thread_id = std::this_thread::get_id();
}
const std::thread::id this_id = std::this_thread::get_id();
const auto it = host_thread_ids.find(this_id);
if (!is_multicore) {
single_core_thread_id = this_id;
}
const auto end =
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
ASSERT(it == host_thread_ids.end());
ASSERT(!registered_core_threads[core_id]);
host_thread_ids[this_id] = static_cast<u32>(core_id);
registered_core_threads.set(core_id);
ASSERT(it == end);
InsertHostThread(static_cast<u32>(core_id));
}
void RegisterHostThread() {
std::unique_lock lock{register_thread_mutex};
const std::thread::id this_id = std::this_thread::get_id();
const auto it = host_thread_ids.find(this_id);
if (it != host_thread_ids.end()) {
return;
const auto end =
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
if (it == end) {
InsertHostThread(registered_thread_ids++);
}
host_thread_ids[this_id] = registered_thread_ids++;
}
u32 GetCurrentHostThreadID() const {
void InsertHostThread(u32 value) {
const size_t index = num_host_threads++;
ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
register_host_thread_values[index] = value;
register_host_thread_keys[index] = std::this_thread::get_id();
}
[[nodiscard]] u32 GetCurrentHostThreadID() const {
const std::thread::id this_id = std::this_thread::get_id();
if (!is_multicore) {
if (single_core_thread_id == this_id) {
return static_cast<u32>(system.GetCpuManager().CurrentCore());
}
if (!is_multicore && single_core_thread_id == this_id) {
return static_cast<u32>(system.GetCpuManager().CurrentCore());
}
std::unique_lock lock{register_thread_mutex};
const auto it = host_thread_ids.find(this_id);
if (it == host_thread_ids.end()) {
const auto end =
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
if (it == end) {
return Core::INVALID_HOST_THREAD_ID;
}
return it->second;
return register_host_thread_values[static_cast<size_t>(
std::distance(register_host_thread_keys.begin(), it))];
}
Core::EmuThreadHandle GetCurrentEmuThreadID() const {
@@ -322,10 +327,14 @@ struct KernelCore::Impl {
std::vector<Kernel::PhysicalCore> cores;
// 0-3 IDs represent core threads, >3 represent others
std::unordered_map<std::thread::id, u32> host_thread_ids;
u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
mutable std::mutex register_thread_mutex;
std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
// Number of host threads is a relatively high number to avoid overflowing
static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
std::atomic<size_t> num_host_threads{0};
std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
register_host_thread_keys{};
std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
// Kernel memory management
std::unique_ptr<Memory::MemoryManager> memory_manager;

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include <bitset>
#include <ctime>
#include <memory>
#include <random>
#include "common/alignment.h"
@@ -123,7 +124,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
: kernel.CreateNewUserProcessID();
process->capabilities.InitializeForMetadatalessProcess();
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr)));
std::uniform_int_distribution<u64> distribution;
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
[&] { return distribution(rng); });

View File

@@ -1201,6 +1201,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{151, nullptr, "TryPopFromNotificationStorageChannel"},
{160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
{170, nullptr, "SetHdcpAuthenticationActivated"},
{180, nullptr, "GetLaunchRequiredVersion"},
{181, nullptr, "UpgradeLaunchRequiredVersion"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, nullptr, "PrepareForJit"},

View File

@@ -84,7 +84,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
void ProgressServiceBackend::SignalUpdate() const {
if (need_hle_lock) {
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
std::lock_guard lock(HLE::g_hle_lock);
event.writable->Signal();
} else {
event.writable->Signal();

View File

@@ -454,6 +454,16 @@ Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
{std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
};
if (!client.is_valid()) {
LOG_ERROR(Service_BCAT, "Client is invalid, going offline!");
return StatusResult::Offline;
}
if (!client.is_socket_open()) {
LOG_ERROR(Service_BCAT, "Failed to open socket, going offline!");
return StatusResult::Offline;
}
const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
if (response == nullptr)
return StatusResult::Offline;

View File

@@ -41,7 +41,7 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
{130, nullptr, "PrecheckToCreateContentsForApplication"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
{142, nullptr, "GetAlbumFileList3AaeAruid"},
{142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
};
@@ -77,17 +77,24 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c
// TODO: Update this when we implement the album.
// Currently we do not have a method of accessing album entries, set this to 0 for now.
constexpr s32 total_entries{0};
constexpr u32 total_entries_1{};
constexpr u32 total_entries_2{};
LOG_WARNING(Service_Capture,
"(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
"end_posix_time={}, applet_resource_user_id={}, total_entries={}",
pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
total_entries);
LOG_WARNING(
Service_Capture,
"(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
"end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
total_entries_1, total_entries_2);
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(total_entries);
rb.Push(total_entries_1);
rb.Push(total_entries_2);
}
void CAPS_U::GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx) {
GetAlbumContentsFileListForApplication(ctx);
}
} // namespace Service::Capture

View File

@@ -20,6 +20,7 @@ public:
private:
void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
void GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture

View File

@@ -79,7 +79,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
}
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
@@ -93,8 +93,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) {
dir = backing;
}
auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this

View File

@@ -260,7 +260,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{404, nullptr, "HasLeftRightBattery"},
{405, nullptr, "GetNpadInterfaceType"},
{406, nullptr, "GetNpadLeftRightInterfaceType"},
{407, nullptr, "GetNpadOfHighestBatteryLevelForJoyLeft"},
{407, nullptr, "GetNpadOfHighestBatteryLevel"},
{408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
{500, nullptr, "GetPalmaConnectionHandle"},
{501, nullptr, "InitializePalma"},

View File

@@ -23,7 +23,7 @@ namespace Service::LDR {
constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
@@ -33,7 +33,7 @@ constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
@@ -166,7 +166,7 @@ public:
{0, &RelocatableObject::LoadNro, "LoadNro"},
{1, &RelocatableObject::UnloadNro, "UnloadNro"},
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
{3, nullptr, "UnloadNrr"},
{3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
{4, &RelocatableObject::Initialize, "Initialize"},
{10, nullptr, "LoadNrrEx"},
};
@@ -272,6 +272,20 @@ public:
rb.Push(RESULT_SUCCESS);
}
void UnloadNrr(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto pid = rp.Pop<u64>();
const auto nrr_address = rp.Pop<VAddr>();
LOG_DEBUG(Service_LDR, "called with pid={}, nrr_address={:016X}", pid, nrr_address);
nrr.erase(nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start,
std::size_t size) const {
constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize};

View File

@@ -131,7 +131,7 @@ template <typename T>
T GetRandomValue(T min, T max) {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(0, static_cast<u64>(max));
std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max));
return static_cast<T>(distribution(gen));
}

View File

@@ -47,6 +47,7 @@ public:
{23, nullptr, "Convert"},
{24, nullptr, "ConvertCoreDataToCharInfo"},
{25, nullptr, "ConvertCharInfoToCoreData"},
{26, nullptr, "Append"},
};
// clang-format on

View File

@@ -62,7 +62,7 @@ public:
{18, nullptr, "SetRequirementByRevision"},
{19, nullptr, "GetRequirement"},
{20, nullptr, "GetRevision"},
{21, nullptr, "GetAppletInfo"},
{21, &IRequest::GetAppletInfo, "GetAppletInfo"},
{22, nullptr, "GetAdditionalInfo"},
{23, nullptr, "SetKeptInSleep"},
{24, nullptr, "RegisterSocketDescriptor"},
@@ -125,6 +125,16 @@ private:
rb.Push(RESULT_SUCCESS);
}
void GetAppletInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 8};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
rb.Push<u32>(0);
rb.Push<u32>(0);
}
Kernel::EventPair event1, event2;
};

View File

@@ -50,19 +50,9 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
};
constexpr std::array<const char*, 7> SHARED_FONTS_TTF{
"FontStandard.ttf",
"FontChineseSimplified.ttf",
"FontExtendedChineseSimplified.ttf",
"FontChineseTraditional.ttf",
"FontKorean.ttf",
"FontNintendoExtended.ttf",
"FontNintendoExtended2.ttf",
};
// The below data is specific to shared font data dumped from Switch on f/w 2.2
// Virtual address and offsets/sizes likely will vary by dump
constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};

View File

@@ -46,6 +46,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
return GetVARegions(input, output);
case IoctlCommand::IocUnmapBufferCommand:
return UnmapBuffer(input, output);
case IoctlCommand::IocFreeSpaceCommand:
return FreeSpace(input, output);
default:
break;
}
@@ -91,6 +93,20 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
return result;
}
u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlFreeSpace params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
params.pages, params.page_size);
system.GPU().MemoryManager().Unmap(params.offset,
static_cast<std::size_t>(params.pages) * params.page_size);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::Success;
}
u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
const auto num_entries = input.size() / sizeof(IoctlRemapEntry);

View File

@@ -82,6 +82,7 @@ private:
IocBindChannelCommand = 0x40044101,
IocGetVaRegionsCommand = 0xC0404108,
IocUnmapBufferCommand = 0xC0084105,
IocFreeSpaceCommand = 0xC0104103,
};
struct IoctlInitalizeEx {
@@ -107,6 +108,13 @@ private:
};
static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
struct IoctlFreeSpace {
u64_le offset;
u32_le pages;
u32_le page_size;
};
static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
struct IoctlRemapEntry {
u16_le flags;
u16_le kind;
@@ -162,6 +170,7 @@ private:
u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);

View File

@@ -15,8 +15,9 @@
namespace Service::Nvidia::Devices {
nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
: nvdevice(system), events_interface{events_interface} {}
nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
SyncpointManager& syncpoint_manager)
: nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
nvhost_ctrl::~nvhost_ctrl() = default;
u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -36,8 +37,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
return IocCtrlEventRegister(input, output);
case IoctlCommand::IocCtrlEventUnregisterCommand:
return IocCtrlEventUnregister(input, output);
case IoctlCommand::IocCtrlEventSignalCommand:
return IocCtrlEventSignal(input, output);
case IoctlCommand::IocCtrlClearEventWaitCommand:
return IocCtrlClearEventWait(input, output);
default:
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
@@ -70,19 +71,33 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter;
}
if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
}
if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
params.value = new_value;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
}
auto event = events_interface.events[event_id];
auto& gpu = system.GPU();
// This is mostly to take into account unimplemented features. As synced
// gpu is always synced.
if (!gpu.IsAsync()) {
event.writable->Signal();
event.event.writable->Signal();
return NvResult::Success;
}
auto lock = gpu.LockSync();
const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
const u32 current_syncpoint_value = event.fence.value;
const s32 diff = current_syncpoint_value - params.threshold;
if (diff >= 0) {
event.writable->Signal();
event.event.writable->Signal();
params.value = current_syncpoint_value;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
@@ -109,7 +124,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
}
params.value |= event_id;
event.writable->Clear();
event.event.writable->Clear();
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
if (!is_async && ctrl.fresh_call) {
ctrl.must_delay = true;
@@ -154,24 +169,22 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
return NvResult::Success;
}
u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventSignalParams params{};
std::memcpy(&params, input.data(), sizeof(params));
// TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
// It is believed from RE to cancel the GPU Event. However, better research is required
u32 event_id = params.user_event_id & 0x00FF;
LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
u32 event_id = params.event_id & 0x00FF;
LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
if (event_id >= MaxNvEvents) {
return NvResult::BadParameter;
}
if (events_interface.status[event_id] == EventState::Waiting) {
auto& gpu = system.GPU();
if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
events_interface.assigned_value[event_id])) {
events_interface.LiberateEvent(event_id);
events_interface.events[event_id].writable->Signal();
}
events_interface.LiberateEvent(event_id);
}
syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
return NvResult::Success;
}

View File

@@ -14,7 +14,8 @@ namespace Service::Nvidia::Devices {
class nvhost_ctrl final : public nvdevice {
public:
explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface,
SyncpointManager& syncpoint_manager);
~nvhost_ctrl() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -31,7 +32,7 @@ private:
IocSyncptWaitexCommand = 0xC0100019,
IocSyncptReadMaxCommand = 0xC008001A,
IocGetConfigCommand = 0xC183001B,
IocCtrlEventSignalCommand = 0xC004001C,
IocCtrlClearEventWaitCommand = 0xC004001C,
IocCtrlEventWaitCommand = 0xC010001D,
IocCtrlEventWaitAsyncCommand = 0xC010001E,
IocCtrlEventRegisterCommand = 0xC004001F,
@@ -94,7 +95,7 @@ private:
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
struct IocCtrlEventSignalParams {
u32_le user_event_id;
u32_le event_id;
};
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
"IocCtrlEventSignalParams is incorrect size");
@@ -142,9 +143,10 @@ private:
u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
EventInterface& events_interface;
SyncpointManager& syncpoint_manager;
};
} // namespace Service::Nvidia::Devices

View File

@@ -7,14 +7,20 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace Service::Nvidia::Devices {
nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
SyncpointManager& syncpoint_manager)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager{syncpoint_manager} {
channel_fence.id = syncpoint_manager.AllocateSyncpoint();
channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
}
nvhost_gpu::~nvhost_gpu() = default;
u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -126,10 +132,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
params.unk3);
auto& gpu = system.GPU();
params.fence_out.id = assigned_syncpoints;
params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
assigned_syncpoints++;
channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
params.fence_out = channel_fence;
std::memcpy(output.data(), &params, output.size());
return 0;
}
@@ -145,37 +151,98 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
return 0;
}
static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
return {
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
Tegra::SubmissionMode::Increasing),
{fence.value},
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
Tegra::SubmissionMode::Increasing),
Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id),
};
}
static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
std::vector<Tegra::CommandHeader> result{
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
Tegra::SubmissionMode::Increasing),
{}};
for (u32 count = 0; count < add_increment; ++count) {
result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
Tegra::SubmissionMode::Increasing));
result.emplace_back(
Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id));
}
return result;
}
static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
u32 add_increment) {
std::vector<Tegra::CommandHeader> result{
Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
Tegra::SubmissionMode::Increasing),
{}};
const std::vector<Tegra::CommandHeader> increment{
BuildIncrementCommandList(fence, add_increment)};
result.insert(result.end(), increment.begin(), increment.end());
return result;
}
u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
Tegra::CommandList&& entries) {
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
auto& gpu = system.GPU();
params.fence_out.id = channel_fence.id;
if (params.flags.add_wait.Value() &&
!syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
}
if (params.flags.add_increment.Value() || params.flags.increment.Value()) {
const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0;
params.fence_out.value = syncpoint_manager.IncreaseSyncpoint(
params.fence_out.id, params.AddIncrementValue() + increment_value);
} else {
params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
}
entries.RefreshIntegrityChecks(gpu);
gpu.PushGPUEntries(std::move(entries));
if (params.flags.add_increment.Value()) {
if (params.flags.suppress_wfi) {
gpu.PushGPUEntries(Tegra::CommandList{
BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())});
} else {
gpu.PushGPUEntries(Tegra::CommandList{
BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())});
}
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return 0;
}
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
params.num_entries * sizeof(Tegra::CommandListHeader),
"Incorrect input size");
Tegra::CommandList entries(params.num_entries);
std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
params.num_entries * sizeof(Tegra::CommandListHeader));
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
auto& gpu = system.GPU();
u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
if (params.flags.increment.Value()) {
params.fence_out.value += current_syncpoint_value;
} else {
params.fence_out.value = current_syncpoint_value;
}
gpu.PushGPUEntries(std::move(entries));
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return 0;
return SubmitGPFIFOImpl(params, output, std::move(entries));
}
u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
@@ -185,31 +252,17 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
Tegra::CommandList entries(params.num_entries);
if (version == IoctlVersion::Version2) {
std::memcpy(entries.data(), input2.data(),
std::memcpy(entries.command_lists.data(), input2.data(),
params.num_entries * sizeof(Tegra::CommandListHeader));
} else {
system.Memory().ReadBlock(params.address, entries.data(),
system.Memory().ReadBlock(params.address, entries.command_lists.data(),
params.num_entries * sizeof(Tegra::CommandListHeader));
}
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
auto& gpu = system.GPU();
u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
if (params.flags.increment.Value()) {
params.fence_out.value += current_syncpoint_value;
} else {
params.fence_out.value = current_syncpoint_value;
}
gpu.PushGPUEntries(std::move(entries));
std::memcpy(output.data(), &params, output.size());
return 0;
return SubmitGPFIFOImpl(params, output, std::move(entries));
}
u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {

View File

@@ -11,6 +11,11 @@
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "video_core/dma_pusher.h"
namespace Service::Nvidia {
class SyncpointManager;
}
namespace Service::Nvidia::Devices {
@@ -21,7 +26,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
class nvhost_gpu final : public nvdevice {
public:
explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
SyncpointManager& syncpoint_manager);
~nvhost_gpu() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -162,10 +168,15 @@ private:
u32_le raw;
BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
BitField<1, 1, u32_le> add_increment; // append an increment to the list
BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
BitField<2, 1, u32_le> new_hw_format; // mostly ignored
BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
BitField<8, 1, u32_le> increment; // increment the returned fence
} flags;
Fence fence_out; // returned new fence object for others to wait on
u32 AddIncrementValue() const {
return flags.add_increment.Value() << 1;
}
};
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
"IoctlSubmitGpfifo is incorrect size");
@@ -190,6 +201,8 @@ private:
u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
Tegra::CommandList&& entries);
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
const std::vector<u8>& input2, IoctlVersion version);
@@ -198,7 +211,8 @@ private:
u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
std::shared_ptr<nvmap> nvmap_dev;
u32 assigned_syncpoints{};
SyncpointManager& syncpoint_manager;
Fence channel_fence;
};
} // namespace Service::Nvidia::Devices

View File

@@ -2,15 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
nvhost_nvdec::~nvhost_nvdec() = default;
u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -21,7 +23,7 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input, output);
return SetNVMAPfd(input);
case IoctlCommand::IocSubmit:
return Submit(input, output);
case IoctlCommand::IocGetSyncpoint:
@@ -29,79 +31,29 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
case IoctlCommand::IocGetWaitbase:
return GetWaitbase(input, output);
case IoctlCommand::IocMapBuffer:
return MapBuffer(input, output);
case IoctlCommand::IocMapBuffer2:
case IoctlCommand::IocMapBuffer3:
case IoctlCommand::IocMapBufferEx:
return MapBufferEx(input, output);
case IoctlCommand::IocUnmapBufferEx:
return UnmapBufferEx(input, output);
return MapBuffer(input, output);
case IoctlCommand::IocUnmapBufferEx: {
// This command is sent when the video stream has ended, flush all video contexts
// This is usually sent in the folowing order: vic, nvdec, vic.
// Inform the GPU to clear any remaining nvdec buffers when this is detected.
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
Tegra::ChCommandHeaderList cmdlist(1);
cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
system.GPU().PushCommandBuffer(cmdlist);
[[fallthrough]]; // fallthrough to unmap buffers
};
case IoctlCommand::IocUnmapBuffer:
case IoctlCommand::IocUnmapBuffer2:
case IoctlCommand::IocUnmapBuffer3:
return UnmapBuffer(input, output);
case IoctlCommand::IocSetSubmitTimeout:
return SetSubmitTimeout(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
}
u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
return 0;
}
u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return 0;
}
u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
return 0;
}
u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
return 0;
}
u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
return 0;
}
u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlUnmapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
return 0;
}

View File

@@ -4,16 +4,14 @@
#pragma once
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include <memory>
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
namespace Service::Nvidia::Devices {
class nvhost_nvdec final : public nvdevice {
class nvhost_nvdec final : public nvhost_nvdec_common {
public:
explicit nvhost_nvdec(Core::System& system);
explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_nvdec() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -27,62 +25,15 @@ private:
IocGetSyncpoint = 0xC0080002,
IocGetWaitbase = 0xC0080003,
IocMapBuffer = 0xC01C0009,
IocMapBuffer2 = 0xC16C0009,
IocMapBuffer3 = 0xC15C0009,
IocMapBufferEx = 0xC0A40009,
IocUnmapBufferEx = 0xC0A4000A,
IocUnmapBuffer = 0xC0A4000A,
IocUnmapBuffer2 = 0xC16C000A,
IocUnmapBufferEx = 0xC01C000A,
IocUnmapBuffer3 = 0xC15C000A,
IocSetSubmitTimeout = 0x40040007,
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
};
static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size");
struct IoctlSubmit {
INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size");
struct IoctlGetSyncpoint {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size");
struct IoctlGetWaitbase {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size");
struct IoctlMapBuffer {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
struct IoctlMapBufferEx {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size");
struct IoctlUnmapBufferEx {
INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size");
u32_le nvmap_fd{};
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -0,0 +1,234 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/memory.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
namespace {
// Splice vectors will copy count amount of type T from the input vector into the dst vector.
template <typename T>
std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
std::size_t offset) {
std::memcpy(dst.data(), input.data() + offset, count * sizeof(T));
offset += count * sizeof(T);
return offset;
}
// Write vectors will write data to the output buffer
template <typename T>
std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) {
std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T));
offset += src.size() * sizeof(T);
return offset;
}
} // Anonymous namespace
namespace NvErrCodes {
constexpr u32 Success{};
[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)};
constexpr u32 InvalidInput{static_cast<u32>(-22)};
} // namespace NvErrCodes
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_nvdec_common::~nvhost_nvdec_common() = default;
u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
}
u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
// Instantiate param buffers
std::size_t offset = sizeof(IoctlSubmit);
std::vector<CommandBuffer> command_buffers(params.cmd_buffer_count);
std::vector<Reloc> relocs(params.relocation_count);
std::vector<u32> reloc_shifts(params.relocation_count);
std::vector<SyncptIncr> syncpt_increments(params.syncpoint_count);
std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
std::vector<Fence> fences(params.fence_count);
// Splice input into their respective buffers
offset = SpliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
offset = SpliceVectors(input, relocs, params.relocation_count, offset);
offset = SpliceVectors(input, reloc_shifts, params.relocation_count, offset);
offset = SpliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset);
offset = SpliceVectors(input, fences, params.fence_count, offset);
// TODO(ameerj): For async gpu, utilize fences for syncpoint 'max' increment
auto& gpu = system.GPU();
for (const auto& cmd_buffer : command_buffers) {
auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;);
const auto map = FindBufferMap(object->dma_map_addr);
if (!map) {
LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
object->addr, object->dma_map_addr);
return 0;
}
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
gpu.PushCommandBuffer(cmdlist);
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// Some games expect command_buffers to be written back
offset = sizeof(IoctlSubmit);
offset = WriteVectors(output, command_buffers, offset);
offset = WriteVectors(output, relocs, offset);
offset = WriteVectors(output, reloc_shifts, offset);
offset = WriteVectors(output, syncpt_increments, offset);
offset = WriteVectors(output, wait_checks, offset);
return NvErrCodes::Success;
}
u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
// We found that implementing this causes deadlocks with async gpu, along with degraded
// performance. TODO: RE the nvdec async implementation
params.value = 0;
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return NvErrCodes::Success;
}
u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
return 0;
}
u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
auto& gpu = system.GPU();
for (auto& cmf_buff : cmd_buffer_handles) {
auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
if (!object) {
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
}
if (object->dma_map_addr == 0) {
// NVDEC and VIC memory is in the 32-bit address space
// MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
object->dma_map_addr = static_cast<u32>(low_addr);
// Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
ASSERT(object->dma_map_addr == low_addr);
}
if (!object->dma_map_addr) {
LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
} else {
cmf_buff.map_address = object->dma_map_addr;
AddBufferMap(object->dma_map_addr, object->size, object->addr,
object->status == nvmap::Object::Status::Allocated);
}
}
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
cmd_buffer_handles.size() * sizeof(MapBufferEntry));
return NvErrCodes::Success;
}
u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
auto& gpu = system.GPU();
for (auto& cmf_buff : cmd_buffer_handles) {
const auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
if (!object) {
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
}
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
} else {
// This occurs quite frequently, however does not seem to impact functionality
LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
object->dma_map_addr);
}
object->dma_map_addr = 0;
}
std::memset(output.data(), 0, output.size());
return NvErrCodes::Success;
}
u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&submit_timeout, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
return NvErrCodes::Success;
}
std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
GPUVAddr gpu_addr) const {
const auto it = std::find_if(
buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
});
ASSERT(it != buffer_mappings.end());
return it->second;
}
void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
bool is_allocated) {
buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
}
std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
const auto iter{buffer_mappings.find(gpu_addr)};
if (iter == buffer_mappings.end()) {
return std::nullopt;
}
std::size_t size = 0;
if (iter->second.IsAllocated()) {
size = iter->second.Size();
}
buffer_mappings.erase(iter);
return size;
}
} // namespace Service::Nvidia::Devices

View File

@@ -0,0 +1,168 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
namespace Service::Nvidia::Devices {
class nvmap;
class nvhost_nvdec_common : public nvdevice {
public:
explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_nvdec_common() override;
virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) = 0;
protected:
class BufferMap final {
public:
constexpr BufferMap() = default;
constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
: start_addr{start_addr}, end_addr{start_addr + size} {}
constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
bool is_allocated)
: start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
is_allocated{is_allocated} {}
constexpr VAddr StartAddr() const {
return start_addr;
}
constexpr VAddr EndAddr() const {
return end_addr;
}
constexpr std::size_t Size() const {
return end_addr - start_addr;
}
constexpr VAddr CpuAddr() const {
return cpu_addr;
}
constexpr bool IsAllocated() const {
return is_allocated;
}
private:
GPUVAddr start_addr{};
GPUVAddr end_addr{};
VAddr cpu_addr{};
bool is_allocated{};
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
struct IoctlSubmitCommandBuffer {
u32_le id;
u32_le offset;
u32_le count;
};
static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
"IoctlSubmitCommandBuffer is incorrect size");
struct IoctlSubmit {
u32_le cmd_buffer_count;
u32_le relocation_count;
u32_le syncpoint_count;
u32_le fence_count;
};
static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
struct CommandBuffer {
s32 memory_id;
u32 offset;
s32 word_count;
};
static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
struct Reloc {
s32 cmdbuffer_memory;
s32 cmdbuffer_offset;
s32 target;
s32 target_offset;
};
static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
struct SyncptIncr {
u32 id;
u32 increments;
};
static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
struct Fence {
u32 id;
u32 value;
};
static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
struct IoctlGetSyncpoint {
// Input
u32_le param;
// Output
u32_le value;
};
static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
struct IoctlGetWaitbase {
u32_le unknown; // seems to be ignored? Nintendo added this
u32_le value;
};
static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
struct IoctlMapBuffer {
u32_le num_entries;
u32_le data_address; // Ignored by the driver.
u32_le attach_host_ch_das;
};
static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
struct IocGetIdParams {
// Input
u32_le param;
// Output
u32_le value;
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
// Used for mapping and unmapping command buffers
struct MapBufferEntry {
u32_le map_handle;
u32_le map_address;
};
static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
/// Ioctl command implementations
u32 SetNVMAPfd(const std::vector<u8>& input);
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
u32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;
// This is expected to be ordered, therefore we must use a map, not unordered_map
std::map<GPUVAddr, BufferMap> buffer_mappings;
};
}; // namespace Service::Nvidia::Devices

View File

@@ -2,15 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
nvhost_vic::~nvhost_vic() = default;
u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -21,7 +23,7 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input, output);
return SetNVMAPfd(input);
case IoctlCommand::IocSubmit:
return Submit(input, output);
case IoctlCommand::IocGetSyncpoint:
@@ -29,83 +31,19 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
case IoctlCommand::IocGetWaitbase:
return GetWaitbase(input, output);
case IoctlCommand::IocMapBuffer:
return MapBuffer(input, output);
case IoctlCommand::IocMapBuffer2:
case IoctlCommand::IocMapBuffer3:
case IoctlCommand::IocMapBuffer4:
case IoctlCommand::IocMapBufferEx:
return MapBuffer(input, output);
case IoctlCommand::IocUnmapBuffer:
case IoctlCommand::IocUnmapBuffer2:
case IoctlCommand::IocUnmapBuffer3:
case IoctlCommand::IocUnmapBufferEx:
return UnmapBufferEx(input, output);
return UnmapBuffer(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
}
u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
// Workaround for Luigi's Mansion 3, as nvhost_vic is not implemented for asynch GPU
params.command_buffer = {};
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
return 0;
}
u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return 0;
}
u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
return 0;
}
u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
return 0;
}
u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
return 0;
}
u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlUnmapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
return 0;
}

View File

@@ -4,19 +4,15 @@
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
namespace Service::Nvidia::Devices {
class nvmap;
class nvhost_vic final : public nvdevice {
class nvhost_vic final : public nvhost_nvdec_common {
public:
explicit nvhost_vic(Core::System& system);
~nvhost_vic() override;
explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_vic();
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
@@ -28,74 +24,14 @@ private:
IocGetSyncpoint = 0xC0080002,
IocGetWaitbase = 0xC0080003,
IocMapBuffer = 0xC01C0009,
IocMapBuffer2 = 0xC0340009,
IocMapBuffer3 = 0xC0140009,
IocMapBuffer4 = 0xC00C0009,
IocMapBufferEx = 0xC03C0009,
IocUnmapBufferEx = 0xC03C000A,
IocUnmapBuffer = 0xC03C000A,
IocUnmapBuffer2 = 0xC034000A,
IocUnmapBuffer3 = 0xC00C000A,
IocUnmapBufferEx = 0xC01C000A,
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
struct IoctlSubmitCommandBuffer {
u32 id;
u32 offset;
u32 count;
};
static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
"IoctlSubmitCommandBuffer is incorrect size");
struct IoctlSubmit {
u32 command_buffer_count;
u32 relocations_count;
u32 syncpt_count;
u32 wait_count;
std::array<IoctlSubmitCommandBuffer, 4> command_buffer;
};
static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit is incorrect size");
struct IoctlGetSyncpoint {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetSyncpoint) == 0x8, "IoctlGetSyncpoint is incorrect size");
struct IoctlGetWaitbase {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
struct IoctlMapBuffer {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
struct IoctlMapBufferEx {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x30); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBufferEx) == 0x3C, "IoctlMapBufferEx is incorrect size");
struct IoctlUnmapBufferEx {
INSERT_PADDING_BYTES(0x3C); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlUnmapBufferEx) == 0x3C, "IoctlUnmapBufferEx is incorrect size");
u32_le nvmap_fd{};
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -37,6 +37,7 @@ public:
VAddr addr;
Status status;
u32 refcount;
u32 dma_map_addr;
};
std::shared_ptr<Object> GetObject(u32 handle) const {

View File

@@ -21,6 +21,7 @@
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvmemp.h"
#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service::Nvidia {
@@ -36,24 +37,26 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
nvflinger.SetNVDrvInstance(module_);
}
Module::Module(Core::System& system) {
Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
auto& kernel = system.Kernel();
for (u32 i = 0; i < MaxNvEvents; i++) {
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label);
events_interface.events[i] = {Kernel::WritableEvent::CreateEventPair(kernel, event_label)};
events_interface.status[i] = EventState::Free;
events_interface.registered[i] = false;
}
auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev);
devices["/dev/nvhost-gpu"] =
std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager);
devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
devices["/dev/nvmap"] = nvmap_dev;
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system);
devices["/dev/nvhost-ctrl"] =
std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager);
devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev);
devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system);
devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev);
}
Module::~Module() = default;
@@ -95,17 +98,17 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
if (events_interface.assigned_syncpt[i] == syncpoint_id &&
events_interface.assigned_value[i] == value) {
events_interface.LiberateEvent(i);
events_interface.events[i].writable->Signal();
events_interface.events[i].event.writable->Signal();
}
}
}
std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
return events_interface.events[event_id].readable;
return events_interface.events[event_id].event.readable;
}
std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
return events_interface.events[event_id].writable;
return events_interface.events[event_id].event.writable;
}
} // namespace Service::Nvidia

View File

@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -22,15 +23,23 @@ class NVFlinger;
namespace Service::Nvidia {
class SyncpointManager;
namespace Devices {
class nvdevice;
}
/// Represents an Nvidia event
struct NvEvent {
Kernel::EventPair event;
Fence fence{};
};
struct EventInterface {
// Mask representing currently busy events
u64 events_mask{};
// Each kernel event associated to an NV event
std::array<Kernel::EventPair, MaxNvEvents> events;
std::array<NvEvent, MaxNvEvents> events;
// The status of the current NVEvent
std::array<EventState, MaxNvEvents> status{};
// Tells if an NVEvent is registered or not
@@ -119,6 +128,9 @@ public:
std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
private:
/// Manages syncpoints on the host
SyncpointManager syncpoint_manager;
/// Id to use for the next open file descriptor.
u32 next_fd = 1;

View File

@@ -0,0 +1,39 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "video_core/gpu.h"
namespace Service::Nvidia {
SyncpointManager::SyncpointManager(Tegra::GPU& gpu) : gpu{gpu} {}
SyncpointManager::~SyncpointManager() = default;
u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
return GetSyncpointMin(syncpoint_id);
}
u32 SyncpointManager::AllocateSyncpoint() {
for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
if (!syncpoints[syncpoint_id].is_allocated) {
syncpoints[syncpoint_id].is_allocated = true;
return syncpoint_id;
}
}
UNREACHABLE_MSG("No more available syncpoints!");
return {};
}
u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
for (u32 index = 0; index < value; ++index) {
syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
}
return GetSyncpointMax(syncpoint_id);
}
} // namespace Service::Nvidia

View File

@@ -0,0 +1,85 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <atomic>
#include "common/common_types.h"
#include "core/hle/service/nvdrv/nvdata.h"
namespace Tegra {
class GPU;
}
namespace Service::Nvidia {
class SyncpointManager final {
public:
explicit SyncpointManager(Tegra::GPU& gpu);
~SyncpointManager();
/**
* Returns true if the specified syncpoint is expired for the given value.
* @param syncpoint_id Syncpoint ID to check.
* @param value Value to check against the specified syncpoint.
* @returns True if the specified syncpoint is expired for the given value, otherwise False.
*/
bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
}
/**
* Gets the lower bound for the specified syncpoint.
* @param syncpoint_id Syncpoint ID to get the lower bound for.
* @returns The lower bound for the specified syncpoint.
*/
u32 GetSyncpointMin(u32 syncpoint_id) const {
return syncpoints[syncpoint_id].min.load(std::memory_order_relaxed);
}
/**
* Gets the uper bound for the specified syncpoint.
* @param syncpoint_id Syncpoint ID to get the upper bound for.
* @returns The upper bound for the specified syncpoint.
*/
u32 GetSyncpointMax(u32 syncpoint_id) const {
return syncpoints[syncpoint_id].max.load(std::memory_order_relaxed);
}
/**
* Refreshes the minimum value for the specified syncpoint.
* @param syncpoint_id Syncpoint ID to be refreshed.
* @returns The new syncpoint minimum value.
*/
u32 RefreshSyncpoint(u32 syncpoint_id);
/**
* Allocates a new syncoint.
* @returns The syncpoint ID for the newly allocated syncpoint.
*/
u32 AllocateSyncpoint();
/**
* Increases the maximum value for the specified syncpoint.
* @param syncpoint_id Syncpoint ID to be increased.
* @param value Value to increase the specified syncpoint by.
* @returns The new syncpoint maximum value.
*/
u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
private:
struct Syncpoint {
std::atomic<u32> min;
std::atomic<u32> max;
std::atomic<bool> is_allocated;
};
std::array<Syncpoint, MaxSyncPoints> syncpoints{};
Tegra::GPU& gpu;
};
} // namespace Service::Nvidia

View File

@@ -29,6 +29,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
.slot = slot,
.status = Buffer::Status::Free,
.igbp_buffer = igbp_buffer,
.transform = {},
.crop_rect = {},
.swap_interval = 0,
.multi_fence = {},
});
buffer_wait_event.writable->Signal();
@@ -99,6 +103,20 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
queue_sequence.push_back(slot);
}
void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
const auto itr = std::find_if(queue.begin(), queue.end(),
[slot](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status != Buffer::Status::Free);
itr->status = Buffer::Status::Free;
itr->multi_fence = multi_fence;
itr->swap_interval = 0;
free_buffers.push_back(slot);
buffer_wait_event.writable->Signal();
}
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
auto itr = queue.end();
// Iterate to find a queued buffer matching the requested slot.

View File

@@ -95,6 +95,7 @@ public:
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect, u32 swap_interval,
Service::Nvidia::MultiFence& multi_fence);
void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
void ReleaseBuffer(u32 slot);
void Disconnect();

View File

@@ -242,6 +242,10 @@ void NVFlinger::Compose() {
const auto& igbp_buffer = buffer->get().igbp_buffer;
if (!system.IsPoweredOn()) {
return; // We are likely shutting down
}
auto& gpu = system.GPU();
const auto& multi_fence = buffer->get().multi_fence;
guard->unlock();

View File

@@ -202,6 +202,7 @@ SET::SET() : ServiceFramework("set") {
{8, &SET::GetQuestFlag, "GetQuestFlag"},
{9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
{10, nullptr, "GetFirmwareVersionForDebug"},
{11, nullptr, "GetDeviceNickName"},
};
// clang-format on

View File

@@ -300,6 +300,8 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
{198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"},
{199, nullptr, "GetButtonConfigRegisteredSettings"},
{200, nullptr, "SetButtonConfigRegisteredSettings"},
{201, nullptr, "GetFieldTestingFlag"},
{202, nullptr, "SetFieldTestingFlag"},
};
// clang-format on

View File

@@ -10,6 +10,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/service/time/interface.h"
#include "core/hle/service/time/time.h"
@@ -125,7 +126,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
Kernel::Thread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
auto& time_manager{module->GetTimeManager()};
auto& time_manager{system.GetTimeManager()};
clock_snapshot.is_automatic_correction_enabled =
time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
@@ -182,7 +183,7 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore(),
rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(),
system);
}
@@ -190,7 +191,7 @@ void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext&
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore(),
rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(),
system);
}
@@ -198,29 +199,28 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore(),
system);
rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system);
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager());
rb.PushIpcInterface<ITimeZoneService>(system.GetTimeManager().GetTimeZoneContentManager());
}
void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore(),
rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(),
system);
}
void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
auto& clock_core{module->GetTimeManager().GetStandardNetworkSystemClockCore()};
auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
@@ -229,7 +229,7 @@ void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()};
auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()};
if (!steady_clock_core.IsInitialized()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_UNINITIALIZED_CLOCK);
@@ -262,8 +262,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
Clock::SystemClockContext user_context{};
if (const ResultCode result{
module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
system, user_context)};
system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
user_context)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -271,7 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
Clock::SystemClockContext network_context{};
if (const ResultCode result{
module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
system, network_context)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -372,7 +372,7 @@ void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& c
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder());
rb.PushCopyObjects(SharedFrom(&system.Kernel().GetTimeSharedMem()));
}
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
@@ -381,7 +381,7 @@ Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& syste
Module::Interface::~Interface() = default;
void InstallInterfaces(Core::System& system) {
auto module{std::make_shared<Module>(system)};
auto module{std::make_shared<Module>()};
std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());

View File

@@ -16,7 +16,7 @@ namespace Service::Time {
class Module final {
public:
Module(Core::System& system) : time_manager{system} {}
Module() = default;
class Interface : public ServiceFramework<Interface> {
public:
@@ -46,13 +46,6 @@ public:
std::shared_ptr<Module> module;
Core::System& system;
};
TimeManager& GetTimeManager() {
return time_manager;
}
private:
TimeManager time_manager;
};
/// Registers all Time services with the specified service manager.

View File

@@ -22,7 +22,277 @@ static std::chrono::seconds GetSecondsSinceEpoch() {
Settings::values.custom_rtc_differential;
}
static s64 GetExternalTimeZoneOffset() {
static s64 GetExternalRtcValue() {
return GetSecondsSinceEpoch().count() + TimeManager::GetExternalTimeZoneOffset();
}
struct TimeManager::Impl final {
explicit Impl(Core::System& system)
: shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
standard_network_system_clock_core{standard_steady_clock_core},
standard_user_system_clock_core{standard_local_system_clock_core,
standard_network_system_clock_core, system},
ephemeral_network_system_clock_core{tick_based_steady_clock_core},
local_system_clock_context_writer{
std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
network_system_clock_context_writer{
std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
ephemeral_network_system_clock_context_writer{
std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
time_zone_content_manager{system} {
const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
SetupEphemeralNetworkSystemClock();
}
~Impl() = default;
Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
return standard_steady_clock_core;
}
const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
return standard_steady_clock_core;
}
Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
return standard_local_system_clock_core;
}
const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
return standard_local_system_clock_core;
}
Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
return standard_network_system_clock_core;
}
const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
return standard_network_system_clock_core;
}
Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
return standard_user_system_clock_core;
}
const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
return standard_user_system_clock_core;
}
TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
return time_zone_content_manager;
}
const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
return time_zone_content_manager;
}
SharedMemory& GetSharedMemory() {
return shared_memory;
}
const SharedMemory& GetSharedMemory() const {
return shared_memory;
}
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,
FileSys::VirtualFile& vfs_file) {
if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
location_name, vfs_file) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
total_location_name_count);
time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(
time_zone_rule_version);
time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
}
static s64 GetExternalTimeZoneOffset() {
// With "auto" timezone setting, we use the external system's timezone offset
if (Settings::GetTimeZoneString() == "auto") {
return Common::TimeZone::GetCurrentOffsetSeconds().count();
}
return 0;
}
void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
Clock::TimeSpanType setup_value,
Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
standard_steady_clock_core.SetClockSourceId(clock_source_id);
standard_steady_clock_core.SetSetupValue(setup_value);
standard_steady_clock_core.SetInternalOffset(internal_offset);
standard_steady_clock_core.MarkAsInitialized();
const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
}
void SetupStandardLocalSystemClock(Core::System& system,
Clock::SystemClockContext clock_context, s64 posix_time) {
standard_local_system_clock_core.SetUpdateCallbackInstance(
local_system_clock_context_writer);
const auto current_time_point{
standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
standard_local_system_clock_core.SetSystemClockContext(clock_context);
} else {
if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) !=
RESULT_SUCCESS) {
UNREACHABLE();
return;
}
}
standard_local_system_clock_core.MarkAsInitialized();
}
void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
Clock::TimeSpanType sufficient_accuracy) {
standard_network_system_clock_core.SetUpdateCallbackInstance(
network_system_clock_context_writer);
if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
RESULT_SUCCESS) {
UNREACHABLE();
return;
}
standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
sufficient_accuracy);
standard_network_system_clock_core.MarkAsInitialized();
}
void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
Clock::SteadyClockTimePoint steady_clock_time_point) {
if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
standard_user_system_clock_core.MarkAsInitialized();
shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
}
void SetupEphemeralNetworkSystemClock() {
ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
ephemeral_network_system_clock_context_writer);
ephemeral_network_system_clock_core.MarkAsInitialized();
}
void UpdateLocalSystemClockTime(Core::System& system, s64 posix_time) {
const auto timespan{Service::Time::Clock::TimeSpanType::FromSeconds(posix_time)};
if (GetStandardLocalSystemClockCore()
.SetCurrentTime(system, timespan.ToSeconds())
.IsError()) {
UNREACHABLE();
return;
}
}
SharedMemory shared_memory;
Clock::StandardSteadyClockCore standard_steady_clock_core;
Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
Clock::StandardUserSystemClockCore standard_user_system_clock_core;
Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
ephemeral_network_system_clock_context_writer;
TimeZone::TimeZoneContentManager time_zone_content_manager;
};
TimeManager::TimeManager(Core::System& system) : system{system} {}
TimeManager::~TimeManager() = default;
void TimeManager::Initialize() {
impl = std::make_unique<Impl>(system);
// Time zones can only be initialized after impl is valid
impl->time_zone_content_manager.Initialize(*this);
}
Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() {
return impl->standard_steady_clock_core;
}
const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const {
return impl->standard_steady_clock_core;
}
Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() {
return impl->standard_local_system_clock_core;
}
const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const {
return impl->standard_local_system_clock_core;
}
Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() {
return impl->standard_network_system_clock_core;
}
const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore()
const {
return impl->standard_network_system_clock_core;
}
Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() {
return impl->standard_user_system_clock_core;
}
const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const {
return impl->standard_user_system_clock_core;
}
TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() {
return impl->time_zone_content_manager;
}
const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const {
return impl->time_zone_content_manager;
}
SharedMemory& TimeManager::GetSharedMemory() {
return impl->shared_memory;
}
const SharedMemory& TimeManager::GetSharedMemory() const {
return impl->shared_memory;
}
void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
impl->UpdateLocalSystemClockTime(system, posix_time);
}
void TimeManager::SetupTimeZoneManager(std::string location_name,
Clock::SteadyClockTimePoint time_zone_updated_time_point,
std::size_t total_location_name_count,
u128 time_zone_rule_version,
FileSys::VirtualFile& vfs_file) {
impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point,
total_location_name_count, time_zone_rule_version, vfs_file);
}
/*static*/ s64 TimeManager::GetExternalTimeZoneOffset() {
// With "auto" timezone setting, we use the external system's timezone offset
if (Settings::GetTimeZoneString() == "auto") {
return Common::TimeZone::GetCurrentOffsetSeconds().count();
@@ -30,117 +300,4 @@ static s64 GetExternalTimeZoneOffset() {
return 0;
}
static s64 GetExternalRtcValue() {
return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset();
}
TimeManager::TimeManager(Core::System& system)
: shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
standard_network_system_clock_core{standard_steady_clock_core},
standard_user_system_clock_core{standard_local_system_clock_core,
standard_network_system_clock_core, system},
ephemeral_network_system_clock_core{tick_based_steady_clock_core},
local_system_clock_context_writer{
std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
network_system_clock_context_writer{
std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
ephemeral_network_system_clock_context_writer{
std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
time_zone_content_manager{*this, system} {
const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
SetupEphemeralNetworkSystemClock();
}
TimeManager::~TimeManager() = default;
void TimeManager::SetupTimeZoneManager(std::string location_name,
Clock::SteadyClockTimePoint time_zone_updated_time_point,
std::size_t total_location_name_count,
u128 time_zone_rule_version,
FileSys::VirtualFile& vfs_file) {
if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
location_name, vfs_file) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
total_location_name_count);
time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
}
void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
Clock::TimeSpanType setup_value,
Clock::TimeSpanType internal_offset,
bool is_rtc_reset_detected) {
standard_steady_clock_core.SetClockSourceId(clock_source_id);
standard_steady_clock_core.SetSetupValue(setup_value);
standard_steady_clock_core.SetInternalOffset(internal_offset);
standard_steady_clock_core.MarkAsInitialized();
const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
}
void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
Clock::SystemClockContext clock_context,
s64 posix_time) {
standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);
const auto current_time_point{
standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
standard_local_system_clock_core.SetSystemClockContext(clock_context);
} else {
if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
}
standard_local_system_clock_core.MarkAsInitialized();
}
void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
Clock::TimeSpanType sufficient_accuracy) {
standard_network_system_clock_core.SetUpdateCallbackInstance(
network_system_clock_context_writer);
if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
sufficient_accuracy);
standard_network_system_clock_core.MarkAsInitialized();
}
void TimeManager::SetupStandardUserSystemClock(
Core::System& system, bool is_automatic_correction_enabled,
Clock::SteadyClockTimePoint steady_clock_time_point) {
if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
standard_user_system_clock_core.MarkAsInitialized();
shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
}
void TimeManager::SetupEphemeralNetworkSystemClock() {
ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
ephemeral_network_system_clock_context_writer);
ephemeral_network_system_clock_core.MarkAsInitialized();
}
} // namespace Service::Time

View File

@@ -5,6 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "common/time_zone.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/service/time/clock_types.h"
#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
@@ -32,86 +33,46 @@ public:
explicit TimeManager(Core::System& system);
~TimeManager();
Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
return standard_steady_clock_core;
}
void Initialize();
const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
return standard_steady_clock_core;
}
Clock::StandardSteadyClockCore& GetStandardSteadyClockCore();
Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
return standard_local_system_clock_core;
}
const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const;
const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
return standard_local_system_clock_core;
}
Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore();
Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
return standard_network_system_clock_core;
}
const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const;
const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
return standard_network_system_clock_core;
}
Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore();
Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
return standard_user_system_clock_core;
}
const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const;
const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
return standard_user_system_clock_core;
}
Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore();
TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
return time_zone_content_manager;
}
const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const;
const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
return time_zone_content_manager;
}
TimeZone::TimeZoneContentManager& GetTimeZoneContentManager();
SharedMemory& GetSharedMemory() {
return shared_memory;
}
const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const;
const SharedMemory& GetSharedMemory() const {
return shared_memory;
}
void UpdateLocalSystemClockTime(s64 posix_time);
SharedMemory& GetSharedMemory();
const SharedMemory& GetSharedMemory() const;
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,
FileSys::VirtualFile& vfs_file);
static s64 GetExternalTimeZoneOffset();
private:
void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
Clock::TimeSpanType setup_value,
Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected);
void SetupStandardLocalSystemClock(Core::System& system,
Clock::SystemClockContext clock_context, s64 posix_time);
void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
Clock::TimeSpanType sufficient_accuracy);
void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
Clock::SteadyClockTimePoint steady_clock_time_point);
void SetupEphemeralNetworkSystemClock();
Core::System& system;
SharedMemory shared_memory;
Clock::StandardSteadyClockCore standard_steady_clock_core;
Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
Clock::StandardUserSystemClockCore standard_user_system_clock_core;
Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
ephemeral_network_system_clock_context_writer;
TimeZone::TimeZoneContentManager time_zone_content_manager;
struct Impl;
std::unique_ptr<Impl> impl;
};
} // namespace Service::Time

View File

@@ -68,9 +68,10 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
return location_name_cache;
}
TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
: system{system}, location_name_cache{BuildLocationNameCache(system)} {
TimeZoneContentManager::TimeZoneContentManager(Core::System& system)
: system{system}, location_name_cache{BuildLocationNameCache(system)} {}
void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
std::string location_name;
const auto timezone_setting = Settings::GetTimeZoneString();
if (timezone_setting == "auto" || timezone_setting == "default") {

View File

@@ -21,7 +21,9 @@ namespace Service::Time::TimeZone {
class TimeZoneContentManager final {
public:
TimeZoneContentManager(TimeManager& time_manager, Core::System& system);
explicit TimeZoneContentManager(Core::System& system);
void Initialize(TimeManager& time_manager);
TimeZoneManager& GetTimeZoneManager() {
return time_zone_manager;

View File

@@ -215,10 +215,9 @@ public:
explicit IGBPConnectRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPConnectRequestParcel() override = default;
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -279,10 +278,9 @@ public:
: Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPSetPreallocatedBufferRequestParcel() override = default;
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
buffer = Read<NVFlinger::IGBPBuffer>();
}
@@ -306,15 +304,40 @@ protected:
}
};
class IGBPCancelBufferRequestParcel : public Parcel {
public:
explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
void DeserializeData() override {
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
struct Data {
u32_le slot;
Service::Nvidia::MultiFence multi_fence;
};
Data data;
};
class IGBPCancelBufferResponseParcel : public Parcel {
protected:
void SerializeData() override {
Write<u32>(0); // Success
}
};
class IGBPDequeueBufferRequestParcel : public Parcel {
public:
explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPDequeueBufferRequestParcel() override = default;
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -333,7 +356,6 @@ class IGBPDequeueBufferResponseParcel : public Parcel {
public:
explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence)
: slot(slot), multi_fence(multi_fence) {}
~IGBPDequeueBufferResponseParcel() override = default;
protected:
void SerializeData() override {
@@ -352,10 +374,9 @@ public:
explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPRequestBufferRequestParcel() override = default;
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
slot = Read<u32_le>();
}
@@ -384,10 +405,9 @@ public:
explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPQueueBufferRequestParcel() override = default;
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -447,10 +467,9 @@ public:
explicit IGBPQueryRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPQueryRequestParcel() override = default;
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
type = Read<u32_le>();
}
@@ -596,7 +615,12 @@ private:
break;
}
case TransactionId::CancelBuffer: {
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
IGBPCancelBufferResponseParcel response{};
ctx.WriteBuffer(response.Serialize());
break;
}
case TransactionId::Disconnect: {

View File

@@ -148,7 +148,7 @@ sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
}
int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) {
return poll(fds, nfds, timeout);
return poll(fds, static_cast<nfds_t>(nfds), timeout);
}
int closesocket(SOCKET fd) {

View File

@@ -14,7 +14,7 @@
namespace Settings {
Values values = {};
bool configuring_global = true;
static bool configuring_global = true;
std::string GetTimeZoneString() {
static constexpr std::array timezones{
@@ -63,6 +63,7 @@ void LogSettings() {
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
log_setting("Renderer_UseAsynchronousGpuEmulation",
values.use_asynchronous_gpu_emulation.GetValue());
log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
@@ -80,11 +81,12 @@ void LogSettings() {
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
}
float Volume() {
if (values.audio_muted) {
return 0.0f;
}
return values.volume.GetValue();
bool IsConfiguringGlobal() {
return configuring_global;
}
void SetConfiguringGlobal(bool is_global) {
configuring_global = is_global;
}
bool IsGPULevelExtreme() {
@@ -96,6 +98,13 @@ bool IsGPULevelHigh() {
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
}
float Volume() {
if (values.audio_muted) {
return 0.0f;
}
return values.volume.GetValue();
}
void RestoreGlobalState() {
// If a game is running, DO NOT restore the global settings state
if (Core::System::GetInstance().IsPoweredOn()) {
@@ -119,6 +128,7 @@ void RestoreGlobalState() {
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
values.use_nvdec_emulation.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.use_assembly_shaders.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);

View File

@@ -33,8 +33,6 @@ enum class CPUAccuracy {
DebugMode = 2,
};
extern bool configuring_global;
template <typename Type>
class Setting final {
public:
@@ -103,7 +101,7 @@ struct Values {
bool renderer_debug;
Setting<int> vulkan_device;
Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
Setting<u16> resolution_factor{1};
Setting<int> aspect_ratio;
Setting<int> max_anisotropy;
Setting<bool> use_frame_limit;
@@ -111,6 +109,7 @@ struct Values {
Setting<bool> use_disk_shader_cache;
Setting<GPUAccuracy> gpu_accuracy;
Setting<bool> use_asynchronous_gpu_emulation;
Setting<bool> use_nvdec_emulation;
Setting<bool> use_vsync;
Setting<bool> use_assembly_shaders;
Setting<bool> use_asynchronous_shaders;
@@ -197,13 +196,18 @@ struct Values {
// Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons;
} extern values;
};
float Volume();
extern Values values;
bool IsConfiguringGlobal();
void SetConfiguringGlobal(bool is_global);
bool IsGPULevelExtreme();
bool IsGPULevelHigh();
float Volume();
std::string GetTimeZoneString();
void Apply();

View File

@@ -206,6 +206,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation.GetValue());
AddField(field_type, "Renderer_UseNvdecEmulation",
Settings::values.use_nvdec_emulation.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseAssemblyShaders",
Settings::values.use_assembly_shaders.GetValue());

View File

@@ -29,6 +29,35 @@ add_library(input_common STATIC
udp/udp.h
)
if (MSVC)
target_compile_options(input_common PRIVATE
# 'expression' : signed/unsigned mismatch
/we4018
# 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
/we4244
# 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
/we4245
# 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
/we4254
# 'var' : conversion from 'size_t' to 'type', possible loss of data
/we4267
# 'context' : truncation from 'type1' to 'type2'
/we4305
)
else()
target_compile_options(input_common PRIVATE
-Werror=conversion
-Werror=ignored-qualifiers
-Werror=implicit-fallthrough
-Werror=reorder
-Werror=shadow
-Werror=sign-compare
-Werror=unused-but-set-parameter
-Werror=unused-but-set-variable
-Werror=unused-variable
)
endif()
if(SDL2_FOUND)
target_sources(input_common PRIVATE
sdl/sdl_impl.cpp

View File

@@ -20,18 +20,22 @@ public:
constexpr float SQRT_HALF = 0.707106781f;
int x = 0, y = 0;
if (right->GetStatus())
if (right->GetStatus()) {
++x;
if (left->GetStatus())
}
if (left->GetStatus()) {
--x;
if (up->GetStatus())
}
if (up->GetStatus()) {
++y;
if (down->GetStatus())
}
if (down->GetStatus()) {
--y;
}
float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF),
y * coef * (x == 0 ? 1.0f : SQRT_HALF));
const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {

View File

@@ -21,14 +21,6 @@
namespace GCAdapter {
/// Used to loop through and assign button in poller
constexpr std::array<PadButton, 12> PadButtonArray{
PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
};
Adapter::Adapter() {
if (usb_adapter_handle != nullptr) {
return;
@@ -37,179 +29,263 @@ Adapter::Adapter() {
const int init_res = libusb_init(&libusb_ctx);
if (init_res == LIBUSB_SUCCESS) {
Setup();
adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
} else {
LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
}
}
GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
GCPadStatus pad = {};
const std::size_t offset = 1 + (9 * port);
Adapter::~Adapter() {
Reset();
}
adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
void Adapter::AdapterInputThread() {
LOG_DEBUG(Input, "GC Adapter input thread started");
s32 payload_size{};
AdapterPayload adapter_payload{};
if (adapter_scan_thread.joinable()) {
adapter_scan_thread.join();
}
while (adapter_input_thread_running) {
libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
static_cast<s32>(adapter_payload.size()), &payload_size, 16);
if (IsPayloadCorrect(adapter_payload, payload_size)) {
UpdateControllers(adapter_payload);
UpdateVibrations();
}
std::this_thread::yield();
}
if (restart_scan_thread) {
adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
restart_scan_thread = false;
}
}
bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
if (payload_size != static_cast<s32>(adapter_payload.size()) ||
adapter_payload[0] != LIBUSB_DT_HID) {
LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
adapter_payload[0]);
if (input_error_counter++ > 20) {
LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
adapter_input_thread_running = false;
restart_scan_thread = true;
}
return false;
}
input_error_counter = 0;
return true;
}
void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
for (std::size_t port = 0; port < pads.size(); ++port) {
const std::size_t offset = 1 + (9 * port);
const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
UpdatePadType(port, type);
if (DeviceConnected(port)) {
const u8 b1 = adapter_payload[offset + 1];
const u8 b2 = adapter_payload[offset + 2];
UpdateStateButtons(port, b1, b2);
UpdateStateAxes(port, adapter_payload);
if (configuring) {
UpdateYuzuSettings(port);
}
}
}
}
void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
if (pads[port].type == pad_type) {
return;
}
// Device changed reset device and set new type
ResetDevice(port);
pads[port].type = pad_type;
}
void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
if (port >= pads.size()) {
return;
}
static constexpr std::array<PadButton, 8> b1_buttons{
PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT,
PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
PadButton::ButtonA, PadButton::ButtonB, PadButton::ButtonX, PadButton::ButtonY,
PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp,
};
static constexpr std::array<PadButton, 4> b2_buttons{
PadButton::PAD_BUTTON_START,
PadButton::PAD_TRIGGER_Z,
PadButton::PAD_TRIGGER_R,
PadButton::PAD_TRIGGER_L,
PadButton::ButtonStart,
PadButton::TriggerZ,
PadButton::TriggerR,
PadButton::TriggerL,
};
pads[port].buttons = 0;
for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
if ((b1 & (1U << i)) != 0) {
pads[port].buttons =
static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i]));
pads[port].last_button = b1_buttons[i];
}
}
for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
if ((b2 & (1U << j)) != 0) {
pads[port].buttons =
static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j]));
pads[port].last_button = b2_buttons[j];
}
}
}
void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
if (port >= pads.size()) {
return;
}
const std::size_t offset = 1 + (9 * port);
static constexpr std::array<PadAxes, 6> axes{
PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
};
if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) {
// Controller may have been disconnected, recalibrate if reconnected.
get_origin[port] = true;
}
if (adapter_controllers_status[port] != ControllerTypes::None) {
const u8 b1 = adapter_payload[offset + 1];
const u8 b2 = adapter_payload[offset + 2];
for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
if ((b1 & (1U << i)) != 0) {
pad.button |= static_cast<u16>(b1_buttons[i]);
}
for (const PadAxes axis : axes) {
const auto index = static_cast<std::size_t>(axis);
const u8 axis_value = adapter_payload[offset + 3 + index];
if (pads[port].axis_origin[index] == 255) {
pads[port].axis_origin[index] = axis_value;
}
for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
if ((b2 & (1U << j)) != 0) {
pad.button |= static_cast<u16>(b2_buttons[j]);
}
}
for (PadAxes axis : axes) {
const std::size_t index = static_cast<std::size_t>(axis);
pad.axis_values[index] = adapter_payload[offset + 3 + index];
}
if (get_origin[port]) {
origin_status[port].axis_values = pad.axis_values;
get_origin[port] = false;
}
}
return pad;
}
void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
for (const auto& button : PadButtonArray) {
const u16 button_value = static_cast<u16>(button);
state.buttons.insert_or_assign(button_value, pad.button & button_value);
}
for (size_t i = 0; i < pad.axis_values.size(); ++i) {
state.axes.insert_or_assign(static_cast<u8>(i), pad.axis_values[i]);
pads[port].axis_values[index] =
static_cast<s16>(axis_value - pads[port].axis_origin[index]);
}
}
void Adapter::Read() {
LOG_DEBUG(Input, "GC Adapter Read() thread started");
void Adapter::UpdateYuzuSettings(std::size_t port) {
if (port >= pads.size()) {
return;
}
int payload_size;
std::array<u8, 37> adapter_payload;
std::array<GCPadStatus, 4> pads;
constexpr u8 axis_threshold = 50;
GCPadStatus pad_status = {.port = port};
while (adapter_thread_running) {
libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
sizeof(adapter_payload), &payload_size, 16);
if (pads[port].buttons != 0) {
pad_status.button = pads[port].last_button;
pad_queue.Push(pad_status);
}
if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
LOG_ERROR(Input,
"Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
payload_size, adapter_payload[0]);
adapter_thread_running = false; // error reading from adapter, stop reading.
break;
// Accounting for a threshold here to ensure an intentional press
for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
const s16 value = pads[port].axis_values[i];
if (value > axis_threshold || value < -axis_threshold) {
pad_status.axis = static_cast<PadAxes>(i);
pad_status.axis_value = value;
pad_status.axis_threshold = axis_threshold;
pad_queue.Push(pad_status);
}
for (std::size_t port = 0; port < pads.size(); ++port) {
pads[port] = GetPadStatus(port, adapter_payload);
if (DeviceConnected(port) && configuring) {
if (pads[port].button != 0) {
pad_queue[port].Push(pads[port]);
}
}
}
// Accounting for a threshold here to ensure an intentional press
for (size_t i = 0; i < pads[port].axis_values.size(); ++i) {
const u8 value = pads[port].axis_values[i];
const u8 origin = origin_status[port].axis_values[i];
void Adapter::UpdateVibrations() {
// Use 8 states to keep the switching between on/off fast enough for
// a human to not notice the difference between switching from on/off
// More states = more rumble strengths = slower update time
constexpr u8 vibration_states = 8;
if (value > origin + pads[port].THRESHOLD ||
value < origin - pads[port].THRESHOLD) {
pads[port].axis = static_cast<PadAxes>(i);
pads[port].axis_value = pads[port].axis_values[i];
pad_queue[port].Push(pads[port]);
}
}
}
PadToState(pads[port], state[port]);
vibration_counter = (vibration_counter + 1) % vibration_states;
for (GCController& pad : pads) {
const bool vibrate = pad.rumble_amplitude > vibration_counter;
vibration_changed |= vibrate != pad.enable_vibration;
pad.enable_vibration = vibrate;
}
SendVibrations();
}
void Adapter::SendVibrations() {
if (!rumble_enabled || !vibration_changed) {
return;
}
s32 size{};
constexpr u8 rumble_command = 0x11;
const u8 p1 = pads[0].enable_vibration;
const u8 p2 = pads[1].enable_vibration;
const u8 p3 = pads[2].enable_vibration;
const u8 p4 = pads[3].enable_vibration;
std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
static_cast<s32>(payload.size()), &size, 16);
if (err) {
LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
if (output_error_counter++ > 5) {
LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
rumble_enabled = false;
}
std::this_thread::yield();
return;
}
output_error_counter = 0;
vibration_changed = false;
}
bool Adapter::RumblePlay(std::size_t port, f32 amplitude) {
amplitude = std::clamp(amplitude, 0.0f, 1.0f);
const auto raw_amp = static_cast<u8>(amplitude * 0x8);
pads[port].rumble_amplitude = raw_amp;
return rumble_enabled;
}
void Adapter::AdapterScanThread() {
adapter_scan_thread_running = true;
adapter_input_thread_running = false;
if (adapter_input_thread.joinable()) {
adapter_input_thread.join();
}
ClearLibusbHandle();
ResetDevices();
while (adapter_scan_thread_running && !adapter_input_thread_running) {
Setup();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void Adapter::Setup() {
// Initialize all controllers as unplugged
adapter_controllers_status.fill(ControllerTypes::None);
// Initialize all ports to store axis origin values
get_origin.fill(true);
usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337);
// pointer to list of connected usb devices
libusb_device** devices{};
// populate the list of devices, get the count
const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
if (device_count < 0) {
LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
if (usb_adapter_handle == NULL) {
return;
}
if (!CheckDeviceAccess()) {
ClearLibusbHandle();
return;
}
if (devices != nullptr) {
for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
if (CheckDeviceAccess(devices[index])) {
// GC Adapter found and accessible, registering it
GetGCEndpoint(devices[index]);
break;
}
}
libusb_free_device_list(devices, 1);
libusb_device* device = libusb_get_device(usb_adapter_handle);
LOG_INFO(Input, "GC adapter is now connected");
// GC Adapter found and accessible, registering it
if (GetGCEndpoint(device)) {
adapter_scan_thread_running = false;
adapter_input_thread_running = true;
rumble_enabled = true;
input_error_counter = 0;
output_error_counter = 0;
adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
}
}
bool Adapter::CheckDeviceAccess(libusb_device* device) {
libusb_device_descriptor desc;
const int get_descriptor_error = libusb_get_device_descriptor(device, &desc);
if (get_descriptor_error) {
// could not acquire the descriptor, no point in trying to use it.
LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}",
get_descriptor_error);
return false;
bool Adapter::CheckDeviceAccess() {
// This fixes payload problems from offbrand GCAdapters
const s32 control_transfer_error =
libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
if (control_transfer_error < 0) {
LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
}
if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
// This isn't the device we are looking for.
return false;
}
const int open_error = libusb_open(device, &usb_adapter_handle);
if (open_error == LIBUSB_ERROR_ACCESS) {
LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.",
desc.idVendor, desc.idProduct);
return false;
}
if (open_error) {
LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error);
return false;
}
int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
if (kernel_driver_error == 1) {
kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
@@ -235,13 +311,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
return true;
}
void Adapter::GetGCEndpoint(libusb_device* device) {
bool Adapter::GetGCEndpoint(libusb_device* device) {
libusb_config_descriptor* config = nullptr;
const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
if (config_descriptor_return != LIBUSB_SUCCESS) {
LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
config_descriptor_return);
return;
return false;
}
for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
@@ -250,7 +326,7 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
for (u8 e = 0; e < interface->bNumEndpoints; e++) {
const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) != 0) {
input_endpoint = endpoint->bEndpointAddress;
} else {
output_endpoint = endpoint->bEndpointAddress;
@@ -263,31 +339,51 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
unsigned char clear_payload = 0x13;
libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
sizeof(clear_payload), nullptr, 16);
adapter_thread_running = true;
adapter_input_thread = std::thread(&Adapter::Read, this);
return true;
}
Adapter::~Adapter() {
Reset();
}
void Adapter::JoinThreads() {
restart_scan_thread = false;
adapter_input_thread_running = false;
adapter_scan_thread_running = false;
void Adapter::Reset() {
if (adapter_thread_running) {
adapter_thread_running = false;
if (adapter_scan_thread.joinable()) {
adapter_scan_thread.join();
}
if (adapter_input_thread.joinable()) {
adapter_input_thread.join();
}
}
adapter_controllers_status.fill(ControllerTypes::None);
get_origin.fill(true);
void Adapter::ClearLibusbHandle() {
if (usb_adapter_handle) {
libusb_release_interface(usb_adapter_handle, 1);
libusb_close(usb_adapter_handle);
usb_adapter_handle = nullptr;
}
}
void Adapter::ResetDevices() {
for (std::size_t i = 0; i < pads.size(); ++i) {
ResetDevice(i);
}
}
void Adapter::ResetDevice(std::size_t port) {
pads[port].type = ControllerTypes::None;
pads[port].enable_vibration = false;
pads[port].rumble_amplitude = 0;
pads[port].buttons = 0;
pads[port].last_button = PadButton::Undefined;
pads[port].axis_values.fill(0);
pads[port].axis_origin.fill(255);
}
void Adapter::Reset() {
JoinThreads();
ClearLibusbHandle();
ResetDevices();
if (libusb_ctx) {
libusb_exit(libusb_ctx);
@@ -296,11 +392,11 @@ void Adapter::Reset() {
std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
std::vector<Common::ParamPackage> devices;
for (std::size_t port = 0; port < state.size(); ++port) {
for (std::size_t port = 0; port < pads.size(); ++port) {
if (!DeviceConnected(port)) {
continue;
}
std::string name = fmt::format("Gamecube Controller {}", port);
std::string name = fmt::format("Gamecube Controller {}", port + 1);
devices.emplace_back(Common::ParamPackage{
{"class", "gcpad"},
{"display", std::move(name)},
@@ -317,18 +413,18 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
// This list also excludes any button that can't be really mapped
static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
switch_to_gcadapter_button = {
std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A},
{Settings::NativeButton::B, PadButton::PAD_BUTTON_B},
{Settings::NativeButton::X, PadButton::PAD_BUTTON_X},
{Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y},
{Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START},
{Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT},
{Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP},
{Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT},
{Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN},
{Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L},
{Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R},
{Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z},
std::pair{Settings::NativeButton::A, PadButton::ButtonA},
{Settings::NativeButton::B, PadButton::ButtonB},
{Settings::NativeButton::X, PadButton::ButtonX},
{Settings::NativeButton::Y, PadButton::ButtonY},
{Settings::NativeButton::Plus, PadButton::ButtonStart},
{Settings::NativeButton::DLeft, PadButton::ButtonLeft},
{Settings::NativeButton::DUp, PadButton::ButtonUp},
{Settings::NativeButton::DRight, PadButton::ButtonRight},
{Settings::NativeButton::DDown, PadButton::ButtonDown},
{Settings::NativeButton::SL, PadButton::TriggerL},
{Settings::NativeButton::SR, PadButton::TriggerR},
{Settings::NativeButton::R, PadButton::TriggerZ},
};
if (!params.Has("port")) {
return {};
@@ -351,8 +447,10 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
Common::ParamPackage button_params({{"engine", "gcpad"}});
button_params.Set("port", params.Get("port", 0));
button_params.Set("button", static_cast<int>(PadButton::PAD_STICK));
button_params.Set("axis", static_cast<int>(gcadapter_axis));
button_params.Set("button", static_cast<s32>(PadButton::Stick));
button_params.Set("axis", static_cast<s32>(gcadapter_axis));
button_params.Set("threshold", 0.5f);
button_params.Set("direction", "+");
mapping.insert_or_assign(switch_button, std::move(button_params));
}
return mapping;
@@ -381,46 +479,33 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
}
bool Adapter::DeviceConnected(std::size_t port) const {
return adapter_controllers_status[port] != ControllerTypes::None;
}
void Adapter::ResetDeviceType(std::size_t port) {
adapter_controllers_status[port] = ControllerTypes::None;
return pads[port].type != ControllerTypes::None;
}
void Adapter::BeginConfiguration() {
get_origin.fill(true);
for (auto& pq : pad_queue) {
pq.Clear();
}
pad_queue.Clear();
configuring = true;
}
void Adapter::EndConfiguration() {
for (auto& pq : pad_queue) {
pq.Clear();
}
pad_queue.Clear();
configuring = false;
}
std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() {
Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
return pad_queue;
}
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const {
const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
return pad_queue;
}
std::array<GCState, 4>& Adapter::GetPadState() {
return state;
GCController& Adapter::GetPadState(std::size_t port) {
return pads.at(port);
}
const std::array<GCState, 4>& Adapter::GetPadState() const {
return state;
}
int Adapter::GetOriginValue(int port, int axis) const {
return origin_status[port].axis_values[axis];
const GCController& Adapter::GetPadState(std::size_t port) const {
return pads.at(port);
}
} // namespace GCAdapter

View File

@@ -19,24 +19,23 @@ struct libusb_device_handle;
namespace GCAdapter {
enum class PadButton {
PAD_BUTTON_LEFT = 0x0001,
PAD_BUTTON_RIGHT = 0x0002,
PAD_BUTTON_DOWN = 0x0004,
PAD_BUTTON_UP = 0x0008,
PAD_TRIGGER_Z = 0x0010,
PAD_TRIGGER_R = 0x0020,
PAD_TRIGGER_L = 0x0040,
PAD_BUTTON_A = 0x0100,
PAD_BUTTON_B = 0x0200,
PAD_BUTTON_X = 0x0400,
PAD_BUTTON_Y = 0x0800,
PAD_BUTTON_START = 0x1000,
Undefined = 0x0000,
ButtonLeft = 0x0001,
ButtonRight = 0x0002,
ButtonDown = 0x0004,
ButtonUp = 0x0008,
TriggerZ = 0x0010,
TriggerR = 0x0020,
TriggerL = 0x0040,
ButtonA = 0x0100,
ButtonB = 0x0200,
ButtonX = 0x0400,
ButtonY = 0x0800,
ButtonStart = 0x1000,
// Below is for compatibility with "AxisButton" type
PAD_STICK = 0x2000,
Stick = 0x2000,
};
extern const std::array<PadButton, 12> PadButtonArray;
enum class PadAxes : u8 {
StickX,
StickY,
@@ -47,89 +46,122 @@ enum class PadAxes : u8 {
Undefined,
};
enum class ControllerTypes {
None,
Wired,
Wireless,
};
struct GCPadStatus {
u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
std::size_t port{};
std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes
static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
u8 port{};
PadAxes axis{PadAxes::Undefined};
u8 axis_value{255};
s16 axis_value{};
u8 axis_threshold{50};
};
struct GCState {
std::unordered_map<int, bool> buttons;
std::unordered_map<int, u16> axes;
struct GCController {
ControllerTypes type{};
bool enable_vibration{};
u8 rumble_amplitude{};
u16 buttons{};
PadButton last_button{};
std::array<s16, 6> axis_values{};
std::array<u8, 6> axis_origin{};
};
enum class ControllerTypes { None, Wired, Wireless };
class Adapter {
public:
/// Initialize the GC Adapter capture and read sequence
Adapter();
/// Close the adapter read thread and release the adapter
~Adapter();
/// Request a vibration for a controlelr
bool RumblePlay(std::size_t port, f32 amplitude);
/// Used for polling
void BeginConfiguration();
void EndConfiguration();
std::vector<Common::ParamPackage> GetInputDevices() const;
InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
Common::SPSCQueue<GCPadStatus>& GetPadQueue();
const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
GCController& GetPadState(std::size_t port);
const GCController& GetPadState(std::size_t port) const;
/// Returns true if there is a device connected to port
bool DeviceConnected(std::size_t port) const;
std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
std::array<GCState, 4>& GetPadState();
const std::array<GCState, 4>& GetPadState() const;
int GetOriginValue(int port, int axis) const;
/// Used for automapping features
std::vector<Common::ParamPackage> GetInputDevices() const;
InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
private:
GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
using AdapterPayload = std::array<u8, 37>;
void PadToState(const GCPadStatus& pad, GCState& state);
void UpdatePadType(std::size_t port, ControllerTypes pad_type);
void UpdateControllers(const AdapterPayload& adapter_payload);
void UpdateYuzuSettings(std::size_t port);
void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
void UpdateVibrations();
void Read();
void AdapterInputThread();
/// Resets status of device connected to port
void ResetDeviceType(std::size_t port);
void AdapterScanThread();
/// Returns true if we successfully gain access to GC Adapter
bool CheckDeviceAccess(libusb_device* device);
bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
/// Captures GC Adapter endpoint address,
void GetGCEndpoint(libusb_device* device);
/// For shutting down, clear all data, join all threads, release usb
void Reset();
// Updates vibration state of all controllers
void SendVibrations();
/// For use in initialization, querying devices to find the adapter
void Setup();
/// Resets status of all GC controller devices to a disconected state
void ResetDevices();
/// Resets status of device connected to a disconected state
void ResetDevice(std::size_t port);
/// Returns true if we successfully gain access to GC Adapter
bool CheckDeviceAccess();
/// Captures GC Adapter endpoint address
/// Returns true if the endpoind was set correctly
bool GetGCEndpoint(libusb_device* device);
/// For shutting down, clear all data, join all threads, release usb
void Reset();
// Join all threads
void JoinThreads();
// Release usb handles
void ClearLibusbHandle();
libusb_device_handle* usb_adapter_handle = nullptr;
std::array<GCController, 4> pads;
Common::SPSCQueue<GCPadStatus> pad_queue;
std::thread adapter_input_thread;
bool adapter_thread_running;
std::thread adapter_scan_thread;
bool adapter_input_thread_running;
bool adapter_scan_thread_running;
bool restart_scan_thread;
libusb_context* libusb_ctx;
u8 input_endpoint = 0;
u8 output_endpoint = 0;
u8 input_endpoint{0};
u8 output_endpoint{0};
u8 input_error_counter{0};
u8 output_error_counter{0};
int vibration_counter{0};
bool configuring = false;
std::array<GCState, 4> state;
std::array<bool, 4> get_origin;
std::array<GCPadStatus, 4> origin_status;
std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
std::array<ControllerTypes, 4> adapter_controllers_status{};
bool configuring{false};
bool rumble_enabled{true};
bool vibration_changed{true};
};
} // namespace GCAdapter

View File

@@ -15,36 +15,43 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice {
public:
explicit GCButton(int port_, int button_, const GCAdapter::Adapter* adapter)
explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
: port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override;
bool GetStatus() const override {
if (gcadapter->DeviceConnected(port)) {
return gcadapter->GetPadState()[port].buttons.at(button);
return (gcadapter->GetPadState(port).buttons & button) != 0;
}
return false;
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
const auto new_amp =
static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
return gcadapter->RumblePlay(port, new_amp);
}
private:
const int port;
const int button;
const GCAdapter::Adapter* gcadapter;
const u32 port;
const s32 button;
GCAdapter::Adapter* gcadapter;
};
class GCAxisButton final : public Input::ButtonDevice {
public:
explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
const GCAdapter::Adapter* adapter)
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
gcadapter(adapter),
origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
gcadapter(adapter) {}
bool GetStatus() const override {
if (gcadapter->DeviceConnected(port)) {
const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis);
const float axis_value = (current_axis_value - origin_value) / 128.0f;
const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
const float axis_value = current_axis_value / 128.0f;
if (trigger_if_greater) {
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
@@ -56,12 +63,11 @@ public:
}
private:
const int port;
const int axis;
const u32 port;
const u32 axis;
float threshold;
bool trigger_if_greater;
const GCAdapter::Adapter* gcadapter;
const float origin_value;
};
GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
@@ -70,10 +76,10 @@ GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
GCButton::~GCButton() = default;
std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
const int button_id = params.Get("button", 0);
const int port = params.Get("port", 0);
const auto button_id = params.Get("button", 0);
const auto port = static_cast<u32>(params.Get("port", 0));
constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK);
constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
// button is not an axis/stick button
if (button_id != PAD_STICK_ID) {
@@ -106,32 +112,25 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
Common::ParamPackage params;
GCAdapter::GCPadStatus pad;
auto& queue = adapter->GetPadQueue();
for (std::size_t port = 0; port < queue.size(); ++port) {
while (queue[port].Pop(pad)) {
// This while loop will break on the earliest detected button
params.Set("engine", "gcpad");
params.Set("port", static_cast<int>(port));
for (const auto& button : GCAdapter::PadButtonArray) {
const u16 button_value = static_cast<u16>(button);
if (pad.button & button_value) {
params.Set("button", button_value);
break;
}
}
while (queue.Pop(pad)) {
// This while loop will break on the earliest detected button
params.Set("engine", "gcpad");
params.Set("port", static_cast<s32>(pad.port));
if (pad.button != GCAdapter::PadButton::Undefined) {
params.Set("button", static_cast<u16>(pad.button));
}
// For Axis button implementation
if (pad.axis != GCAdapter::PadAxes::Undefined) {
params.Set("axis", static_cast<u8>(pad.axis));
params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK));
if (pad.axis_value > 128) {
params.Set("direction", "+");
params.Set("threshold", "0.25");
} else {
params.Set("direction", "-");
params.Set("threshold", "-0.25");
}
break;
// For Axis button implementation
if (pad.axis != GCAdapter::PadAxes::Undefined) {
params.Set("axis", static_cast<u8>(pad.axis));
params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
params.Set("threshold", "0.25");
if (pad.axis_value > 0) {
params.Set("direction", "+");
} else {
params.Set("direction", "-");
}
break;
}
}
return params;
@@ -149,25 +148,24 @@ void GCButtonFactory::EndConfiguration() {
class GCAnalog final : public Input::AnalogDevice {
public:
GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_,
const GCAdapter::Adapter* adapter, float range_)
explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_,
const GCAdapter::Adapter* adapter, float range_)
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))),
origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))),
range(range_) {}
float GetAxis(int axis) const {
float GetAxis(u32 axis) const {
if (gcadapter->DeviceConnected(port)) {
std::lock_guard lock{mutex};
const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range);
const auto axis_value =
static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
return (axis_value) / (100.0f * range);
}
return 0.0f;
}
std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
float x = GetAxis(axis_x);
float y = GetAxis(axis_y);
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
float x = GetAxis(analog_axis_x);
float y = GetAxis(analog_axis_y);
// Make sure the coordinates are in the unit circle,
// otherwise normalize it.
@@ -208,13 +206,11 @@ public:
}
private:
const int port;
const int axis_x;
const int axis_y;
const u32 port;
const u32 axis_x;
const u32 axis_y;
const float deadzone;
const GCAdapter::Adapter* gcadapter;
const float origin_value_x;
const float origin_value_y;
const float range;
mutable std::mutex mutex;
};
@@ -231,11 +227,11 @@ GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
* - "axis_y": the index of the axis to be bind as y-axis
*/
std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
const auto port = static_cast<u32>(params.Get("port", 0));
const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
}
@@ -252,26 +248,44 @@ void GCAnalogFactory::EndConfiguration() {
Common::ParamPackage GCAnalogFactory::GetNextInput() {
GCAdapter::GCPadStatus pad;
Common::ParamPackage params;
auto& queue = adapter->GetPadQueue();
for (std::size_t port = 0; port < queue.size(); ++port) {
while (queue[port].Pop(pad)) {
if (pad.axis == GCAdapter::PadAxes::Undefined ||
std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) {
continue;
}
// An analog device needs two axes, so we need to store the axis for later and wait for
// a second input event. The axes also must be from the same joystick.
const u8 axis = static_cast<u8>(pad.axis);
if (analog_x_axis == -1) {
analog_x_axis = axis;
controller_number = static_cast<int>(port);
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
controller_number == static_cast<int>(port)) {
analog_y_axis = axis;
}
while (queue.Pop(pad)) {
if (pad.button != GCAdapter::PadButton::Undefined) {
params.Set("engine", "gcpad");
params.Set("port", static_cast<s32>(pad.port));
params.Set("button", static_cast<u16>(pad.button));
return params;
}
if (pad.axis == GCAdapter::PadAxes::Undefined ||
std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
continue;
}
// An analog device needs two axes, so we need to store the axis for later and wait for
// a second input event. The axes also must be from the same joystick.
const u8 axis = static_cast<u8>(pad.axis);
if (axis == 0 || axis == 1) {
analog_x_axis = 0;
analog_y_axis = 1;
controller_number = static_cast<s32>(pad.port);
break;
}
if (axis == 2 || axis == 3) {
analog_x_axis = 2;
analog_y_axis = 3;
controller_number = static_cast<s32>(pad.port);
break;
}
if (analog_x_axis == -1) {
analog_x_axis = axis;
controller_number = static_cast<s32>(pad.port);
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
controller_number == static_cast<s32>(pad.port)) {
analog_y_axis = axis;
break;
}
}
Common::ParamPackage params;
if (analog_x_axis != -1 && analog_y_axis != -1) {
params.Set("engine", "gcpad");
params.Set("port", controller_number);

View File

@@ -49,8 +49,9 @@ public:
void ChangeKeyStatus(int key_code, bool pressed) {
std::lock_guard guard{mutex};
for (const KeyButtonPair& pair : list) {
if (pair.key_code == key_code)
if (pair.key_code == key_code) {
pair.key_button->status.store(pressed);
}
}
}
@@ -73,7 +74,7 @@ KeyButton::~KeyButton() {
}
std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
int key_code = params.Get("code", 0);
const int key_code = params.Get("code", 0);
std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
key_button_list->AddKeyButton(key_code, button.get());
return button;

View File

@@ -196,6 +196,10 @@ ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPacka
return impl->GetButtonMappingForDevice(device);
}
MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const {
return impl->GetMotionMappingForDevice(device);
}
GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
return impl->gcanalog.get();
}

View File

@@ -18,11 +18,11 @@ namespace InputCommon {
// Implementation class of the motion emulation device
class MotionEmuDevice {
public:
MotionEmuDevice(int update_millisecond, float sensitivity)
: update_millisecond(update_millisecond),
explicit MotionEmuDevice(int update_millisecond_, float sensitivity_)
: update_millisecond(update_millisecond_),
update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::milliseconds(update_millisecond))),
sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
sensitivity(sensitivity_), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
~MotionEmuDevice() {
if (motion_emu_thread.joinable()) {
@@ -37,16 +37,18 @@ public:
}
void Tilt(int x, int y) {
auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
if (is_tilting) {
std::lock_guard guard{tilt_mutex};
if (mouse_move.x == 0 && mouse_move.y == 0) {
tilt_angle = 0;
} else {
tilt_direction = mouse_move.Cast<float>();
tilt_angle =
std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f);
}
if (!is_tilting) {
return;
}
std::lock_guard guard{tilt_mutex};
const auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
if (mouse_move.x == 0 && mouse_move.y == 0) {
tilt_angle = 0;
} else {
tilt_direction = mouse_move.Cast<float>();
tilt_angle =
std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f);
}
}
@@ -86,11 +88,10 @@ private:
void MotionEmuThread() {
auto update_time = std::chrono::steady_clock::now();
Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0);
Common::Quaternion<float> old_q;
while (!shutdown_event.WaitUntil(update_time)) {
update_time += update_duration;
old_q = q;
const Common::Quaternion<float> old_q = q;
{
std::lock_guard guard{tilt_mutex};
@@ -100,14 +101,14 @@ private:
Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle);
}
auto inv_q = q.Inverse();
const auto inv_q = q.Inverse();
// Set the gravity vector in world space
auto gravity = Common::MakeVec(0.0f, -1.0f, 0.0f);
// Find the angular rate vector in world space
auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
angular_rate *= 1000 / update_millisecond / Common::PI * 180;
angular_rate *= static_cast<float>(1000 / update_millisecond) / Common::PI * 180.0f;
// Transform the two vectors from world space to 3DS space
gravity = QuaternionRotate(inv_q, gravity);
@@ -136,7 +137,7 @@ private:
// can forward all the inputs to the implementation only when it is valid.
class MotionEmuDeviceWrapper : public Input::MotionDevice {
public:
MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
explicit MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
}
@@ -148,8 +149,8 @@ public:
};
std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
int update_period = params.Get("update_period", 100);
float sensitivity = params.Get("sensitivity", 0.01f);
const int update_period = params.Get("update_period", 100);
const float sensitivity = params.Get("sensitivity", 0.01f);
auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
// Previously created device is disconnected here. Having two motion devices for 3DS is not
// expected.

View File

@@ -11,7 +11,7 @@ class MotionKey final : public Input::MotionDevice {
public:
using Button = std::unique_ptr<Input::ButtonDevice>;
MotionKey(Button key_) : key(std::move(key_)) {}
explicit MotionKey(Button key_) : key(std::move(key_)) {}
Input::MotionStatus GetStatus() const override {

View File

@@ -8,8 +8,7 @@
namespace InputCommon {
MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd)
: kp(new_kp), ki(new_ki), kd(new_kd), quat{{0, 0, -1}, 0} {}
MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) : kp(new_kp), ki(new_ki), kd(new_kd) {}
void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
accel = acceleration;
@@ -59,7 +58,7 @@ bool MotionInput::IsCalibrated(f32 sensitivity) const {
}
void MotionInput::UpdateRotation(u64 elapsed_time) {
const f32 sample_period = elapsed_time / 1000000.0f;
const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
if (sample_period > 0.1f) {
return;
}
@@ -75,7 +74,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
f32 q2 = quat.xyz[0];
f32 q3 = quat.xyz[1];
f32 q4 = quat.xyz[2];
const f32 sample_period = elapsed_time / 1000000.0f;
const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
// Ignore invalid elapsed time
if (sample_period > 0.1f) {
@@ -203,21 +202,21 @@ Input::MotionStatus MotionInput::GetRandomMotion(int accel_magnitude, int gyro_m
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<s16> distribution(-1000, 1000);
const Common::Vec3f gyroscope = {
distribution(gen) * 0.001f,
distribution(gen) * 0.001f,
distribution(gen) * 0.001f,
const Common::Vec3f gyroscope{
static_cast<f32>(distribution(gen)) * 0.001f,
static_cast<f32>(distribution(gen)) * 0.001f,
static_cast<f32>(distribution(gen)) * 0.001f,
};
const Common::Vec3f accelerometer = {
distribution(gen) * 0.001f,
distribution(gen) * 0.001f,
distribution(gen) * 0.001f,
const Common::Vec3f accelerometer{
static_cast<f32>(distribution(gen)) * 0.001f,
static_cast<f32>(distribution(gen)) * 0.001f,
static_cast<f32>(distribution(gen)) * 0.001f,
};
const Common::Vec3f rotation = {};
const std::array<Common::Vec3f, 3> orientation = {
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
constexpr Common::Vec3f rotation;
constexpr std::array orientation{
Common::Vec3f{1.0f, 0.0f, 0.0f},
Common::Vec3f{0.0f, 1.0f, 0.0f},
Common::Vec3f{0.0f, 0.0f, 1.0f},
};
return {accelerometer * accel_magnitude, gyroscope * gyro_magnitude, rotation, orientation};
}
@@ -247,9 +246,6 @@ void MotionInput::SetOrientationFromAccelerometer() {
const f32 sample_period = 0.015f;
const auto normal_accel = accel.Normalized();
const f32 ax = -normal_accel.x;
const f32 ay = normal_accel.y;
const f32 az = -normal_accel.z;
while (!IsCalibrated(0.01f) && ++iterations < 100) {
// Short name local variable for readability
@@ -258,7 +254,7 @@ void MotionInput::SetOrientationFromAccelerometer() {
f32 q3 = quat.xyz[1];
f32 q4 = quat.xyz[2];
Common::Vec3f rad_gyro = {};
Common::Vec3f rad_gyro;
const f32 ax = -normal_accel.x;
const f32 ay = normal_accel.y;
const f32 az = -normal_accel.z;

View File

@@ -22,7 +22,7 @@ public:
MotionInput& operator=(MotionInput&&) = default;
void SetAcceleration(const Common::Vec3f& acceleration);
void SetGyroscope(const Common::Vec3f& acceleration);
void SetGyroscope(const Common::Vec3f& gyroscope);
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
void SetGyroDrift(const Common::Vec3f& drift);
void SetGyroThreshold(f32 threshold);
@@ -49,16 +49,16 @@ private:
void SetOrientationFromAccelerometer();
// PID constants
const f32 kp;
const f32 ki;
const f32 kd;
f32 kp;
f32 ki;
f32 kd;
// PID errors
Common::Vec3f real_error;
Common::Vec3f integral_error;
Common::Vec3f derivative_error;
Common::Quaternion<f32> quat;
Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f};
Common::Vec3f rotations;
Common::Vec3f accel;
Common::Vec3f gyro;

View File

@@ -56,9 +56,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
class SDLJoystick {
public:
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
SDL_GameController* gamecontroller)
SDL_GameController* game_controller)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
sdl_controller{game_controller, &SDL_GameControllerClose} {}
void SetButton(int button, bool value) {
std::lock_guard lock{mutex};
@@ -77,10 +77,10 @@ public:
float GetAxis(int axis, float range) const {
std::lock_guard lock{mutex};
return state.axes.at(axis) / (32767.0f * range);
return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
}
bool RumblePlay(f32 amp_low, f32 amp_high, int time) {
bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) {
const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF);
const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF);
// Lower drastically the number of state changes
@@ -124,7 +124,7 @@ public:
return std::make_tuple(x, y);
}
const InputCommon::MotionInput& GetMotion() const {
const MotionInput& GetMotion() const {
return motion;
}
@@ -155,15 +155,15 @@ public:
return sdl_joystick.get();
}
void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
sdl_controller.reset(controller);
sdl_joystick.reset(joystick);
}
SDL_GameController* GetSDLGameController() const {
return sdl_controller.get();
}
void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
sdl_joystick.reset(joystick);
sdl_controller.reset(controller);
}
private:
struct State {
std::unordered_map<int, bool> buttons;
@@ -172,83 +172,72 @@ private:
} state;
std::string guid;
int port;
u16 last_state_rumble_high;
u16 last_state_rumble_low;
u16 last_state_rumble_high = 0;
u16 last_state_rumble_low = 0;
std::chrono::time_point<std::chrono::system_clock> last_vibration;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
// motion is initalized without PID values as motion input is not aviable for SDL2
InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
// Motion is initialized without PID values as motion input is not aviable for SDL2
MotionInput motion{0.0f, 0.0f, 0.0f};
};
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
std::lock_guard lock{joystick_map_mutex};
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
while (it->second.size() <= static_cast<std::size_t>(port)) {
auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
nullptr, nullptr);
it->second.emplace_back(std::move(joystick));
}
return it->second[port];
return it->second[static_cast<std::size_t>(port)];
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
return joystick_map[guid].emplace_back(std::move(joystick));
}
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
const auto map_it = joystick_map.find(guid);
if (map_it != joystick_map.end()) {
const auto vec_it =
std::find_if(map_it->second.begin(), map_it->second.end(),
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
return sdl_joystick == joystick->GetSDLJoystick();
});
if (vec_it != map_it->second.end()) {
// This is the common case: There is already an existing SDL_Joystick maped to a
// SDLJoystick. return the SDLJoystick
return *vec_it;
}
// Search for a SDLJoystick without a mapped SDL_Joystick...
const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) {
return !joystick->GetSDLJoystick();
});
if (nullptr_it != map_it->second.end()) {
// ... and map it
(*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
return *nullptr_it;
}
// There is no SDLJoystick without a mapped SDL_Joystick
// Create a new SDLJoystick
const int port = static_cast<int>(map_it->second.size());
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
return map_it->second.emplace_back(std::move(joystick));
if (map_it == joystick_map.end()) {
return nullptr;
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
return joystick_map[guid].emplace_back(std::move(joystick));
const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
[&sdl_joystick](const auto& joystick) {
return joystick->GetSDLJoystick() == sdl_joystick;
});
if (vec_it == map_it->second.end()) {
return nullptr;
}
return *vec_it;
}
void SDLState::InitJoystick(int joystick_index) {
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
SDL_GameController* sdl_gamecontroller = nullptr;
if (SDL_IsGameController(joystick_index)) {
sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
}
if (!sdl_joystick) {
LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
return;
}
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
@@ -257,14 +246,17 @@ void SDLState::InitJoystick(int joystick_index) {
joystick_map[guid].emplace_back(std::move(joystick));
return;
}
auto& joystick_guid_list = joystick_map[guid];
const auto it = std::find_if(
joystick_guid_list.begin(), joystick_guid_list.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
if (it != joystick_guid_list.end()) {
(*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
const auto joystick_it =
std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
[](const auto& joystick) { return !joystick->GetSDLJoystick(); });
if (joystick_it != joystick_guid_list.end()) {
(*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
return;
}
const int port = static_cast<int>(joystick_guid_list.size());
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
joystick_guid_list.emplace_back(std::move(joystick));
@@ -273,22 +265,15 @@ void SDLState::InitJoystick(int joystick_index) {
void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
const std::string guid = GetGUID(sdl_joystick);
std::shared_ptr<SDLJoystick> joystick;
{
std::lock_guard lock{joystick_map_mutex};
// This call to guid is safe since the joystick is guaranteed to be in the map
const auto& joystick_guid_list = joystick_map[guid];
const auto joystick_it =
std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
return joystick->GetSDLJoystick() == sdl_joystick;
});
joystick = *joystick_it;
}
std::lock_guard lock{joystick_map_mutex};
// This call to guid is safe since the joystick is guaranteed to be in the map
const auto& joystick_guid_list = joystick_map[guid];
const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
[&sdl_joystick](const auto& joystick) {
return joystick->GetSDLJoystick() == sdl_joystick;
});
// Destruct SDL_Joystick outside the lock guard because SDL can internally call the
// event callback which locks the mutex again.
joystick->SetSDLJoystick(nullptr, nullptr);
(*joystick_it)->SetSDLJoystick(nullptr, nullptr);
}
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -392,8 +377,8 @@ private:
class SDLAnalog final : public Input::AnalogDevice {
public:
SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_,
float range_)
explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
float deadzone_, float range_)
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_),
range(range_) {}
@@ -672,13 +657,13 @@ SDLState::SDLState() {
RegisterFactory<ButtonDevice>("sdl", button_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
// If the frontend is going to manage the event loop, then we dont start one here
start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
// If the frontend is going to manage the event loop, then we don't start one here
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
return;
}
has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
}
@@ -723,8 +708,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
std::vector<Common::ParamPackage> devices;
for (const auto& [key, value] : joystick_map) {
for (const auto& joystick : value) {
auto joy = joystick->GetSDLJoystick();
if (auto controller = joystick->GetSDLGameController()) {
if (auto* const controller = joystick->GetSDLGameController()) {
std::string name =
fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
devices.emplace_back(Common::ParamPackage{
@@ -733,7 +717,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
{"guid", joystick->GetGUID()},
{"port", std::to_string(joystick->GetPort())},
});
} else if (joy) {
} else if (auto* const joy = joystick->GetSDLJoystick()) {
std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
devices.emplace_back(Common::ParamPackage{
{"class", "sdl"},
@@ -748,7 +732,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
}
namespace {
Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
float value = 0.1f) {
Common::ParamPackage params({{"engine", "sdl"}});
params.Set("port", port);
@@ -764,7 +748,7 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid
return params;
}
Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
Common::ParamPackage params({{"engine", "sdl"}});
params.Set("port", port);
params.Set("guid", std::move(guid));
@@ -772,7 +756,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
return params;
}
Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
Common::ParamPackage params({{"engine", "sdl"}});
params.Set("port", port);
@@ -800,19 +784,27 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
switch (event.type) {
case SDL_JOYAXISMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jaxis.axis, event.jaxis.value);
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
static_cast<s32>(event.jaxis.axis),
event.jaxis.value);
}
break;
}
case SDL_JOYBUTTONUP: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jbutton.button);
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
static_cast<s32>(event.jbutton.button));
}
break;
}
case SDL_JOYHATMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jhat.hat, event.jhat.value);
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
static_cast<s32>(event.jhat.hat),
static_cast<s32>(event.jhat.value));
}
break;
}
}
return {};
@@ -821,19 +813,27 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
switch (event.type) {
case SDL_JOYAXISMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jaxis.axis, event.jaxis.value);
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
static_cast<s32>(event.jaxis.axis),
event.jaxis.value);
}
break;
}
case SDL_JOYBUTTONUP: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jbutton.button);
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
static_cast<s32>(event.jbutton.button));
}
break;
}
case SDL_JOYHATMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jhat.hat, event.jhat.value);
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
static_cast<s32>(event.jhat.hat),
static_cast<s32>(event.jhat.value));
}
break;
}
}
return {};
@@ -1061,9 +1061,8 @@ public:
// Simplify controller config by testing if game controller support is enabled.
if (event.type == SDL_JOYAXISMOTION) {
const auto axis = event.jaxis.axis;
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
const auto controller = joystick->GetSDLGameController();
if (controller) {
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
auto* const controller = joystick->GetSDLGameController()) {
const auto axis_left_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
.value.axis;
@@ -1097,12 +1096,13 @@ public:
}
if (analog_x_axis != -1 && analog_y_axis != -1) {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
analog_x_axis, analog_y_axis);
analog_x_axis = -1;
analog_y_axis = -1;
return params;
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
analog_x_axis, analog_y_axis);
analog_x_axis = -1;
analog_y_axis = -1;
return params;
}
}
return {};
}

View File

@@ -331,8 +331,6 @@ struct PlayerInput {
ButtonsRaw buttons;
AnalogsRaw analogs;
MotionRaw motions;
std::string lstick_mod;
std::string rstick_mod;
u32 body_color_left;
u32 body_color_right;

View File

@@ -11,9 +11,11 @@ namespace InputCommon {
class TouchFromButtonDevice final : public Input::TouchDevice {
public:
TouchFromButtonDevice() {
for (const auto& config_entry :
Settings::values.touch_from_button_maps[Settings::values.touch_from_button_map_index]
.buttons) {
const auto button_index =
static_cast<std::size_t>(Settings::values.touch_from_button_map_index);
const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons;
for (const auto& config_entry : buttons) {
const Common::ParamPackage package{config_entry};
map.emplace_back(
Input::CreateDevice<Input::ButtonDevice>(config_entry),

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