Compare commits

..

292 Commits

Author SHA1 Message Date
Lioncash
5ccf2a7b82 input_common/sdl/sdl_impl: Correct logging string in SDLState constructor
If this path was ever taken, a runtime exception would occur due to the
lack of a formatting specifier to insert the error code into the format
string.
2019-06-03 16:56:47 -04:00
Lioncash
cfac942e63 input_common/sdl/sdl_impl: Move documentation comments to header where applicable
Places the documentation comments with the rest of SDLState's member
function documentation.
2019-06-03 16:56:47 -04:00
Lioncash
b9b23c98ff input_common/sdl/sdl_impl: Amend names for axes for SDLAnalogPoller
Adds another underscore to clearly indicate the axis names.
2019-06-03 16:56:47 -04:00
Lioncash
50048d9f5a input_common/sdl/sdl_impl: Mark variables const where applicable
Make it explicit that these aren't modified elsewhere (either through
functions by reference, or by other operations).
2019-06-03 16:56:47 -04:00
Lioncash
ca7ca2919c input_common/sdl/sdl_impl: Mark SDLEventToButtonParamPackage() as static
Its prototype declared at the top of the translation unit contains the
static qualifier, so the function itself should also contain it to make
it a proper internally linked function.
2019-06-03 16:56:47 -04:00
Lioncash
b73ea457cc input_common/sdl/sdl_impl: Convert reinterpret_cast into a static_cast
It's valid to static_cast a void pointer back into its proper type.
2019-06-03 16:56:46 -04:00
Lioncash
2c679cda51 input_common/sdl/sdl_impl: Use insert_or_assign() where applicable
Same behavior, but without a potential need to unnecessarily default
construct a value.
2019-06-03 16:56:46 -04:00
Lioncash
b46e615551 input_common/sdl/sdl_impl: Simplify SDL_Joystick deleter handling
The deleter can just be set in the constructor and maintained throughout
the lifetime of the object.

If a contained pointer is null, then the deleter won't execute, so this
is safe to do. We don't need to swap it out with a version of a deleter
that does nothing.
2019-06-03 16:56:46 -04:00
Lioncash
7ea07c6063 input_common/sdl/sdl_impl: Resolve two sign conversion warnings
Silences the final two warnings in SDL code.
2019-06-03 16:56:46 -04:00
Lioncash
cf0d01a5d7 input_common/sdl: Remove unused header includes and forward declarations
Gets rid of a few unnecessary inclusion dependencies. It also uncovered
a few indirect inclusion dependencies being relied upon.
2019-06-03 16:56:42 -04:00
Lioncash
00f0827a26 input_common/sdl/sdl_impl: Use nested namespace specifiers where applicable 2019-06-03 15:49:04 -04:00
Lioncash
e70f16fff7 input_common/sdl/sdl_impl: Silence sign conversion warnings
Makes the conversions explicit, as opposed to implicit.
2019-05-31 04:47:02 -03:00
Lioncash
1edf018319 common/math_util: Provide a template deduction guide for Common::Rectangle
Allows for things such as:

auto rect = Common::Rectangle{0, 0, 0, 0};

as opposed to being required to explicitly write out the underlying
type, such as:

auto rect = Common::Rectangle<int>{0, 0, 0, 0};

The only requirement for the deduction is that all constructor arguments
be the same type.
2019-05-31 04:44:02 -03:00
bunnei
ed74a3cb8b Merge pull request #1931 from DarkLordZach/mii-database-1
mii: Implement MiiManager backend and several mii service commands
2019-05-30 13:26:40 -04:00
bunnei
75561d190a Merge pull request #2431 from DarkLordZach/game-list-cache
yuzu: Implement a caching mechanism for the game list
2019-05-30 13:04:40 -04:00
Zach Hilman
9b2d38582f main: Remove extraneous comment 2019-05-30 10:47:56 -04:00
bunnei
e3608578e4 Merge pull request #2446 from ReinUsesLisp/tid
shader: Implement S2R Tid{XYZ} and CtaId{XYZ}
2019-05-29 12:21:17 -04:00
bunnei
665b7e8e18 Merge pull request #2518 from ReinUsesLisp/sdl2-window
yuzu_cmd: Split emu_window OpenGL implementation into its own file
2019-05-29 11:01:12 -04:00
bunnei
cfd885163f Merge pull request #2519 from lioncash/sign
loader/nso, core/core_timing_util: Silence sign-comparison warning
2019-05-27 12:26:17 -04:00
bunnei
2eb4d27c48 Merge pull request #2524 from ReinUsesLisp/fixup-extension
gl_shader_gen: Always declare extensions after the version declaration
2019-05-27 12:25:59 -04:00
ReinUsesLisp
21c0b4dec8 gl_device: Add commentary to AOFFI unit test source code
The intention behind this commit is to hint someone inspecting an
apitrace dump to ignore this ill-formed GLSL code.
2019-05-27 00:55:57 -03:00
ReinUsesLisp
84928e6d67 gl_shader_gen: Always declare extensions after the version declaration
This addresses a bug on geometry shaders where code was being written
before all #extension declarations were done. Ref to #2523
2019-05-27 00:51:35 -03:00
Zach Hilman
46e2ca5475 game_list_worker: Add better error handling to caching 2019-05-26 17:14:09 -04:00
Zach Hilman
944c07ac7d yuzu: Clear partial/full game list cache when data is updated 2019-05-26 15:12:12 -04:00
Zach Hilman
f95bdb5088 game_list: Implement caching for game list
Preserves list of add ons and the icon, which are the two costliest parts of game list population.
2019-05-26 15:12:12 -04:00
Zach Hilman
180f22f17e ui_settings: Add option to cache game list 2019-05-26 15:12:12 -04:00
ReinUsesLisp
37eaf39b44 emu_window: Pass OnMinimalClientAreaChangeRequest argument by copy
There's no performance improvement in passing an unsigned pair by
reference.
2019-05-26 00:54:13 -03:00
bunnei
90c9d703ba Merge pull request #2516 from lioncash/label
renderer_opengl/utils: Use a std::string_view with LabelGLObject()
2019-05-25 23:01:25 -04:00
bunnei
bb248a2710 Merge pull request #2509 from lioncash/aoc
service/aoc_u: Minor cleanup
2019-05-25 23:00:12 -04:00
bunnei
f97e206348 Merge pull request #2511 from lioncash/file-str
common/file_util: Minor cleanup
2019-05-25 22:59:16 -04:00
bunnei
91300bdfb2 Merge pull request #2517 from lioncash/hotkey
configure_hotkeys: Minor cleanup
2019-05-25 22:58:46 -04:00
Lioncash
0fa039d8d0 core_timing_util: Silence sign-comparison warnings
We can just make the conversion explicit instead of implicit here to
silence -Wsign-compare warnings.
2019-05-25 17:01:18 -04:00
Lioncash
e5159cfb84 loader/nso: Silence sign-comparison warning
This was previously performing a size_t == int comparison. Silences a
-Wsign-compare warning.
2019-05-25 16:53:33 -04:00
ReinUsesLisp
4b80dd23a4 yuzu_cmd: Split emu_window OpenGL implementation into its own file 2019-05-25 17:47:13 -03:00
Lioncash
88cd5e888e configure_hotkeys: Remove unnecessary Settings::Apply() call
Nothing from the hotkeys dialog relies on this call occurring, and is
already called from the dialog that calls applyConfiguration().
2019-05-25 04:34:54 -04:00
Lioncash
6640f631e2 configure_hotkeys: Tidy up key sequence conflict error string
Avoids mentioning the user and formalizes the error itself.
2019-05-25 04:25:11 -04:00
Lioncash
d61199721d configure_hotkeys: Change critical error dialog into a warning dialog
critical() is intended for critical/fatal errors that threaten the
overall stability of an application. A user entering a conflicting key
sequence is neither of those.
2019-05-25 04:08:18 -04:00
Lioncash
ef3c0f54d0 configure_hotkeys: Move conflict detection logic to IsUsedKey()
We don't need to extract the entire set of hotkeys into a list and then
iterate through it. We can traverse the list and early-exit if we're
able to.
2019-05-25 04:08:13 -04:00
Lioncash
c03fb00ac1 configure_hotkeys: Remove unused EmitHotkeysChanged()
1. This is something that should be solely emitted by the hotkey dialog
itself
2. This is functionally unused, given there's nothing listening for the
signal.
2019-05-25 04:08:07 -04:00
Lioncash
5d645c6dd9 sequence_dialog: Reorganize the constructor
The previous code was all "smushed" together wasn't really grouped
together that well.

This spaces things out and separates them by relation to one another,
making it easier to visually parse the individual sections of code that
make up the constructor.
2019-05-25 04:08:02 -04:00
Lioncash
9218e347cd sequence_dialog: Remove unnecessary horizontal specifier
QDialogButtonBoxes are horizontal by default.
2019-05-25 04:07:56 -04:00
Lioncash
5a4564bd8e renderer_opengl/utils: Use a std::string_view with LabelGLObject()
Uses a std::string_view instead of a std::string, given the pointed to
string isn't modified and is only used in a formatting operation.

This is nice because a few usages directly supply a string literal to
the function, allowing these usages to otherwise not heap allocate,
unlike the std::string overloads.

While we're at it, we can combine the address formatting into a single
formatting call.
2019-05-24 23:50:10 -04:00
bunnei
e86d2e2e5b Merge pull request #2513 from lioncash/string
yuzu/main: Specify string conversions explicitly
2019-05-24 22:46:10 -04:00
bunnei
68c9c9222d Merge pull request #2358 from ReinUsesLisp/parallel-shader
gl_shader_cache: Use shared contexts to build shaders in parallel at boot
2019-05-24 22:42:08 -04:00
Lioncash
3c0280cf66 yuzu/CMakeLists: Disable implicit QString conversions
Now that all of our code is compilable with implicit QString
conversions, we can enforce it at compile-time by disabling them.
2019-05-24 21:31:01 -04:00
Lioncash
bb06b98d81 yuzu/applets/software_keyboard: Remove unused assert header
This isn't actually used anywhere, so it can be removed.
2019-05-24 21:27:13 -04:00
Lioncash
16bf791939 yuzu/applets/software_keyboard: std::move argument in MainWindowFinishedText()
Given the std::optional can contain an object type that heap allocates,
we can use std::move to avoid an unnecessary copy/allocation from
occurring.
2019-05-24 21:27:12 -04:00
Lioncash
b3d7180164 yuzu/applets/software_keyboard: Resolve sign mismatch comparison
Qt uses a signed value to represent container sizes, so this was causing
a sign mismatch warning.
2019-05-24 21:27:12 -04:00
Lioncash
cf9cc41478 yuzu/applets/software_keyboard: Specify string conversions explicitly
Allows the software keyboard applet code to compile with implicit string
conversions disabled.
2019-05-24 21:27:12 -04:00
Lioncash
f5d416e071 yuzu/applets/error: Specify string conversions explicitly
Allows the error applet to build successfully with implicit string
conversions disabled.
2019-05-24 21:27:12 -04:00
Lioncash
6f2a8fbb13 yuzu/main: Specify string conversions where applicable 2019-05-24 21:27:09 -04:00
bunnei
1a2d90ab09 Merge pull request #2485 from ReinUsesLisp/generic-memory
shader/memory: Implement generic memory stores and loads (ST and LD)
2019-05-24 18:24:26 -04:00
bunnei
59f110ef31 Merge pull request #2504 from lioncash/config
yuzu/configuration/config: Specify string conversions explicitly
2019-05-24 18:23:58 -04:00
bunnei
d4f8fe24d9 Merge pull request #2489 from FearlessTobi/port-4716
Port citra-emu/citra#4716: "HLE/IPC: HLEContext can memorize the client thread and use it for SleepClientThread"
2019-05-24 18:23:15 -04:00
bunnei
c70404eab5 Merge pull request #2505 from ReinUsesLisp/glad-update
externals: Update glad to support OpenGL 4.6 compatibility profile
2019-05-24 18:23:04 -04:00
Lioncash
e7ab0e9127 common/file_util: Remove unnecessary return at end of void StripTailDirSlashes()
While we're at it, also invert the conditional into a guard clause.
2019-05-23 14:33:29 -04:00
Lioncash
11e9bee91d common/file_util: Make GetCurrentDir() return a std::optional
nullptr was being returned in the error case, which, at a glance may
seem perfectly OK... until you realize that std::string has the
invariant that it may not be constructed from a null pointer. This
means that if this error case was ever hit, then the application would
most likely crash from a thrown exception in std::string's constructor.

Instead, we can change the function to return an optional value,
indicating if a failure occurred.
2019-05-23 14:24:13 -04:00
Lioncash
943f6da1ac common/file_util: Remove duplicated documentation comments
These are already present within the header, so they don't need to be
repeated in the cpp file.
2019-05-23 14:22:12 -04:00
Lioncash
2b1fcc8a14 common/file_util: Make ReadFileToString and WriteStringToFile consistent
Makes the parameter ordering consistent, and also makes the filename
parameter a std::string. A std::string would be constructed anyways with
the previous code, as IOFile's only constructor with a filepath is one
taking a std::string.

We can also make WriteStringToFile's string parameter utilize a
std::string_view for the string, making use of our previous changes to
IOFile.
2019-05-23 13:52:43 -04:00
Lioncash
e3b2539986 common/file_util: Remove unnecessary c_str() calls
The file stream open functions have supported std::string overloads
since C++11, so we don't need to use c_str() here. Same behavior, less
code.
2019-05-23 13:37:47 -04:00
Lioncash
8cd3d9be26 common/file_util: Make IOFile's WriteString take a std::string_view
We don't need to force the usage of a std::string here, and can instead
use a std::string_view, which allows writing out other forms of strings
(e.g. C-style strings) without any unnecessary heap allocations.
2019-05-23 13:35:31 -04:00
Lioncash
b6dcb1ae4d shader/shader_ir: Make Comment() take a std::string by value
This allows for forming comment nodes without making unnecessary copies
of the std::string instance.

e.g. previously:

Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]",
        cbuf->GetIndex(), cbuf_offset));

Would result in a copy of the string being created, as CommentNode()
takes a std::string by value (a const ref passed to a value parameter
results in a copy).

Now, only one instance of the string is ever moved around. (fmt::format
returns a std::string, and since it's returned from a function by value,
this is a prvalue (which can be treated like an rvalue), so it's moved
into Comment's string parameter), we then move it into the CommentNode
constructor, which then moves the string into its member variable).
2019-05-23 03:01:55 -03:00
Lioncash
228e58d0a5 shader/decode/*: Add missing newline to files lacking them
Keeps the shader code file endings consistent.
2019-05-23 02:55:52 -03:00
Lioncash
87b4c1ac5e shader/decode/*: Eliminate indirect inclusions
Amends cases where we were using things that were indirectly being
satisfied through other headers. This way, if those headers change and
eliminate dependencies on other headers in the future, we don't have
cascading compilation errors.
2019-05-23 02:55:52 -03:00
Lioncash
3e7d37301a service/aoc: Avoid allocating and discarding data
Previously, the code was accumulating data into a std::vector and then
tossing all of it away if a setting was disabled.

Instead, we can just check if it's disabled and do no work at all if
possible. If it's enabled, then we can append to the vector and
allocate.

Unlikely to impact usage much, but it is slightly less sloppy with
resources.
2019-05-23 00:26:21 -04:00
Lioncash
d0e200a894 service/aoc: Remove unnecessary includes
Removes two header dependencies related to file handling that aren't
actually used within the source file.
2019-05-22 23:26:12 -04:00
Lioncash
819d229e76 service/aoc: Pop all passed values where applicable
A few of the aoc service stubs/implementations weren't fully popping all
of the parameters passed to them. This ensures that all parameters are
popped and, at minimum, logged out.
2019-05-22 23:24:27 -04:00
Lioncash
195b54602f shader/decode/memory: Remove left in debug pragma 2019-05-22 17:08:50 -04:00
ReinUsesLisp
f4c15db9e8 externals: Update glad to support OpenGL 4.6 compatibility profile
Now that we have an OpenGL compatibility profile we might want to use
OpenGL compatibility symbols that are not available in our current glad.

This commit has been generated with https://glad.dav1d.de/ with all
extensions enabled and OpenGL 4.6 compatibility profile.
2019-05-21 20:52:00 -03:00
Lioncash
de23847184 renderer_opengl/gl_shader_decompiler: Remove redundant name specification in format string
This accidentally slipped through a rebase.
2019-05-21 09:47:21 -04:00
ReinUsesLisp
69215b5a55 gl_shader_cache: Fix clang strict standard build issues 2019-05-20 22:46:05 -03:00
ReinUsesLisp
c03b8c4c19 gl_shader_cache: Use shared contexts to build shaders in parallel 2019-05-20 22:45:55 -03:00
ReinUsesLisp
75e7b45d69 shader/memory: Implement ST (generic memory) 2019-05-20 22:41:53 -03:00
ReinUsesLisp
f78ef617b6 shader/memory: Implement LD (generic memory) 2019-05-20 22:38:59 -03:00
Lioncash
f49a04ba39 yuzu/configuration/config: Make default hotkeys an internally-linked array in the cpp file
Given the array is a private static array, we can just make it
internally linked to hide it from external code. This also allows us to
remove an inclusion within the header.
2019-05-20 21:09:35 -04:00
Lioncash
938d6dca30 yuzu/configuration/config: Specify string conversions explicitly
Allows the configuration code to build successfully with implicit string
conversions disabled.
2019-05-20 21:08:32 -04:00
bunnei
9ffc60b5b3 Merge pull request #2455 from lioncash/config
configuration/config: Move config loading and saving to functions based off groups
2019-05-20 20:46:05 -04:00
bunnei
dbcff5d574 Merge pull request #2503 from lioncash/util
yuzu/game_list: Specify string conversions explicitly
2019-05-20 20:43:28 -04:00
bunnei
9a17b20896 Merge pull request #2494 from lioncash/shader-text
gl_shader_decompiler: Add AddLine() overloads with single function that forwards to libfmt
2019-05-20 20:42:40 -04:00
ReinUsesLisp
9c3461604c shader: Implement S2R Tid{XYZ} and CtaId{XYZ} 2019-05-20 16:36:49 -03:00
ReinUsesLisp
ada79fa8ad gl_shader_decompiler: Make GetSwizzle constexpr 2019-05-20 16:36:48 -03:00
Lioncash
bc32474901 yuzu/game_list: Specify string conversions explicitly
Allows the game list code to compile successfully with implicit string
conversions disabled.
2019-05-20 15:30:50 -04:00
Lioncash
ed2fedac13 yuzu/game_list_worker: Specify string conversions explicitly
Allows the game list worker code to compile successfully with implicit
string conversions disabled.
2019-05-20 15:07:59 -04:00
Lioncash
7a82d6f394 yuzu/game_list_p: Amend mentions of SMDH in comments
SMDH is a metadata format used in some executable formats for the
Nintendo 3DS. Switch executables don't utilize this metadata format, so
this just a holdover from Citra and can be corrected.
2019-05-20 15:04:35 -04:00
Lioncash
486c3e6085 yuzu/game_list_p: Specify string conversions explicitly
Allows the game list item code to build with implicit string conversions
disabled.
2019-05-20 15:02:37 -04:00
Lioncash
922d8c6cb4 yuzu/loading_screen: Specify string conversions explicitly
Allows the loading screen code to compile with implicit string
conversions disabled.

While we're at it remove unnecessary const usages, and add it to nearby
variables where appropriate.
2019-05-20 14:53:44 -04:00
Lioncash
fd34732e26 yuzu/bootmanager: Specify string conversions explicitly
Allows the bootmanager code to compile with implicit string conversions
disabled.
2019-05-20 14:50:53 -04:00
Lioncash
317f1263fb yuzu/util: Specify string conversions explicitly
Allows the util code to build with implicit string conversions disabled.
2019-05-20 14:44:42 -04:00
Lioncash
58a0c13e34 gl_shader_decompiler: Tidy up minor remaining cases of unnecessary std::string concatenation 2019-05-20 14:14:48 -04:00
Lioncash
6fb29764d6 gl_shader_decompiler: Replace individual overloads with the fmt-based one
Gets rid of the need to special-case brace handling depending on the
overload used, and makes it consistent across the board with how fmt
handles them.

Strings with compile-time deducible strings are directly forwarded to
std::string's constructor, so we don't need to worry about the
performance difference here, as it'll be identical.
2019-05-20 14:14:48 -04:00
Lioncash
784d2b6c3d gl_shader_decompiler: Utilize fmt overload of AddLine() where applicable 2019-05-20 14:14:44 -04:00
bunnei
0adb54abc1 Merge pull request #2499 from lioncash/translate
yuzu/configuration: Specify string conversions explicitly
2019-05-20 11:13:25 -04:00
Hexagon12
73ee85e9ae Merge pull request #2500 from FernandoS27/revert-2466
Revert #2466
2019-05-19 21:23:06 +01:00
Fernando Sahmkow
911fafb967 Revert #2466
This reverts a tested behavior on delay slots not exiting if the exit 
flag is set. Currently new tests are required in order to ensure this 
behavior.
2019-05-19 16:04:44 -04:00
Lioncash
91ec251c4a gl_shader_decompiler: Add AddLine() overload that forwards to fmt
In a lot of places throughout the decompiler, string concatenation via
operator+ is used quite heavily. This is usually fine, when not heavily
used, but when used extensively, can be a problem. operator+ creates an
entirely new heap allocated temporary string and given we perform
expressions like:

std::string thing = a + b + c + d;

this ends up with a lot of unnecessary temporary strings being created
and discarded, which kind of thrashes the heap more than we need to.
Given we utilize fmt in some AddLine calls, we can make this a part of
the ShaderWriter's API. We can make an overload that simply acts as a
passthrough to fmt.

This way, whenever things need to be appended to a string, the operation
can be done via a single string formatting operation instead of
discarding numerous temporary strings. This also has the benefit of
making the strings themselves look nicer and makes it easier to spot
errors in them.
2019-05-19 14:12:20 -04:00
bunnei
d49efbfb4a Merge pull request #2441 from ReinUsesLisp/al2p
shader: Implement AL2P and ALD.PHYS
2019-05-19 14:02:58 -04:00
bunnei
13dda1d8ed Merge pull request #2410 from lioncash/affinity
kernel/svc: Reorganize and fix up the initial handling of svcSetThreadCoreMask()
2019-05-19 13:59:52 -04:00
Lioncash
2318c394a8 yuzu/configuration/configure_web: Specify string conversions explicitly
Allows the web config code to compile with implicit string conversions
disabled. We can also deduplicate the calls to create the pixmap.
2019-05-19 13:05:09 -04:00
Lioncash
d9c4d64ed5 yuzu/configuration/configure_system: Specify string conversions explicitly
Allows the system config code to build successfully with implicit string
conversions disabled.
2019-05-19 12:56:43 -04:00
Lioncash
428d8098a7 yuzu/configuration/configure_profile_manager: Mark UI string as translatable
This is a user-facing string, so it should be marked as translatable.
2019-05-19 12:54:04 -04:00
Lioncash
17255cd835 yuzu/configuration/configure_per_general: Specify string conversions explicitly
Allows the per-game configuration to be successfully built with implicit
string conversions disabled.
2019-05-19 12:47:33 -04:00
Lioncash
3039211c20 yuzu/configuration/configure_mouse_advanced: Clean up array accesses
Deduplicates array accesses and uses a named variable where appropriate.
2019-05-19 12:34:18 -04:00
Lioncash
c9c4208c4a yuzu/configuration/configure_mouse_advanced: Specify string conversions explicitly
Allows the advanced mouse configuration code to build with implicit
string conversions disabled.
2019-05-19 12:34:18 -04:00
Lioncash
aa83639b78 yuzu/configuration/configure_input_player: Clean up array accesses
Rather than repeatedly index arrays that have quite a large array index,
we can just use a named variable instead.
2019-05-19 12:34:15 -04:00
Lioncash
4d2da5a40a yuzu/configuration/configure_input_player: Specify string conversions explicitly
Allows the player input configuration code to compile with implicit
string conversions disabled.
2019-05-19 11:38:31 -04:00
Hexagon12
b94b08fa6f Merge pull request #2491 from FernandoS27/dma-fix
Dma_pusher: ASSERT on empty command_list
2019-05-19 16:27:15 +01:00
Lioncash
d81d4a0f68 yuzu/configuration/configure_input: Mark controller type names as translateable
These are user-facing strings, so they should be localizable.
2019-05-19 11:23:25 -04:00
Lioncash
7e650088dd yuzu/configuration/configure_general: Specify string conversions explicitly
Allows the general configuration code to successfully compile with
implicit string conversions disabled.
2019-05-19 11:18:16 -04:00
Lioncash
05235ccaa9 yuzu/configuration/configure_gamelist: Specify string conversions explicitly
Allows the gamelist configuration code to compile with implicit string
conversions disabled.
2019-05-19 11:16:23 -04:00
Lioncash
5f01ec338e yuzu/configuration/configure_audio: Store power on query into a variable
Avoids using the system accessor more than necessary, and ensures that
both dialog boxes see the same power on state.
2019-05-19 11:12:31 -04:00
Lioncash
d00ca5c6c8 yuzu/configuration/configure_audio: Tidy up function cast
We can just use qOverload here to tidy up the function cast.
2019-05-19 11:10:58 -04:00
Lioncash
d184224e8f yuzu/configuration/configure_audio: Specify string conversions explicitly
Allows the audio configuration code to build with implicit string
conversions disabled.
2019-05-19 11:08:56 -04:00
Hexagon12
f8b1e53369 Merge pull request #2452 from FernandoS27/raster-cache-fix
Correct possible error on Rasterizer Caches
2019-05-19 16:00:44 +01:00
Hexagon12
2aebbe9bf9 Merge pull request #2497 from lioncash/shader-ir
shader/shader_ir: Minor changes
2019-05-19 15:51:06 +01:00
Hexagon12
fadf66993c Merge pull request #2495 from lioncash/cache
gl_shader_disk_cache: Minor cleanup
2019-05-19 15:50:23 +01:00
Fernando Sahmkow
9e98100c94 Dma_pusher: ASSERT on empty command_list
This is a measure to avoid crashes on command list reading as an empty
command_list is considered a NOP.
2019-05-19 10:48:31 -04:00
Hexagon12
6fd247c84a Merge pull request #2439 from lioncash/audren
service/audren_u: Get rid of magic values within GetAudioRendererWorkBufferSize
2019-05-19 15:23:04 +01:00
Hexagon12
18cdbdafa2 Merge pull request #2467 from lioncash/move
video_core/gpu_thread: Remove redundant copy constructor for CommandDataContainer
2019-05-19 15:20:37 +01:00
Hexagon12
594328f494 Merge pull request #2463 from lioncash/set
service/set: Correct and simplify behavior related to copying language codes
2019-05-19 15:17:39 +01:00
Hexagon12
9175bffbdb Merge pull request #2466 from yuzu-emu/mme-exit-delay-slot
GPU/MMEInterpreter: Ignore the 'exit' flag when it's executed inside a delay slot.
2019-05-19 15:14:41 +01:00
Hexagon12
ac3775e6ae Merge pull request #2468 from lioncash/deduction
yuzu: Remove explicit types from locks where applicable
2019-05-19 15:05:56 +01:00
Hexagon12
b54bd3f018 Merge pull request #2472 from FernandoS27/tic
maxwell_3d: reduce severity of different component formats assert.
2019-05-19 15:04:47 +01:00
Hexagon12
3bd5f01240 Merge pull request #2469 from lioncash/copyable
video_core/engines/maxwell_3d: Add is_trivially_copyable_v check for Regs
2019-05-19 15:02:17 +01:00
Sebastian Valle
a6ed792ac4 Merge pull request #2470 from lioncash/ranged-for
video_core/engines/maxwell_3d: Simplify for loops into ranged for loops within InitializeRegisterDefaults()
2019-05-19 09:01:19 -05:00
Hexagon12
3ff0c70c72 Merge pull request #2487 from lioncash/service-return
service/am: Add missing return in error case for IStorageAccessor's Read/Write()
2019-05-19 14:59:40 +01:00
Hexagon12
4452195d41 Merge pull request #2480 from ReinUsesLisp/fix-quads
gl_rasterizer: Pass the right number of array quad vertices count
2019-05-19 14:58:49 +01:00
Hexagon12
8e9a1e4249 Merge pull request #2483 from ReinUsesLisp/fix-point-size
gl_rasterizer: Limit OpenGL point size to a minimum of 1
2019-05-19 14:57:05 +01:00
Sebastian Valle
dfddb12255 Merge pull request #2471 from lioncash/engine-upload
video_core/engines/engine_upload: Minor tidying
2019-05-19 08:54:42 -05:00
Sebastian Valle
f9ad88f9d7 Merge pull request #2484 from ReinUsesLisp/triangle-fan
maxwell_to_gl: Add TriangleFan primitive topology
2019-05-19 08:53:29 -05:00
Hexagon12
edf8c0a545 Merge pull request #2490 from lioncash/float
ipc_helpers: Amend floating-point type in Pop<double> specialization
2019-05-19 14:50:30 +01:00
Hexagon12
209a0dfa35 Merge pull request #2492 from lioncash/debugger
yuzu/debugger: Specify string conversions explicitly
2019-05-19 14:49:54 +01:00
Sebastian Valle
27033de2e5 Merge pull request #2486 from lioncash/resetname
core/kernel/object: Rename ResetType enum members for clarity
2019-05-19 08:47:59 -05:00
Sebastian Valle
30c984dc97 Merge pull request #2488 from lioncash/static-fn
kernel/svc: Mark GetThreadList() and UnmapProcessCodeMemory() as internally linked
2019-05-19 08:43:47 -05:00
Sebastian Valle
256e5c9583 Merge pull request #2493 from lioncash/translate
yuzu/applets/profile_select: Mark header string as translatable
2019-05-19 08:42:39 -05:00
Sebastian Valle
b42ca9888d Merge pull request #2496 from lioncash/move-con
gl_shader_gen: std::move objects where applicable
2019-05-19 08:35:47 -05:00
Hexagon12
ffd9a1f3ef Merge pull request #2473 from lioncash/vs2019
CMakeLists: Handle VS 2019 in a less annoying manner
2019-05-19 14:33:28 +01:00
Hexagon12
2437ca04d7 Merge pull request #2476 from ReinUsesLisp/fix-compat
yuzu/bootmanager: Explicitly enable deprecated OpenGL features on compat
2019-05-19 14:31:52 +01:00
Hexagon12
aa61478d8c Merge pull request #2498 from lioncash/unused-code
yuzu/util: Remove unused spinbox.cpp/.h
2019-05-19 14:30:07 +01:00
Lioncash
e310d943b8 shader/shader_ir: Remove unnecessary inline specifiers
constexpr internally links by default, so the inline specifier is
unnecessary.
2019-05-19 08:23:15 -04:00
Lioncash
212b148923 shader/shader_ir: Simplify constructors for OperationNode
Many of these constructors don't even need to be templated. The only
ones that need to be templated are the ones that actually make use of
the parameter pack.

Even then, since std::vector accepts an initializer list, we can supply
the parameter pack directly to it instead of creating our own copy of
the list, then copying it again into the std::vector.
2019-05-19 08:23:14 -04:00
Lioncash
81e7e63080 shader/shader_ir: Remove unnecessary template parameter packs from Operation() overloads where applicable
These overloads don't actually make use of the parameter pack, so they
can be turned into regular non-template function overloads.
2019-05-19 08:23:14 -04:00
Lioncash
e09ee0ff23 shader/shader_ir: Mark tracking functions as const member functions
These don't actually modify instance state, so they can be marked as
const member functions
2019-05-19 08:23:09 -04:00
Lioncash
bc6972caf9 yuzu/util: Remove unused spinbox.cpp/.h
This has been left unused since the removal of the vestigial surface
viewer. Given it has no uses left, this can be removed as well.
2019-05-19 05:35:34 -04:00
Lioncash
ce04ab38bb shader/shader_ir: Place implementations of constructor and destructor in cpp file
Given the class contains quite a lot of non-trivial types, place the
constructor and destructor within the cpp file to avoid inlining
construction and destruction code everywhere the class is used.
2019-05-19 04:02:02 -04:00
Lioncash
3356ea5bc2 gl_shader_gen: std::move objects where applicable
Avoids performing copies into the pair being returned. Instead, we can
just move the resources into the pair, avoiding the need to make copies
of both the std::string and ShaderEntries struct.
2019-05-19 03:46:54 -04:00
Lioncash
0a7f09a99b gl_shader_disk_cache: in-class initialize virtual file offset of ShaderDiskCacheOpenGL
Given the offset is assigned a fixed value in the constructor, we can
just assign it directly and get rid of the need to write the name of the
variable again in the constructor initializer list.
2019-05-19 02:55:18 -04:00
Lioncash
634b78a4c6 gl_shader_disk_cache: Default ShaderDiskCacheOpenGL's destructor in the cpp file
Given the disk shader cache contains non-trivial types, we should
default it in the cpp file in order to prevent inlining of the
complex destruction logic.
2019-05-19 02:50:50 -04:00
Lioncash
7fdc644c44 gl_shader_disk_cache: Make hash specializations noexcept
The standard library expects hash specializations that don't throw
exceptions. Make this explicit in the type to allow selection of better
code paths if possible in implementations.
2019-05-19 02:46:45 -04:00
Lioncash
683c4e523f gl_shader_disk_cache: Remove redundant code string construction in LoadDecompiledEntry()
We don't need to load the code into a vector and then construct a string
over the data. We can just create a string with the necessary size ahead
of time, and read the data directly into it, getting rid of an
unnecessary heap allocation.
2019-05-19 02:46:44 -04:00
Lioncash
5e4c227608 gl_shader_disk_cache: Make variable non-const in decompiled entry case
std::move does nothing when applied to a const variable. Resources can't
be moved if the object is immutable. With this change, we don't end up
making several unnecessary heap allocations and copies.
2019-05-19 02:46:44 -04:00
Lioncash
f417be9d3b gl_shader_disk_cache: Special-case boolean handling
Booleans don't have a guaranteed size, but we still want to have them
integrate into the disk cache system without needing to actually use a
different type. We can do this by supplying non-template overloads for
the bool type.

Non-template overloads always have precedence during function
resolution, so this is safe to provide.

This gets rid of the need to smatter ternary conditionals, as well as
the need to use u8 types to store the value in.
2019-05-19 02:46:38 -04:00
Lioncash
22324e3ef1 yuzu/applets/profile_select: Mark header string as translatable
This is a user-facing string, so it should be marked as translatable.
2019-05-19 01:18:37 -04:00
Lioncash
d77d1a0207 yuzu/debugger/graphics/graphics_breakpoints: Specify string conversions explicitly
Allows the graphics breakpoints to compile with implicit string
conversions disabled.
2019-05-19 01:10:05 -04:00
Lioncash
cad4f2ed29 yuzu/debugger/profiler: Specify string conversions explicitly
This allows the microprofile widget to compile with implicit string
conversions disabled.
2019-05-19 01:10:05 -04:00
Lioncash
a059b9eed4 yuzu/debugger/wait_tree: Specify string conversions explicitly
Allows compiling the wait tree widget with implicit string conversions
disabled.
2019-05-19 01:10:05 -04:00
Lioncash
242273788a ipc_helpers: Amend floating-point type in Pop<double> specialization
Currently, this overload isn't used, so this wasn't actually hit in any
code, only the float overload is used.
2019-05-18 22:05:33 -04:00
Tobias
5993133d5e Address review comment
Co-Authored-By: Mat M. <mathew1800@gmail.com>
2019-05-19 02:14:30 +02:00
Lioncash
d5cce86431 kernel/svc: Mark GetThreadList() and UnmapProcessCodeMemory() as internally linked
These are only used from within this translation unit, so they don't
need to have external linkage. They were intended to be marked with this
anyways to be consistent with the other service functions.
2019-05-18 19:10:34 -04:00
Lioncash
88c263ee8e service/am: Add missing return in error case for IStorageAccessor's Read()/Write().
Previously this would fall through and return successfully, despite
being an out of bounds read or write.
2019-05-18 18:50:04 -04:00
Lioncash
a47aaa7f1b core/kernel/object: Rename ResetType enum members
Renames the members to more accurately indicate what they signify.
"OneShot" and "Sticky" are kind of ambiguous identifiers for the reset
types, and can be kind of misleading. Automatic and Manual communicate
the kind of reset type in a clearer manner. Either the event is
automatically reset, or it isn't and must be manually cleared.

The "OneShot" and "Sticky" terminology is just a hold-over from Citra
where the kernel had a third type of event reset type known as "Pulse".
Given the Switch kernel only has two forms of event reset types, we
don't need to keep the old terminology around anymore.
2019-05-18 15:52:51 -04:00
Weiyi Wang
8d6342384b HLE/IPC: HLEContext can memorize the client thread and use it for SleepClientThread
This reduces the boilerplate that services have to write out the current thread explicitly. Using current thread instead of client thread is also semantically incorrect, and will be a problem when we implement multicore (at which time there will be multiple current threads)
2019-05-18 19:53:39 +02:00
ReinUsesLisp
21ea8b2fcb gl_rasterizer: Limit OpenGL point size to a minimum of 1 2019-05-18 03:07:29 -03:00
ReinUsesLisp
52340c3294 maxwell_to_gl: Add TriangleFan primitive topology 2019-05-17 19:58:02 -03:00
ReinUsesLisp
a652e58c54 gl_rasterizer: Pass the right number of array quad vertices count 2019-05-17 17:08:34 -03:00
bunnei
fb85d5670d Merge pull request #2457 from lioncash/about
yuzu/{about_dialog, main}: Specify string conversions explicitly for SCM-related info
2019-05-17 15:42:43 -04:00
bunnei
6f1720a5b7 Merge pull request #2477 from ReinUsesLisp/fix-sdl2
yuzu_cmd: Make OpenGL's context current
2019-05-17 13:04:33 -04:00
bunnei
865025f612 Merge pull request #2478 from ReinUsesLisp/sdl2-compat
yuzu_cmd: Use OpenGL compat when asked in the settings
2019-05-17 13:04:04 -04:00
bunnei
1975d32f2d Merge pull request #2479 from ReinUsesLisp/qt-shadow
qt/configure_graphics: Shadow options at runtime
2019-05-17 13:01:41 -04:00
ReinUsesLisp
4cf64f8e09 qt/configure_graphics: Shadow options at runtime
Compatibility profile and the disk shader cache settings shouldn't
be changed at runtime. This aims to address that shadowing those
options.
2019-05-17 04:29:20 -03:00
ReinUsesLisp
69265e4504 yuzu_cmd: Use OpenGL compat when asked in the settings 2019-05-17 04:25:26 -03:00
ReinUsesLisp
5f877d9458 yuzu_cmd: Make OpenGL's context current
The SDL2 frontend never bound the OpenGL context, resulting on a white
screen and no-ops all over the backend.
2019-05-17 04:13:20 -03:00
ReinUsesLisp
e6c60b419c yuzu/bootmanager: Explicitly enable deprecated OpenGL features on compat
Nvidia's proprietary driver creates a real OpenGL compatibility profile
without this option, meanwhile Intel (and probably AMD, I haven't tested
it) require that QSurfaceFormat::FormatOption::DeprecatedFunctions is
explicitly enabled.
2019-05-17 04:09:17 -03:00
Lioncash
a6fb6ccc83 CMakeLists: Handle VS 2019 in a less annoying manner
VS 2019 is binary compatible with VS 2017, so we can safely use
the prebuilt libraries for VS 2017 with VS 2019. This makes it less
annoying to build yuzu with the most up to date toolchain.
2019-05-14 19:05:51 -04:00
Fernando Sahmkow
fc975e9021 maxwell_3d: reduce sevirity of different component formats assert.
This was reduced due to happening on most games and at such constant
rate that it affected performance heavily for the end user. In general,
we are well aware of the assert and an implementation is already
planned.
2019-05-14 17:12:54 -04:00
Lioncash
b01cce716e video_core/engines/engine_upload: Amend constructor initializer list order
Silences a -Wreorder warning.
2019-05-14 13:43:28 -04:00
Lioncash
9b6d993e52 video_core/engines/engine_upload: Default destructor in the cpp file
Avoids inlining destruction logic where applicable, and also makes
forward declarations not cause unexpected compilation errors depending
on where the State class is used.
2019-05-14 13:41:41 -04:00
Lioncash
ec1c69258a video_core/engines/engine_upload: Remove unnecessary const on parameters in function declarations
These only apply in the definition of the function. They can be omitted
from the declaration.
2019-05-14 13:40:09 -04:00
Lioncash
0f83c8dffa video_core/engines/engine_upload: Remove unnecessary includes 2019-05-14 13:39:04 -04:00
Lioncash
5db1b54b58 video_core/engines/maxwell3d: Get rid of three magic values in CallMethod()
We can use the named constant instead of using 32 directly.
2019-05-14 09:02:47 -04:00
Lioncash
48ce5880a0 video_core/engines/maxwell_3d: Simplify for loops into ranged for loops within InitializeRegisterDefaults()
Lessens the amount of code that needs to be read, and gets rid of the
need to introduce an indexing variable. Instead, we just operate on the
objects directly.
2019-05-14 08:53:19 -04:00
Lioncash
c212fc9b2c video_core/engines/maxwell_3d: Add is_trivially_copyable_v check for Regs
std::memset is used to clear the entire register structure, which
requires that the Regs struct be trivially copyable (otherwise undefined
behavior is invoked). This prevents the case where a non-trivial type is
potentially added to the struct.
2019-05-14 08:47:56 -04:00
Lioncash
d6d809db87 yuzu: Remove explicit types from locks where applicable
With C++17's deduction guides, the type doesn't need to be explicitly
specified within locking primitives anymore.
2019-05-14 08:18:48 -04:00
Lioncash
c5129a3a58 video_core/gpu_thread: Remove redundant copy constructor for CommandDataContainer
std::move within a copy constructor (on a data member that isn't
mutable) will always result in a copy. Because of that, the behavior of
this copy constructor is identical to the one that would be generated
automatically by the compiler, so we can remove it.
2019-05-14 08:09:17 -04:00
Mat M
c4d549919f Merge pull request #2462 from lioncash/video-mm
video_core/memory_manager: Minor tidying
2019-05-14 06:40:33 -04:00
Mat M
dadcf317dc Merge pull request #2461 from lioncash/unused-var
video_core: Remove a few unused variables and functions
2019-05-14 06:36:26 -04:00
Mat M
8b933e77cd Merge pull request #2460 from lioncash/volatile
CMakeLists: Specify /volatile:iso for MSVC
2019-05-14 06:34:53 -04:00
Mat M
3e8e335a5c Merge pull request #2450 from lioncash/warn-level
CMakeLists: Explicitly specify -Wall for the non-MSVC case
2019-05-14 06:34:05 -04:00
Rodrigo Locatti
940a71089d Merge pull request #2413 from FernandoS27/opt-gpu
Rasterizer Cache: refactor flushing & optimize memory usage of surfaces
2019-05-13 23:01:59 -03:00
Sebastian Valle
9ef45f00bf GPU/MMEInterpreter: Ignore the 'exit' flag when it's executed inside a delay slot.
It seems instructions marked with the 'exit' flag will not cause an exit when executed within a delay slot.

This was hwtested by fincs.
2019-05-12 16:38:51 -05:00
Lioncash
c823cf6594 service/set: Correct and simplify behavior related to copying language codes
This corrects cases where it was possible to write more entries into the
write buffer than were requested. Now, we check the size of the buffer
before actually writing into them.

We were also returning the wrong value for
GetAvailableLanguageCodeCount2(). This was previously returning 64, but
only 17 should have been returned. 64 entries is the size of the static
array used in MakeLanguageCode() within the service binary itself, but
isn't the actual total number of language codes present.
2019-05-09 21:28:36 -04:00
Lioncash
716fbaef74 video_core/memory_manager: Mark IsBlockContinuous() as a const member function
Corrects the typo in its name and marks the function as a const member
function, given it doesn't actually modify memory manager state.
2019-05-09 19:14:36 -04:00
Lioncash
d4bcd006b2 video_core/memory_manager: Mark the constructor as explicit
Prevents implicit converting constructions of the memory manager.
2019-05-09 19:10:26 -04:00
Lioncash
fd12788967 video_core/memory_manager: Default the destructor within the cpp file
Makes the class less surprising when it comes to forward declaring the
type, and also prevents inlining the destruction code of the class,
given it contains non-trivial types.
2019-05-09 19:10:13 -04:00
Lioncash
53afe47cec video_core/memory_manager: Amend doxygen comments
Corrects references to non-existent parameters and corrects typos.
2019-05-09 19:09:19 -04:00
Lioncash
5235b053b4 video_core/memory_manager: Remove superfluous const from function declarations
These are able to be omitted from the declaration of functions, since
they don't do anything at the type system level. The definitions of the
functions can retain the use of const though, since they make the
variables immutable in the implementation of the function where they're
used.
2019-05-09 18:59:49 -04:00
Lioncash
b6408e9671 video_core/renderer_opengl/gl_shader_cache: Correct member initialization order
Silences a -Wreorder warning.
2019-05-09 18:55:47 -04:00
Lioncash
e43ba3acd4 video_core/shader/decode/texture: Remove unused variable from GetTld4Code() 2019-05-09 18:49:56 -04:00
Lioncash
e3c45b4338 renderer_vulkan/vk_shader_decompiler: Remove unused variable from DeclareInternalFlags() 2019-05-09 18:47:48 -04:00
Lioncash
175fe8aaeb video_core/renderer_opengl/gl_shader_decompiler: Remove unused Composite() function
This isn't used at all, so it can be removed.
2019-05-09 18:45:26 -04:00
Lioncash
6d28d288a3 video_core/renderer_opengl/gl_rasterizer_cache: Remove unused variable in UploadGLMipmapTexture()
This variable is unused entirely, so it can be removed.
2019-05-09 18:42:48 -04:00
Lioncash
ba165b1092 video_core/gpu_thread: Remove unused local variable
Instead of retrieving the data from the std::variant instance, we can
just check if the variant contains that type of data.

This is essentially the same behavior, only it returns a bool indicating
whether or not the type in the variant is currently active, instead of
actually retrieving the data.
2019-05-09 18:39:21 -04:00
Lioncash
c56d893e77 video_core/textures/astc: Remove unused variables
Silences a few compilation warnings.
2019-05-09 18:33:36 -04:00
Lioncash
c4d03f0154 CMakeLists: Specify /volatile:iso for MSVC
By default, MSVC doesn't use standards-compliant volatile semantics.
This makes it behave in a standards-compliant manner, making
expectations more uniform across compilers.
2019-05-09 15:49:30 -04:00
bunnei
7cb17834c7 Merge pull request #2437 from lioncash/audctl
service/audctl: Update documentation comments to be relative to 8.0.0
2019-05-09 13:24:13 -04:00
bunnei
f3317cf2db Merge pull request #2454 from lioncash/cflag
src/CMakeLists: Add /Zc:externConstexpr to the MSVC build flags
2019-05-09 13:23:49 -04:00
bunnei
daca045fcd Merge pull request #2442 from FernandoS27/astc-fix
Fix Layered ASTC Textures
2019-05-09 13:23:14 -04:00
bunnei
f69d3a6351 Merge pull request #2443 from ReinUsesLisp/skip-repeated-variants
gl_shader_disk_cache: Skip stored shader variants instead of asserting
2019-05-09 13:22:42 -04:00
bunnei
5907619a04 Merge pull request #2445 from FearlessTobi/port-4749
Port citra-emu/citra#4749: "web_service: Misc fixes"
2019-05-09 13:22:00 -04:00
bunnei
9567b3a293 Merge pull request #2458 from lioncash/hotkey
yuzu/hotkeys: Remove unnecessary constructor
2019-05-09 13:21:36 -04:00
bunnei
c6f3831320 Merge pull request #2456 from lioncash/qualifier
yuzu/compatdb: Remove unnecessary qualifiers
2019-05-09 13:21:04 -04:00
bunnei
8abf0add04 Merge pull request #2459 from lioncash/what
configure_dialog: Remove the Whats This? button from the dialog
2019-05-09 13:20:12 -04:00
bunnei
5b6571c170 Merge pull request #2453 from lioncash/enum
core/memory: Remove unused FlushMode enum
2019-05-09 13:19:49 -04:00
bunnei
c27b81cb85 Merge pull request #2429 from FernandoS27/compute
Corrections and Implementation on GPU Engines
2019-05-09 13:19:22 -04:00
bunnei
0e9a17b029 Merge pull request #2440 from lioncash/dynarmic
externals: Update dynarmic to master
2019-05-09 13:18:49 -04:00
Lioncash
f3c18d622e configure_dialog: Remove the Whats This? button from the dialog 2019-05-09 03:20:13 -04:00
Lioncash
8bdef4f951 yuzu/hotkeys: Remove unnecessary constructor
The behavior of the Hotkey constructor is already accomplished via in-class member
initializers, so the constructor is superfluous here.
2019-05-09 02:17:22 -04:00
Lioncash
d955944869 yuzu/main: Move window title updating logic to its own function
For similar reasons to the previous change, we move this to a single
function, so we don't need to duplicate the conversion logic in several
places within main.cpp.
2019-05-09 01:46:01 -04:00
Lioncash
bf1829a717 yuzu/about_dialog: Specify string conversions explicitly
Specifies the conversions explicitly to avoid implicit conversions from
const char* to QString. This makes it easier to disable implicit QString
conversions in the future.

In this case, the implicit conversion was technically wrong as well. The
implicit conversion treats the input strings as ASCII characters. This
would result in an incorrect conversion being performed in the rare case
a branch name was created with a non-ASCII Unicode character, likely
resulting in junk being displayed.
2019-05-09 01:18:37 -04:00
Lioncash
a97120efc1 yuzu/compatdb: Remove unnecessary qualifiers
Keeps the code consistent in regards to how the buttons are referred to.
2019-05-09 01:08:06 -04:00
Lioncash
4ef3329f81 configuration/config: Move config loading and saving to functions based off groups
Over time our config values have grown quite numerous in size.
Unfortunately it also makes the single functions we have for loading and
saving values more error prone.

For example, we were loading the core settings twice when they only
should have been loaded once. In another section, a variable was
shadowing another variable used to load settings from a completely
different section.

Finally, in one other case, there was an extraneous endGroup() call used
that didn't need to be done. This was essentially dead code and also a
bug waiting to happen.

This separates the section loading code into its own separate functions.
This keeps variables only visible to the code that actually needs it,
and makes it much easier to visually see the end of each individual
configuration group. It also makes it much easier to visually catch bugs
during code review.

While we're at it, this also uses QStringLiteral instead of raw string
literals, which both avoids constructing a lot of QString instances, but
also makes it much easier to disable implicit ASCII to QString and
vice-versa in the future via setting QT_NO_CAST_FROM_ASCII and
QT_NO_CAST_TO_ASCII as compilation flags.
2019-05-09 00:52:49 -04:00
Lioncash
70c6506a7e src/CMakeLists: Add /Zc:externConstexpr to the MSVC build flags
The C++ standard allows constexpr variables declared with the extern
keyword to have external linkage. Previously MSVC wasn't abiding by
this. This just makes the compiler more standards compliant during
builds.

Given we currently don't make use of anything that would break by this,
this is safe to enable.
2019-05-07 14:06:22 -04:00
Lioncash
6ca7241bd9 src/CMakeLists: Vertically order compilation flags
Makes it much nicer to visually scan the options. This also starts the
flag descriptions from the same column for the same reason.
2019-05-07 14:05:48 -04:00
Lioncash
495a8d8d95 core/memory: Remove unused FlushMode enum
Recent changes to memory-related code resulted in this being unused, so
we can remove it.
2019-05-07 13:55:17 -04:00
Fernando Sahmkow
3a08c3207b Correct possible error on Rasterizer Caches
There was a weird bug that could happen if the object died directly and
the cache address wasn't stored.
2019-05-07 12:33:10 -04:00
Lioncash
0964444529 externals: Update dynarmic to master
Better instruction support has been added since the last update.
2019-05-07 10:39:25 -04:00
Merry
c63e68c480 Merge pull request #2451 from lioncash/travis
travis: Update to using Xcode 10.2
2019-05-07 15:36:13 +01:00
Lioncash
4aefd45193 travis: Update to using Xcode 10.2
Keeps the CI toolchain updated. This is also necessary for updating
dynarmic.
2019-05-07 06:40:30 -04:00
Rodrigo Locatti
6743982d28 Merge pull request #2447 from lioncash/dtor
core/frontend/emu_window: Make GraphicsContext's destructor virtual
2019-05-07 05:54:04 -03:00
Rodrigo Locatti
57db3f6763 Merge pull request #2448 from lioncash/pragma
common/zstd_compression: Remove #pragma once directive from source file
2019-05-07 05:51:37 -03:00
Rodrigo Locatti
a206418846 Merge pull request #2449 from lioncash/unused-var
gl_rasterizer: Silence unused variable warnings
2019-05-07 05:50:59 -03:00
zhupengfei
10c4f23953 core/telemetry_session: Only create the backend when we really need it
The backend is not used until we decide to submit the testcase/telemetry, and creating it early prevents users from updating the credentials properly while the games are running.
2019-05-04 19:45:48 +02:00
Lioncash
9e15193ef8 shader/decode/texture: Remove unused variable
This isn't used anywhere, so we can get rid of it.
2019-05-04 02:10:38 -04:00
Lioncash
5d0dca73c6 CMakeLists: Explicitly specify -Wall for the non-MSVC case
Ensures that -Wall is always active as a compilation flag.
2019-05-04 02:06:56 -04:00
Lioncash
08b270676b gl_rasterizer: Silence unused variable warning
Makes use of src, so it's not considered unused.
2019-05-04 02:00:17 -04:00
Lioncash
a6f7a44aab common/zstd_compression: Remove #pragma once directive from source file
Introduced in 72477731ed. This is only
necessary within header files.
2019-05-04 01:54:29 -04:00
Lioncash
1230a0e7ce core/frontend/emu_window: Make GraphicsContext's destructor virtual
This class is used in a polymorphic context, so destruction of the
context will lead to undefined behavior if the destructor isn't virtual.
2019-05-04 01:47:38 -04:00
bunnei
1f72bb733f Merge pull request #2408 from FearlessTobi/port-4215
Port citra-emu/citra#4215: "travis: Use Ninja for Travis builds"
2019-05-02 20:59:09 -04:00
ReinUsesLisp
d4df803b2b shader_ir/other: Implement IPA.IDX 2019-05-02 21:46:37 -03:00
ReinUsesLisp
5321cdd276 gl_shader_decompiler: Skip physical unused attributes 2019-05-02 21:46:37 -03:00
ReinUsesLisp
28bffb1ffa shader_ir/memory: Assert on non-32 bits ALD.PHYS 2019-05-02 21:46:25 -03:00
ReinUsesLisp
fe700e1856 shader: Add physical attributes commentaries 2019-05-02 21:46:25 -03:00
ReinUsesLisp
c6f9e651b2 gl_shader_decompiler: Implement GLSL physical attributes 2019-05-02 21:46:25 -03:00
ReinUsesLisp
71aa9d0877 shader_ir/memory: Implement physical input attributes 2019-05-02 21:46:25 -03:00
ReinUsesLisp
b7d412c99b gl_shader_decompiler: Abstract generic attribute operations 2019-05-02 21:46:25 -03:00
ReinUsesLisp
bd81a03d9d gl_shader_decompiler: Declare all possible varyings on physical attribute usage 2019-05-02 21:46:25 -03:00
ReinUsesLisp
06b363c9b5 shader: Remove unused AbufNode Ipa mode 2019-05-02 21:46:25 -03:00
ReinUsesLisp
002ecbea19 shader_ir/memory: Emit AL2P IR 2019-05-02 21:46:25 -03:00
ReinUsesLisp
7632a7d6d2 shader_bytecode: Add AL2P decoding 2019-05-02 21:46:25 -03:00
Fernando Sahmkow
e64c41efe8 Refactors and name corrections. 2019-05-01 15:31:39 -04:00
Lioncash
2bcb8a20b4 service/audren_u: Handle variadic command buffers in GetWorkBufferSize()
Also introduced in REV5 was a variable-size audio command buffer. This
also affects how the size of the work buffer should be determined, so we
can add handling for this as well.

Thankfully, no other alterations were made to how the work buffer size
is calculated in 7.0.0-8.0.0. There were indeed changes made to to how
some of the actual audio commands are generated though (particularly in
REV7), however they don't apply here.
2019-04-30 23:52:28 -04:00
Lioncash
03746be097 service/audren_u: Handle version 2 of performance frame info in GetWorkBufferSize()
Introduced in REV5. This is trivial to add support for, now that
everything isn't a mess of random magic constant values.

All this is, is a change in data type sizes as far as this function
cares.
2019-04-30 23:52:28 -04:00
Lioncash
de93507a5a service/audren_u: Clean up work buffer calculations
"Unmagics" quite a few magic constants within this code, making it much
easier to understand. Particularly given this factors out specific
sections into their own self-contained lambda functions.
2019-04-30 23:51:59 -04:00
ReinUsesLisp
4aa081b4e7 gl_shader_disk_cache: Skip stored shader variants instead of asserting
Instead of asserting on already stored shader variants, silently skip them.
This shouldn't be happening but when a shader is invalidated and it is
not stored in the shader cache, this assert would hit and save that
shader anyways when the asserts are disabled.
2019-05-01 00:36:11 -03:00
Fernando Sahmkow
95261639fb Fix Layered ASTC Textures
By adding the missing layer offset in ASTC compression.
2019-04-30 23:02:31 -04:00
Lioncash
75a8b304d4 loader/nso: Remove left-in debug pragma
Unintentionally introduced in 552d5071fa
2019-04-30 22:55:53 -04:00
bunnei
fb420358a9 Merge pull request #2406 from FearlessTobi/port-3839
Port citra-emu/citra#3839: "travis: use prebuilt image"
2019-04-30 19:25:53 -04:00
bunnei
79e54abe19 Merge pull request #2100 from FreddyFunk/disk-cache-precompiled-file
gl_shader_disk_cache: Improve precompiled shader cache generation speed and size
2019-04-30 19:24:01 -04:00
Lioncash
19632d2421 kernel/svc: Make svcCreateThread/svcStartThread/svcSleepThread/svcExitThread calls show up in the debug log
These are actually quite important indicators of thread lifetimes, so
they should be going into the debug log, rather than being treated as
misc info and delegated to the trace log.
2019-04-29 01:38:27 -04:00
Lioncash
d672c6e759 kernel/svc: Reorganize svcSetThreadCoreMask()
Makes the code much nicer to follow in terms of behavior and control
flow. It also fixes a few bugs in the implementation.

Notably, the thread's owner process shouldn't be accessed in order to
retrieve the core mask or ideal core. This should be done through the
current running process. The only reason this bug wasn't encountered yet
is because we currently only support running one process, and thus every
owner process will be the current process.

We also weren't checking against the process' CPU core mask to see if an
allowed core is specified or not.

With this out of the way, it'll be less noisy to implement proper
handling of the affinity flags internally within the kernel thread
instances.
2019-04-29 01:38:27 -04:00
Lioncash
69a2003a8e kernel/thread: Update thread processor ID flags
Adds the missing flags to the enum and documents them.
2019-04-29 01:37:51 -04:00
bunnei
91e239d66f Merge pull request #2435 from ReinUsesLisp/misc-vc
shader_ir: Miscellaneous fixes
2019-04-28 22:29:43 -04:00
bunnei
2be32eb3d2 Merge pull request #2412 from lioncash/system
kernel/vm_manager: Remove usages of global system accessors
2019-04-28 22:27:14 -04:00
bunnei
c52233ec8b Merge pull request #2322 from ReinUsesLisp/wswitch
video_core: Silent -Wswitch warnings
2019-04-28 22:24:58 -04:00
bunnei
9a3737120d Merge pull request #2423 from FernandoS27/half-correct
Corrections on Half Float operations: HADD2 HMUL2 and HFMA2
2019-04-28 22:24:22 -04:00
Lioncash
565fce71b1 service/audctl: Update documentation comments to be relative to 8.0.0
The state of these service calls are still the same in version 8.0.0.
2019-04-27 23:17:58 -04:00
Zach Hilman
4e462d1fd7 mii_manager: Fix incorrect loop condition in mii UUID generation code 2019-04-25 08:57:23 -04:00
Zach Hilman
851c01c45e profile_select: Port Service::Account::UUID to Common::UUID 2019-04-25 08:13:11 -04:00
Zach Hilman
1aa2b99a98 mii: Implement Delete and Destroy file 2019-04-25 08:07:57 -04:00
Zach Hilman
c40cff454d mii: Implement IsUpdated command (IPC 0) 2019-04-25 08:07:57 -04:00
Zach Hilman
f0db2e3ef3 mii_manager: Cleanup and optimization 2019-04-25 08:07:57 -04:00
Zach Hilman
e25a7891e9 mii: Implement IDatabaseService commands using MiiManager
Since the MiiManager was designed around the IPC interface, this is quite easy. Only functions that were clearly defined were implemented.
2019-04-25 08:07:57 -04:00
Zach Hilman
daf5b8c61b mii: Add MiiManager class to manage Mii database
Provides serialization/deserialization to the database in system save files, accessors for database state and proper handling of both major Mii formats (MiiInfo and MiiStoreData)
2019-04-25 08:07:57 -04:00
Zach Hilman
ca5638a142 common: Extract UUID to its own class
Since the Mii database uses UUIDs very similar to the Accounts database, it makes no sense to not share code between them.
2019-04-25 08:07:57 -04:00
FreddyFunk
1a3ff252a4 Re added new lines at the end of files 2019-04-23 23:19:28 +02:00
unknown
3091b40691 gl_shader_disk_cache: Compress precompiled shader cache file with Zstandard 2019-04-23 22:24:31 +02:00
unknown
9db2c734c9 gl_shader_disk_cache: Use VectorVfsFile for the virtual precompiled shader cache file 2019-04-23 22:24:23 +02:00
unknown
3fe542cf60 gl_shader_disk_cache: Remove per shader compression 2019-04-23 21:40:01 +02:00
Fernando Sahmkow
b3118ee316 Fixes and Corrections to DMA Engine 2019-04-23 15:28:18 -04:00
Fernando Sahmkow
f1e5314f1a Add Swizzle Parameters to the DMA engine 2019-04-23 11:21:00 -04:00
Fernando Sahmkow
e140e2ebc6 Add Documentation Headers to all the GPU Engines 2019-04-23 08:44:52 -04:00
Fernando Sahmkow
021d28c9b8 Corrections and styling 2019-04-23 08:02:24 -04:00
Fernando Sahmkow
701ce1c9d0 Implement Maxwell3D Data Upload 2019-04-22 19:27:36 -04:00
Fernando Sahmkow
e4ff140b99 Introduce skeleton of the GPU Compute Engine. 2019-04-22 19:05:43 -04:00
Fernando Sahmkow
a91d3fc639 Revamp Kepler Memory to use a subegine to manage uploads 2019-04-22 18:50:56 -04:00
Fernando Sahmkow
4c36b78567 Rasterizer Cache: Use a temporal storage for Surfaces loading/flushing.
This PR should heavily reduce memory usage since temporal buffers are no
longer stored per Surface but instead managed by the Rasterizer Cache.
2019-04-21 11:42:07 -04:00
Fernando Sahmkow
623b2e4b8f Corrections Half Float operations on const buffers and implement saturation. 2019-04-20 21:11:33 -04:00
Fernando Sahmkow
a3eb91ed8c RasterizerCache Redesign: Flush
flushing is now responsability of children caches instead of the cache 
object. This change will allow the specific cache to pass extra 
parameters on flushing and will allow more flexibility.
2019-04-19 20:44:56 -04:00
ReinUsesLisp
fbe8d1ceaa video_core: Silent -Wswitch warnings 2019-04-18 15:54:39 -03:00
Lioncash
b6a87b422e kernel/vm_manager: Remove usages of global system accessors
Makes the dependency on the system instance explicit within VMManager's
interface.
2019-04-16 20:02:50 -04:00
Cameron Cawley
1f3cc036da travis: Use Ninja for Travis builds 2019-04-16 01:06:34 +02:00
fearlessTobi
b67be7154d GenerateSCMRev: fix Travis compilation on repo forks 2019-04-16 00:34:22 +02:00
liushuyu
a9f58593d4 travis: use prebuilt image (#3839)
* travis: use prebuilt image

* travis: use prebuilt image (MinGW)
2019-04-15 22:22:09 +02:00
196 changed files with 8128 additions and 3433 deletions

View File

@@ -24,7 +24,7 @@ matrix:
- os: osx
env: NAME="macos build"
sudo: false
osx_image: xcode10.1
osx_image: xcode10.2
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"

View File

@@ -1,3 +1,3 @@
#!/bin/bash -ex
mkdir "$HOME/.ccache" || true
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh

View File

@@ -1,3 +1,3 @@
#!/bin/sh -ex
docker pull ubuntu:18.04
docker pull yuzuemu/build-environments:linux-mingw

View File

@@ -1,16 +1,6 @@
#!/bin/bash -ex
cd /yuzu
MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
apt-get update
apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
apt-get update
apt-get install -y ${MINGW_PACKAGES}
# fix a problem in current MinGW headers
wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h
# override Travis CI unreasonable ccache size
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
@@ -23,8 +13,8 @@ echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
make -j4
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
ninja
# Clean up the dirty hacks
rm /bin/uname && mv /bin/uname1 /bin/uname

View File

@@ -1,4 +1,4 @@
#!/bin/bash -ex
mkdir -p "$HOME/.ccache"
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -1,3 +1,3 @@
#!/bin/sh -ex
docker pull ubuntu:18.04
docker pull yuzuemu/build-environments:linux-fresh

View File

@@ -1,12 +1,9 @@
#!/bin/bash -ex
apt-get update
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev qtwebengine5-dev wget cmake ninja-build ccache
cd /yuzu
mkdir build && cd build
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
ninja
ccache -s

View File

@@ -7,6 +7,7 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
# TODO: Build using ninja instead of make
mkdir build && cd build
cmake --version
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON

View File

@@ -132,7 +132,7 @@ find_package(Threads REQUIRED)
if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.8")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
@@ -165,7 +165,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
if (MSVC)
message(STATUS "unicorn not found, falling back to bundled")
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
set(UNICORN_VER "unicorn-yuzu")
else()
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
@@ -233,7 +233,7 @@ endif()
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.12.0-msvc2017_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")

View File

@@ -19,7 +19,7 @@ set(BUILD_VERSION "0")
if (BUILD_REPOSITORY)
# regex capture the string nightly or canary into CMAKE_MATCH_1
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if (${CMAKE_MATCH_COUNT} GREATER 0)
if ("${CMAKE_MATCH_COUNT}" GREATER 0)
# capitalize the first letter of each word in the repo name.
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
foreach(WORD ${REPO_NAME_LIST})

View File

@@ -7,6 +7,10 @@ include(DownloadExternals)
add_library(catch-single-include INTERFACE)
target_include_directories(catch-single-include INTERFACE catch/single_include)
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# Dynarmic
if (ARCHITECTURE_x86_64)
set(DYNARMIC_TESTS OFF)
@@ -14,10 +18,6 @@ if (ARCHITECTURE_x86_64)
add_subdirectory(dynarmic)
endif()
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# getopt
if (MSVC)
add_subdirectory(getopt)

View File

@@ -90,12 +90,20 @@
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
@@ -111,7 +119,7 @@
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -21,15 +21,29 @@ if (MSVC)
# Ensure that projects build with Unicode support.
add_definitions(-DUNICODE -D_UNICODE)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - enhanced debug info for optimized builds
# /permissive- - enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /Zc:throwingNew - let codegen assume `operator new` will never return null
# /Zc:inline - let codegen omit inline functions in object files
add_compile_options(/W3 /MP /Zi /Zo /permissive- /EHsc /std:c++latest /Zc:throwingNew,inline)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /volatile:iso - Use strict standards-compliant volatile semantics.
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
# /Zc:inline - Let codegen omit inline functions in object files
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
add_compile_options(
/W3
/MP
/Zi
/Zo
/permissive-
/EHsc
/std:c++latest
/volatile:iso
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
)
# /GS- - No stack buffer overflow checks
add_compile_options("$<$<CONFIG:Release>:/GS->")
@@ -37,7 +51,10 @@ if (MSVC)
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
else()
add_compile_options("-Wno-attributes")
add_compile_options(
-Wall
-Wno-attributes
)
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_compile_options("-stdlib=libc++")

View File

@@ -123,6 +123,8 @@ add_library(common STATIC
timer.h
uint128.cpp
uint128.h
uuid.cpp
uuid.h
vector_math.h
web_result.h
zstd_compression.cpp

View File

@@ -78,16 +78,17 @@ namespace FileUtil {
// Remove any ending forward slashes from directory paths
// Modifies argument.
static void StripTailDirSlashes(std::string& fname) {
if (fname.length() > 1) {
std::size_t i = fname.length();
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
--i;
fname.resize(i);
if (fname.length() <= 1) {
return;
}
return;
std::size_t i = fname.length();
while (i > 0 && fname[i - 1] == DIR_SEP_CHR) {
--i;
}
fname.resize(i);
}
// Returns true if file filename exists
bool Exists(const std::string& filename) {
struct stat file_info;
@@ -107,7 +108,6 @@ bool Exists(const std::string& filename) {
return (result == 0);
}
// Returns true if filename is a directory
bool IsDirectory(const std::string& filename) {
struct stat file_info;
@@ -132,8 +132,6 @@ bool IsDirectory(const std::string& filename) {
return S_ISDIR(file_info.st_mode);
}
// Deletes a given filename, return true on success
// Doesn't supports deleting a directory
bool Delete(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "file {}", filename);
@@ -165,7 +163,6 @@ bool Delete(const std::string& filename) {
return true;
}
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& path) {
LOG_TRACE(Common_Filesystem, "directory {}", path);
#ifdef _WIN32
@@ -194,7 +191,6 @@ bool CreateDir(const std::string& path) {
#endif
}
// Creates the full path of fullPath returns true on success
bool CreateFullPath(const std::string& fullPath) {
int panicCounter = 100;
LOG_TRACE(Common_Filesystem, "path {}", fullPath);
@@ -230,7 +226,6 @@ bool CreateFullPath(const std::string& fullPath) {
}
}
// Deletes a directory filename, returns true on success
bool DeleteDir(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "directory {}", filename);
@@ -252,7 +247,6 @@ bool DeleteDir(const std::string& filename) {
return false;
}
// renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string& srcFilename, const std::string& destFilename) {
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
#ifdef _WIN32
@@ -268,7 +262,6 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
return false;
}
// copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string& srcFilename, const std::string& destFilename) {
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
#ifdef _WIN32
@@ -324,7 +317,6 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
#endif
}
// Returns the size of filename (64bit)
u64 GetSize(const std::string& filename) {
if (!Exists(filename)) {
LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename);
@@ -351,7 +343,6 @@ u64 GetSize(const std::string& filename) {
return 0;
}
// Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd) {
struct stat buf;
if (fstat(fd, &buf) != 0) {
@@ -361,7 +352,6 @@ u64 GetSize(const int fd) {
return buf.st_size;
}
// Overloaded GetSize, accepts FILE*
u64 GetSize(FILE* f) {
// can't use off_t here because it can be 32-bit
u64 pos = ftello(f);
@@ -377,7 +367,6 @@ u64 GetSize(FILE* f) {
return size;
}
// creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "{}", filename);
@@ -502,7 +491,6 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
return true;
}
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path) {
#ifndef _WIN32
if (source_path == dest_path)
@@ -539,8 +527,7 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
#endif
}
// Returns the current directory
std::string GetCurrentDir() {
std::optional<std::string> GetCurrentDir() {
// Get the current working directory (getcwd uses malloc)
#ifdef _WIN32
wchar_t* dir;
@@ -550,7 +537,7 @@ std::string GetCurrentDir() {
if (!(dir = getcwd(nullptr, 0))) {
#endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
return nullptr;
return {};
}
#ifdef _WIN32
std::string strDir = Common::UTF16ToUTF8(dir);
@@ -561,7 +548,6 @@ std::string GetCurrentDir() {
return strDir;
}
// Sets the current directory to the given directory
bool SetCurrentDir(const std::string& directory) {
#ifdef _WIN32
return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
@@ -673,8 +659,6 @@ std::string GetSysDirectory() {
return sysDir;
}
// Returns a string with a yuzu data dir or file in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path) {
static std::unordered_map<UserPath, std::string> paths;
auto& user_path = paths[UserPath::UserDir];
@@ -762,11 +746,11 @@ std::string GetNANDRegistrationDir(bool system) {
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
}
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) {
return IOFile(filename, text_file ? "w" : "wb").WriteString(str);
}
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str) {
IOFile file(filename, text_file ? "r" : "rb");
if (!file.IsOpen())
@@ -776,13 +760,6 @@ std::size_t ReadFileToString(bool text_file, const char* filename, std::string&
return file.ReadArray(&str[0], str.size());
}
/**
* Splits the filename into 8.3 format
* Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
* @param filename The normal filename to use
* @param short_name A 9-char array in which the short name will be written
* @param extension A 4-char array in which the extension will be written
*/
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension) {
const std::string forbidden_characters = ".\"/\\[]:;=, ";

View File

@@ -9,6 +9,7 @@
#include <fstream>
#include <functional>
#include <limits>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
@@ -118,7 +119,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
// Returns the current directory
std::string GetCurrentDir();
std::optional<std::string> GetCurrentDir();
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -146,9 +147,9 @@ const std::string& GetExeDirectory();
std::string AppDataRoamingDirectory();
#endif
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str);
/**
* Splits the filename into 8.3 format
@@ -257,8 +258,8 @@ public:
return WriteArray(&object, 1);
}
std::size_t WriteString(const std::string& str) {
return WriteArray(str.c_str(), str.length());
std::size_t WriteString(std::string_view str) {
return WriteArray(str.data(), str.length());
}
bool IsOpen() const {
@@ -286,8 +287,8 @@ private:
template <typename T>
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
#ifdef _MSC_VER
fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode);
fstream.open(Common::UTF8ToUTF16W(filename), openmode);
#else
fstream.open(filename.c_str(), openmode);
fstream.open(filename, openmode);
#endif
}

View File

@@ -41,4 +41,7 @@ struct Rectangle {
}
};
template <typename T>
Rectangle(T, T, T, T)->Rectangle<T>;
} // namespace Common

33
src/common/uuid.cpp Normal file
View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <random>
#include <fmt/format.h>
#include "common/uuid.h"
namespace Common {
UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return UUID{distribution(gen), distribution(gen)};
}
std::string UUID::Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
std::string UUID::FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
}
} // namespace Common

48
src/common/uuid.h Normal file
View File

@@ -0,0 +1,48 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
namespace Common {
constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid = INVALID_UUID;
constexpr UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
constexpr explicit operator bool() const {
return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
}
constexpr bool operator==(const UUID& rhs) const {
// TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
}
constexpr bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
constexpr void Invalidate() {
uuid = INVALID_UUID;
}
std::string Format() const;
std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
} // namespace Common

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <zstd.h>

View File

@@ -310,6 +310,8 @@ add_library(core STATIC
hle/service/mig/mig.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mii/mii_manager.cpp
hle/service/mii/mii_manager.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/ncm/ncm.cpp

View File

@@ -14,11 +14,11 @@ namespace Core::Timing {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
s64 usToCycles(s64 us) {
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
if (static_cast<u64>(us / 1000000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (us > MAX_VALUE_TO_MULTIPLY) {
if (static_cast<u64>(us) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (us / 1000000);
}
@@ -38,11 +38,11 @@ s64 usToCycles(u64 us) {
}
s64 nsToCycles(s64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
if (static_cast<u64>(ns / 1000000000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (ns > MAX_VALUE_TO_MULTIPLY) {
if (static_cast<u64>(ns) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (ns / 1000000000);
}

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
namespace Core::Frontend {
@@ -10,9 +11,9 @@ namespace Core::Frontend {
ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const {
std::function<void(std::optional<Common::UUID>)> callback) const {
Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{}));
callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
}

View File

@@ -6,7 +6,7 @@
#include <functional>
#include <optional>
#include "core/hle/service/acc/profile_manager.h"
#include "common/uuid.h"
namespace Core::Frontend {
@@ -14,14 +14,12 @@ class ProfileSelectApplet {
public:
virtual ~ProfileSelectApplet();
virtual void SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0;
virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0;
};
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
public:
void SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override;
};
} // namespace Core::Frontend

View File

@@ -10,6 +10,8 @@
namespace Core::Frontend {
GraphicsContext::~GraphicsContext() = default;
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
public std::enable_shared_from_this<TouchState> {
public:

View File

@@ -19,6 +19,8 @@ namespace Core::Frontend {
*/
class GraphicsContext {
public:
virtual ~GraphicsContext();
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
@@ -167,8 +169,7 @@ private:
* For the request to be honored, EmuWindow implementations will usually reimplement this
* function.
*/
virtual void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
virtual void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>) {
// By default, ignore this request and do nothing.
}

View File

@@ -438,7 +438,7 @@ inline float RequestParser::Pop() {
template <>
inline double RequestParser::Pop() {
const u64 value = Pop<u64>();
float real;
double real;
std::memcpy(&real, &value, sizeof(real));
return real;
}

View File

@@ -43,7 +43,7 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s
}
SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback,
const std::string& reason, u64 timeout, WakeupCallback&& callback,
SharedPtr<WritableEvent> writable_event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->SetWakeupCallback([context = *this, callback](
@@ -58,7 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
auto& kernel = Core::System::GetInstance().Kernel();
if (!writable_event) {
// Create event if not provided
const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
"HLE Pause Event: " + reason);
writable_event = pair.writable;
}
@@ -76,8 +76,9 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
return writable_event;
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
: server_session(std::move(server_session)) {
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session,
SharedPtr<Thread> thread)
: server_session(std::move(server_session)), thread(std::move(thread)) {
cmd_buf[0] = 0;
}

View File

@@ -97,7 +97,7 @@ protected:
*/
class HLERequestContext {
public:
explicit HLERequestContext(SharedPtr<ServerSession> session);
explicit HLERequestContext(SharedPtr<ServerSession> session, SharedPtr<Thread> thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -119,7 +119,6 @@ public:
/**
* Puts the specified guest thread to sleep until the returned event is signaled or until the
* specified timeout expires.
* @param thread Thread to be put to sleep.
* @param reason Reason for pausing the thread, to be used for debugging purposes.
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
* invoked with a Timeout reason.
@@ -130,8 +129,8 @@ public:
* created.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
u64 timeout, WakeupCallback&& callback,
SharedPtr<WritableEvent> SleepClientThread(const std::string& reason, u64 timeout,
WakeupCallback&& callback,
SharedPtr<WritableEvent> writable_event = nullptr);
/// Populates this context with data from the requesting process/thread.
@@ -268,6 +267,7 @@ private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;
SharedPtr<Thread> thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<SharedPtr<Object>, 8> move_objects;
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;

View File

@@ -33,8 +33,8 @@ enum class HandleType : u32 {
};
enum class ResetType {
OneShot, ///< Reset automatically on object acquisition
Sticky, ///< Never reset automatically
Automatic, ///< Reset automatically on object acquisition
Manual, ///< Never reset automatically
};
class Object : NonCopyable {

View File

@@ -241,7 +241,8 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
}
Process::Process(Core::System& system)
: WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
: WaitObject{system.Kernel()}, vm_manager{system},
address_arbiter{system}, mutex{system}, system{system} {}
Process::~Process() = default;

View File

@@ -21,8 +21,9 @@ bool ReadableEvent::ShouldWait(const Thread* thread) const {
void ReadableEvent::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
if (reset_type == ResetType::OneShot)
if (reset_type == ResetType::Automatic) {
signaled = false;
}
}
void ReadableEvent::Signal() {

View File

@@ -130,7 +130,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
// similar.
Kernel::HLERequestContext context(this);
Kernel::HLERequestContext context(this, thread);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);

View File

@@ -1255,8 +1255,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
return vm_manager.MapCodeMemory(dst_address, src_address, size);
}
ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
u64 src_address, u64 size) {
static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
u64 dst_address, u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
"size=0x{:016X}",
@@ -1342,7 +1342,7 @@ static void ExitProcess(Core::System& system) {
/// Creates a new thread
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
VAddr stack_top, u32 priority, s32 processor_id) {
LOG_TRACE(Kernel_SVC,
LOG_DEBUG(Kernel_SVC,
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
entry_point, arg, stack_top, priority, processor_id, *out_handle);
@@ -1402,7 +1402,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
/// Starts the thread for the provided handle
static ResultCode StartThread(Core::System& system, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
@@ -1425,7 +1425,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
/// Called when a thread exits
static void ExitThread(Core::System& system) {
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->Stop();
@@ -1435,7 +1435,7 @@ static void ExitThread(Core::System& system) {
/// Sleep the current thread
static void SleepThread(Core::System& system, s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds);
enum class SleepType : s64 {
YieldWithoutLoadBalancing = 0,
@@ -1880,11 +1880,51 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
}
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
u64 mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle,
mask, core);
u64 affinity_mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
thread_handle, core, affinity_mask);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const auto* const current_process = system.Kernel().CurrentProcess();
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
const u8 ideal_cpu_core = current_process->GetIdealCore();
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
// Set the target CPU to the ideal core specified by the process.
core = ideal_cpu_core;
affinity_mask = 1ULL << core;
} else {
const u64 core_mask = current_process->GetCoreMask();
if ((core_mask | affinity_mask) != core_mask) {
LOG_ERROR(
Kernel_SVC,
"Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
core_mask, affinity_mask);
return ERR_INVALID_PROCESSOR_ID;
}
if (affinity_mask == 0) {
LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
return ERR_INVALID_COMBINATION;
}
if (core < Core::NUM_CPU_CORES) {
if ((affinity_mask & (1ULL << core)) == 0) {
LOG_ERROR(Kernel_SVC,
"Core is not enabled for the current mask, core={}, mask={:016X}", core,
affinity_mask);
return ERR_INVALID_COMBINATION;
}
} else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
return ERR_INVALID_PROCESSOR_ID;
}
}
const auto& handle_table = current_process->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
@@ -1892,40 +1932,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
return ERR_INVALID_HANDLE;
}
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
// Set the target CPU to the ideal core specified by the process.
core = ideal_cpu_core;
mask = 1ULL << core;
}
if (mask == 0) {
LOG_ERROR(Kernel_SVC, "Mask is 0");
return ERR_INVALID_COMBINATION;
}
/// This value is used to only change the affinity mask without changing the current ideal core.
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
if (core == OnlyChangeMask) {
core = thread->GetIdealCore();
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core);
return ERR_INVALID_PROCESSOR_ID;
}
// Error out if the input core isn't enabled in the input mask.
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}",
core, mask);
return ERR_INVALID_COMBINATION;
}
thread->ChangeCore(core, mask);
thread->ChangeCore(core, affinity_mask);
return RESULT_SUCCESS;
}
@@ -1980,7 +1987,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle
auto& kernel = system.Kernel();
const auto [readable_event, writable_event] =
WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent");
WritableEvent::CreateEventPair(kernel, ResetType::Manual, "CreateEvent");
HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
@@ -2183,8 +2190,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
return RESULT_SUCCESS;
}
ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
u32 out_thread_ids_size, Handle debug_handle) {
static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
u32 out_thread_ids_size, Handle debug_handle) {
// TODO: Handle this case when debug events are supported.
UNIMPLEMENTED_IF(debug_handle != InvalidHandle);

View File

@@ -30,12 +30,21 @@ enum ThreadPriority : u32 {
};
enum ThreadProcessorId : s32 {
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
/// Indicates that no particular processor core is preferred.
THREADPROCESSORID_DONT_CARE = -1,
/// Run thread on the ideal core specified by the process.
THREADPROCESSORID_IDEAL = -2,
/// Indicates that the preferred processor ID shouldn't be updated in
/// a core mask setting operation.
THREADPROCESSORID_DONT_UPDATE = -3,
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
/// Allowed CPU mask
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |

View File

@@ -62,7 +62,7 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
return true;
}
VMManager::VMManager() {
VMManager::VMManager(Core::System& system) : system{system} {
// Default to assuming a 39-bit address space. This way we have a sane
// starting point with executables that don't provide metadata.
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
@@ -111,7 +111,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
auto& system = Core::System::GetInstance();
system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
VMAPermission::ReadWriteExecute);
system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
@@ -140,7 +139,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
auto& system = Core::System::GetInstance();
system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
@@ -223,7 +221,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
ASSERT(FindVMA(target)->second.size >= size);
auto& system = Core::System::GetInstance();
system.ArmInterface(0).UnmapMemory(target, size);
system.ArmInterface(1).UnmapMemory(target, size);
system.ArmInterface(2).UnmapMemory(target, size);
@@ -376,7 +373,7 @@ ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64
Reprotect(src_vma_iter, VMAPermission::ReadWrite);
if (dst_memory_state == MemoryState::ModuleCode) {
Core::System::GetInstance().InvalidateCpuInstructionCaches();
system.InvalidateCpuInstructionCaches();
}
return unmap_result;

View File

@@ -14,6 +14,10 @@
#include "core/hle/result.h"
#include "core/memory.h"
namespace Core {
class System;
}
namespace FileSys {
enum class ProgramAddressSpaceType : u8;
}
@@ -321,7 +325,7 @@ class VMManager final {
public:
using VMAHandle = VMAMap::const_iterator;
VMManager();
explicit VMManager(Core::System& system);
~VMManager();
/// Clears the address space map, re-initializing with a single free area.
@@ -712,5 +716,7 @@ private:
// The end of the currently allocated heap. This is not an inclusive
// end of the range. This is essentially 'base_address + current_size'.
VAddr heap_end = 0;
Core::System& system;
};
} // namespace Kernel

View File

@@ -34,7 +34,7 @@ constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
}};
static std::string GetImagePath(UUID uuid) {
static std::string GetImagePath(Common::UUID uuid) {
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
}
@@ -46,7 +46,7 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
class IProfile final : public ServiceFramework<IProfile> {
public:
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager)
: ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) {
static const FunctionInfo functions[] = {
{0, &IProfile::Get, "Get"},
@@ -131,7 +131,7 @@ private:
}
const ProfileManager& profile_manager;
UUID user_id; ///< The user id this profile refers to.
Common::UUID user_id; ///< The user id this profile refers to.
};
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
@@ -179,7 +179,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
UUID user_id = rp.PopRaw<UUID>();
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
@@ -205,12 +205,12 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser());
rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
}
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
UUID user_id = rp.PopRaw<UUID>();
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -245,15 +245,15 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
IPC::ResponseBuilder rb{ctx, 6};
if (profile_manager->GetUserCount() != 1) {
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u128>(INVALID_UUID);
rb.PushRaw<u128>(Common::INVALID_UUID);
return;
}
const auto user_list = profile_manager->GetAllUsers();
if (std::all_of(user_list.begin(), user_list.end(),
[](const auto& user) { return user.uuid == INVALID_UUID; })) {
[](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
rb.PushRaw<u128>(INVALID_UUID);
rb.PushRaw<u128>(Common::INVALID_UUID);
return;
}

View File

@@ -13,6 +13,8 @@
namespace Service::Account {
using Common::UUID;
struct UserRaw {
UUID uuid;
UUID uuid2;
@@ -35,26 +37,6 @@ constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return UUID{distribution(gen), distribution(gen)};
}
std::string UUID::Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
std::string UUID::FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
}
ProfileManager::ProfileManager() {
ParseUserSaveFile();
@@ -217,7 +199,7 @@ bool ProfileManager::UserExists(UUID uuid) const {
bool ProfileManager::UserExistsIndex(std::size_t index) const {
if (index >= MAX_USERS)
return false;
return profiles[index].user_uuid.uuid != INVALID_UUID;
return profiles[index].user_uuid.uuid != Common::INVALID_UUID;
}
/// Opens a specific user
@@ -311,7 +293,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
const auto index = GetUserIndex(uuid);
if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) {
return false;
}
@@ -342,7 +324,7 @@ void ProfileManager::ParseUserSaveFile() {
}
for (const auto& user : data.users) {
if (user.uuid == UUID(INVALID_UUID)) {
if (user.uuid == UUID(Common::INVALID_UUID)) {
continue;
}

View File

@@ -9,47 +9,15 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"
#include "core/hle/result.h"
namespace Service::Account {
constexpr std::size_t MAX_USERS = 8;
constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid = INVALID_UUID;
UUID() = default;
explicit UUID(const u128& id) : uuid{id} {}
explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
explicit operator bool() const {
return uuid != INVALID_UUID;
}
bool operator==(const UUID& rhs) const {
return uuid == rhs.uuid;
}
bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
void Invalidate() {
uuid = INVALID_UUID;
}
std::string Format() const;
std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
constexpr std::size_t profile_username_size = 32;
using ProfileUsername = std::array<u8, profile_username_size>;
using UserIDArray = std::array<UUID, MAX_USERS>;
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
@@ -66,7 +34,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
UUID user_uuid;
Common::UUID user_uuid;
ProfileUsername username;
u64 creation_time;
ProfileData data; // TODO(ognik): Work out what this is
@@ -74,7 +42,7 @@ struct ProfileInfo {
};
struct ProfileBase {
UUID user_uuid;
Common::UUID user_uuid;
u64_le timestamp;
ProfileUsername username;
@@ -96,33 +64,33 @@ public:
~ProfileManager();
ResultCode AddUser(const ProfileInfo& user);
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
ResultCode CreateNewUser(UUID uuid, const std::string& username);
std::optional<UUID> GetUser(std::size_t index) const;
std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
ResultCode CreateNewUser(Common::UUID uuid, const std::string& username);
std::optional<Common::UUID> GetUser(std::size_t index) const;
std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const;
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
ProfileData& data) const;
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(UUID uuid) const;
bool UserExists(Common::UUID uuid) const;
bool UserExistsIndex(std::size_t index) const;
void OpenUser(UUID uuid);
void CloseUser(UUID uuid);
void OpenUser(Common::UUID uuid);
void CloseUser(Common::UUID uuid);
UserIDArray GetOpenUsers() const;
UserIDArray GetAllUsers() const;
UUID GetLastOpenedUser() const;
Common::UUID GetLastOpenedUser() const;
bool CanSystemRegisterUser() const;
bool RemoveUser(UUID uuid);
bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
bool RemoveUser(Common::UUID uuid);
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
private:
void ParseUserSaveFile();
@@ -132,7 +100,7 @@ private:
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count = 0;
UUID last_opened_user{INVALID_UUID};
Common::UUID last_opened_user{Common::INVALID_UUID};
};
}; // namespace Service::Account

View File

@@ -276,7 +276,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"ISelfController:LaunchableEvent");
}
@@ -442,10 +442,10 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
AppletMessageQueue::AppletMessageQueue() {
auto& kernel = Core::System::GetInstance().Kernel();
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"AMMessageQueue:OnMessageRecieved");
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged");
kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged");
}
AppletMessageQueue::~AppletMessageQueue() = default;
@@ -835,6 +835,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
return;
}
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
@@ -857,6 +858,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
return;
}
ctx.WriteBuffer(backing.buffer.data() + offset, size);

View File

@@ -26,11 +26,11 @@ namespace Service::AM::Applets {
AppletDataBroker::AppletDataBroker() {
auto& kernel = Core::System::GetInstance().Kernel();
state_changed_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent");
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent");
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
}
AppletDataBroker::~AppletDataBroker() = default;

View File

@@ -53,19 +53,19 @@ void ProfileSelect::Execute() {
return;
}
frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); });
}
void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) {
void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
UserSelectionOutput output{};
if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) {
if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) {
output.result = 0;
output.uuid_selected = uuid->uuid;
} else {
status = ERR_USER_CANCELLED_SELECTION;
output.result = ERR_USER_CANCELLED_SELECTION.raw;
output.uuid_selected = Account::INVALID_UUID;
output.uuid_selected = Common::INVALID_UUID;
}
final_data = std::vector<u8>(sizeof(UserSelectionOutput));

View File

@@ -7,7 +7,8 @@
#include <vector>
#include "common/common_funcs.h"
#include "core/hle/service/acc/profile_manager.h"
#include "common/uuid.h"
#include "core/hle/result.h"
#include "core/hle/service/am/applets/applets.h"
namespace Service::AM::Applets {
@@ -38,7 +39,7 @@ public:
void ExecuteInteractive() override;
void Execute() override;
void SelectionComplete(std::optional<Account::UUID> uuid);
void SelectionComplete(std::optional<Common::UUID> uuid);
private:
const Core::Frontend::ProfileSelectApplet& frontend;

View File

@@ -9,7 +9,6 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/ipc_helpers.h"
@@ -18,7 +17,6 @@
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
@@ -68,14 +66,22 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"GetAddOnContentListChanged:Event");
}
AOC_U::~AOC_U() = default;
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AOC, "called");
struct Parameters {
u64 process_id;
};
static_assert(sizeof(Parameters) == 8);
IPC::RequestParser rp{ctx};
const auto params = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -94,23 +100,32 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
}
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 offset;
u32 count;
u64 process_id;
};
static_assert(sizeof(Parameters) == 16);
const auto offset = rp.PopRaw<u32>();
auto count = rp.PopRaw<u32>();
LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count);
IPC::RequestParser rp{ctx};
const auto [offset, count, process_id] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
process_id);
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
std::vector<u32> out;
for (size_t i = 0; i < add_on_content.size(); ++i) {
if ((add_on_content[i] & DLC_BASE_TITLE_ID_MASK) == current)
out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF));
}
const auto& disabled = Settings::values.disabled_addons[current];
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end())
out = {};
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
for (u64 content_id : add_on_content) {
if ((content_id & DLC_BASE_TITLE_ID_MASK) != current) {
continue;
}
out.push_back(static_cast<u32>(content_id & 0x7FF));
}
}
if (out.size() < offset) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -119,22 +134,31 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
return;
}
count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
std::rotate(out.begin(), out.begin() + offset, out.end());
out.resize(count);
out.resize(out_count);
ctx.WriteBuffer(out);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(count);
rb.Push(out_count);
}
void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AOC, "called");
struct Parameters {
u64 process_id;
};
static_assert(sizeof(Parameters) == 8);
IPC::RequestParser rp{ctx};
const auto params = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
@@ -148,10 +172,17 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
}
void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
s32 addon_index;
u64 process_id;
};
static_assert(sizeof(Parameters) == 16);
const auto aoc_id = rp.PopRaw<u32>();
LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id);
IPC::RequestParser rp{ctx};
const auto [addon_index, process_id] = rp.PopRaw<Parameters>();
LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -50,7 +50,7 @@ void AudCtl::GetTargetVolumeMin(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called.");
// This service function is currently hardcoded on the
// actual console to this value (as of 6.0.0).
// actual console to this value (as of 8.0.0).
constexpr s32 target_min_volume = 0;
IPC::ResponseBuilder rb{ctx, 3};
@@ -62,7 +62,7 @@ void AudCtl::GetTargetVolumeMax(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called.");
// This service function is currently hardcoded on the
// actual console to this value (as of 6.0.0).
// actual console to this value (as of 8.0.0).
constexpr s32 target_max_volume = 15;
IPC::ResponseBuilder rb{ctx, 3};

View File

@@ -67,7 +67,7 @@ public:
// This is the event handle used to check if the audio buffer was released
auto& system = Core::System::GetInstance();
buffer_event = Kernel::WritableEvent::CreateEventPair(
system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
audio_params.channel_count, std::move(unique_name),

View File

@@ -8,6 +8,7 @@
#include "audio_core/audio_renderer.h"
#include "common/alignment.h"
#include "common/bit_util.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -46,7 +47,7 @@ public:
auto& system = Core::System::GetInstance();
system_event = Kernel::WritableEvent::CreateEventPair(
system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
system_event.writable);
}
@@ -178,7 +179,7 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IAudioOutBufferReleasedEvent");
}
@@ -262,64 +263,304 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
OpenAudioRendererImpl(ctx);
}
static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) {
// +1 represents the final mix.
return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
1;
}
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
LOG_DEBUG(Service_Audio, "called");
u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40);
buffer_sz += params.submix_count * 1024;
buffer_sz += 0x940 * (params.submix_count + 1);
buffer_sz += 0x3F0 * params.voice_count;
buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10);
buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
buffer_sz += Common::AlignUp(
(0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) *
(params.mix_buffer_count + 6),
0x40);
// Several calculations below align the sizes being calculated
// onto a 64 byte boundary.
static constexpr u64 buffer_alignment_size = 64;
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
const u32 count = params.submix_count + 1;
u64 node_count = Common::AlignUp(count, 0x40);
const u64 node_state_buffer_sz =
4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
u64 edge_matrix_buffer_sz = 0;
node_count = Common::AlignUp(count * count, 0x40);
if (node_count >> 31 != 0) {
edge_matrix_buffer_sz = (node_count | 7) / 8;
} else {
edge_matrix_buffer_sz = node_count / 8;
// Some calculations that calculate portions of the buffer
// that will contain information, on the other hand, align
// the result of some of their calcularions on a 16 byte boundary.
static constexpr u64 info_field_alignment_size = 16;
// Maximum detail entries that may exist at one time for performance
// frame statistics.
static constexpr u64 max_perf_detail_entries = 100;
// Size of the data structure representing the bulk of the voice-related state.
static constexpr u64 voice_state_size = 0x100;
// Size of the upsampler manager data structure
constexpr u64 upsampler_manager_size = 0x48;
// Calculates the part of the size that relates to mix buffers.
const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) {
// As of 8.0.0 this is the maximum on voice channels.
constexpr u64 max_voice_channels = 6;
// The service expects the sample_count member of the parameters to either be
// a value of 160 or 240, so the maximum sample count is assumed in order
// to adequately handle all values at runtime.
constexpr u64 default_max_sample_count = 240;
const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
u64 size = 0;
size += total_mix_buffers * (sizeof(s32) * params.sample_count);
size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
size += u64{params.submix_count} + params.sink_count;
size = Common::AlignUp(size, buffer_alignment_size);
size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
return size;
};
// Calculates the portion of the size related to the mix data (and the sorting thereof).
const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) {
// The size of the mixing info data structure.
constexpr u64 mix_info_size = 0x940;
// Consists of total submixes with the final mix included.
const u64 total_mix_count = u64{params.submix_count} + 1;
// The total number of effects that may be available to the audio renderer at any time.
constexpr u64 max_effects = 256;
// Calculates the part of the size related to the audio node state.
// This will only be used if the audio revision supports the splitter.
const auto calculate_node_state_size = [](std::size_t num_nodes) {
// Internally within a nodestate, it appears to use a data structure
// similar to a std::bitset<64> twice.
constexpr u64 bit_size = Common::BitSize<u64>();
constexpr u64 num_bitsets = 2;
// Node state instances have three states internally for performing
// depth-first searches of nodes. Initialized, Found, and Done Sorting.
constexpr u64 num_states = 3;
u64 size = 0;
size += (num_nodes * num_nodes) * sizeof(s32);
size += num_states * (num_nodes * sizeof(s32));
size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
return size;
};
// Calculates the part of the size related to the adjacency (aka edge) matrix.
const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
return (num_nodes * num_nodes) * sizeof(s32);
};
u64 size = 0;
size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
info_field_alignment_size);
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
calculate_edge_matrix_size(total_mix_count),
info_field_alignment_size);
}
buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
}
buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
buffer_sz += 0xE0 * params.num_splitter_send_channels;
buffer_sz += 0x20 * params.splitter_count;
buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10);
}
buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
((params.voice_count * 256) | 0x40);
return size;
};
if (params.performance_frame_count >= 1) {
output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
16 * params.voice_count + 16) +
0x658) *
(params.performance_frame_count + 1) +
0xc0,
0x40) +
output_sz;
}
output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
// Calculates the part of the size related to voice channel info.
const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) {
constexpr u64 voice_info_size = 0x220;
constexpr u64 voice_resource_size = 0xD0;
u64 size = 0;
size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
size +=
Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
size += Common::AlignUp(voice_state_size * params.voice_count, info_field_alignment_size);
return size;
};
// Calculates the part of the size related to memory pools.
const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) {
const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
const u64 memory_pool_info_size = 0x20;
return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
};
// Calculates the part of the size related to the splitter context.
const auto calculate_splitter_context_size =
[this](const AudioCore::AudioRendererParameter& params) -> u64 {
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
return 0;
}
constexpr u64 splitter_info_size = 0x20;
constexpr u64 splitter_destination_data_size = 0xE0;
u64 size = 0;
size += params.num_splitter_send_channels;
size +=
Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
info_field_alignment_size);
return size;
};
// Calculates the part of the size related to the upsampler info.
const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) {
constexpr u64 upsampler_info_size = 0x280;
// Yes, using the buffer size over info alignment size is intentional here.
return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count),
buffer_alignment_size);
};
// Calculates the part of the size related to effect info.
const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) {
constexpr u64 effect_info_size = 0x2B0;
return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
};
// Calculates the part of the size related to audio sink info.
const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) {
const u64 sink_info_size = 0x170;
return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
};
// Calculates the part of the size related to voice state info.
const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) {
const u64 voice_state_size = 0x100;
const u64 additional_size = buffer_alignment_size - 1;
return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
info_field_alignment_size);
};
// Calculates the part of the size related to performance statistics.
const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) {
// Extra size value appended to the end of the calculation.
constexpr u64 appended = 128;
// Whether or not we assume the newer version of performance metrics data structures.
const bool is_v2 =
IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
// Data structure sizes
constexpr u64 perf_statistics_size = 0x0C;
const u64 header_size = is_v2 ? 0x30 : 0x18;
const u64 entry_size = is_v2 ? 0x18 : 0x10;
const u64 detail_size = is_v2 ? 0x18 : 0x10;
const u64 entry_count = CalculateNumPerformanceEntries(params);
const u64 size_per_frame =
header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
u64 size = 0;
size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
buffer_alignment_size);
size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
size += appended;
return size;
};
// Calculates the part of the size that relates to the audio command buffer.
const auto calculate_command_buffer_size =
[this](const AudioCore::AudioRendererParameter& params) {
constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
constexpr u64 command_buffer_size = 0x18000;
return command_buffer_size + alignment;
}
// When the variadic command buffer is supported, this means
// the command generator for the audio renderer can issue commands
// that are (as one would expect), variable in size. So what we need to do
// is determine the maximum possible size for a few command data structures
// then multiply them by the amount of present commands indicated by the given
// respective audio parameters.
constexpr u64 max_biquad_filters = 2;
constexpr u64 max_mix_buffers = 24;
constexpr u64 biquad_filter_command_size = 0x2C;
constexpr u64 depop_mix_command_size = 0x24;
constexpr u64 depop_setup_command_size = 0x50;
constexpr u64 effect_command_max_size = 0x540;
constexpr u64 mix_command_size = 0x1C;
constexpr u64 mix_ramp_command_size = 0x24;
constexpr u64 mix_ramp_grouped_command_size = 0x13C;
constexpr u64 perf_command_size = 0x28;
constexpr u64 sink_command_size = 0x130;
constexpr u64 submix_command_max_size =
depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
constexpr u64 volume_command_size = 0x1C;
constexpr u64 volume_ramp_command_size = 0x20;
constexpr u64 voice_biquad_filter_command_size =
biquad_filter_command_size * max_biquad_filters;
constexpr u64 voice_data_command_size = 0x9C;
const u64 voice_command_max_size =
(params.splitter_count * depop_setup_command_size) +
(voice_data_command_size + voice_biquad_filter_command_size +
volume_ramp_command_size + mix_ramp_grouped_command_size);
// Now calculate the individual elements that comprise the size and add them together.
const u64 effect_commands_size = params.effect_count * effect_command_max_size;
const u64 final_mix_commands_size =
depop_mix_command_size + volume_command_size * max_mix_buffers;
const u64 perf_commands_size =
perf_command_size *
(CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
const u64 sink_commands_size = params.sink_count * sink_command_size;
const u64 splitter_commands_size =
params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
const u64 submix_commands_size = params.submix_count * submix_command_max_size;
const u64 voice_commands_size = params.voice_count * voice_command_max_size;
return effect_commands_size + final_mix_commands_size + perf_commands_size +
sink_commands_size + splitter_commands_size + submix_commands_size +
voice_commands_size + alignment;
};
IPC::RequestParser rp{ctx};
const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
u64 size = 0;
size += calculate_mix_buffer_sizes(params);
size += calculate_mix_info_size(params);
size += calculate_voice_info_size(params);
size += upsampler_manager_size;
size += calculate_memory_pools_size(params);
size += calculate_splitter_context_size(params);
size = Common::AlignUp(size, buffer_alignment_size);
size += calculate_upsampler_info_size(params);
size += calculate_effect_info_size(params);
size += calculate_sink_info_size(params);
size += calculate_voice_state_size(params);
size += calculate_perf_size(params);
size += calculate_command_buffer_size(params);
// finally, 4KB page align the size, and we're done.
size = Common::AlignUp(size, 4096);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(output_sz);
rb.Push<u64>(size);
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
}
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
@@ -357,10 +598,15 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
}
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
// Byte swap
const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
switch (feature) {
case AudioFeatures::Splitter:
return version_num >= 2u;
return version_num >= 2U;
case AudioFeatures::PerformanceMetricsVersion2:
case AudioFeatures::VariadicCommandBuffer:
return version_num >= 5U;
default:
return false;
}

View File

@@ -28,6 +28,8 @@ private:
enum class AudioFeatures : u32 {
Splitter,
PerformanceMetricsVersion2,
VariadicCommandBuffer,
};
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;

View File

@@ -34,8 +34,8 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
"BT:RegisterEvent");
register_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
}
private:

View File

@@ -57,13 +57,13 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IBtmUserCore:ScanEvent");
connection_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent");
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent");
service_discovery = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery");
config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IBtmUserCore:ConfigEvent");
}

View File

@@ -170,7 +170,7 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
void Controller_NPad::OnInit() {
auto& kernel = Core::System::GetInstance().Kernel();
styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged");
if (!IsControllerActivated()) {
return;

View File

@@ -4,42 +4,50 @@
#include <memory>
#include <fmt/ostream.h>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Mii {
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "IsUpdated"},
{1, nullptr, "IsFullDatabase"},
{2, nullptr, "GetCount"},
{3, nullptr, "Get"},
{4, nullptr, "Get1"},
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
{1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"},
{2, &IDatabaseService::GetCount, "GetCount"},
{3, &IDatabaseService::Get, "Get"},
{4, &IDatabaseService::Get1, "Get1"},
{5, nullptr, "UpdateLatest"},
{6, nullptr, "BuildRandom"},
{7, nullptr, "BuildDefault"},
{8, nullptr, "Get2"},
{9, nullptr, "Get3"},
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
{8, &IDatabaseService::Get2, "Get2"},
{9, &IDatabaseService::Get3, "Get3"},
{10, nullptr, "UpdateLatest1"},
{11, nullptr, "FindIndex"},
{12, nullptr, "Move"},
{13, nullptr, "AddOrReplace"},
{14, nullptr, "Delete"},
{15, nullptr, "DestroyFile"},
{16, nullptr, "DeleteFile"},
{17, nullptr, "Format"},
{11, &IDatabaseService::FindIndex, "FindIndex"},
{12, &IDatabaseService::Move, "Move"},
{13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
{14, &IDatabaseService::Delete, "Delete"},
{15, &IDatabaseService::DestroyFile, "DestroyFile"},
{16, &IDatabaseService::DeleteFile, "DeleteFile"},
{17, &IDatabaseService::Format, "Format"},
{18, nullptr, "Import"},
{19, nullptr, "Export"},
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
{21, nullptr, "GetIndex"},
{21, &IDatabaseService::GetIndex, "GetIndex"},
{22, nullptr, "SetInterfaceVersion"},
{23, nullptr, "Convert"},
};
@@ -47,6 +55,305 @@ public:
RegisterHandlers(functions);
}
private:
template <typename OutType>
std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
u32 requested_size, u32& read_size) {
read_size = std::min(requested_size, db.Size() - offset);
std::vector<u8> out(read_size * sizeof(OutType));
for (u32 i = 0; i < read_size; ++i) {
const auto obj = (db.*getter)(offset + i);
std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
}
return out;
}
void IsUpdated(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with source={}", source);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.CheckUpdatedFlag());
db.ResetUpdatedFlag();
}
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.Full());
}
void GetCount(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with source={}", source);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(db.Size());
}
// Gets Miis from database at offset and index in format MiiInfoElement
void Get(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[0], source);
u32 read_size{};
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
offsets[0] += read_size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
}
// Gets Miis from database at offset and index in format MiiInfo
void Get1(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[1], source);
u32 read_size{};
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
offsets[1] += read_size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
}
void BuildRandom(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
if (unknown1 > 3) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
return;
}
if (unknown2 > 2) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
return;
}
if (unknown3 > 3) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
return;
}
LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
unknown1, unknown2, unknown3);
const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<MiiInfo>(info);
}
void BuildDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto index{rp.PopRaw<u32>()};
if (index > 5) {
LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
const auto info = db.CreateDefault(index);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<MiiInfo>(info);
}
// Gets Miis from database at offset and index in format MiiStoreDataElement
void Get2(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[2], source);
u32 read_size{};
ctx.WriteBuffer(
SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
offsets[2] += read_size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
}
// Gets Miis from database at offset and index in format MiiStoreData
void Get3(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[3], source);
u32 read_size{};
ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
offsets[3] += read_size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
}
void FindIndex(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
const auto unknown{rp.PopRaw<bool>()};
LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
IPC::ResponseBuilder rb{ctx, 3};
const auto index = db.IndexOf(uuid);
if (index > MAX_MIIS) {
// TODO(DarkLordZach): Find a better error code
rb.Push(ResultCode(-1));
rb.Push(index);
} else {
rb.Push(RESULT_SUCCESS);
rb.Push(index);
}
}
void Move(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
const auto index{rp.PopRaw<s32>()};
if (index < 0) {
LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
const auto success = db.Move(uuid, index);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code
rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
}
void AddOrReplace(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto data{rp.PopRaw<MiiStoreData>()};
LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
Common::UTF16ToUTF8(data.Name()));
const auto success = db.AddOrReplace(data);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code
rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
}
void Delete(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
const auto success = db.Remove(uuid);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
}
void DestroyFile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
if (!db.IsTestModeEnabled()) {
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_IN_TEST_MODE);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.DestroyFile());
}
void DeleteFile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
if (!db.IsTestModeEnabled()) {
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_IN_TEST_MODE);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.DeleteFile());
}
void Format(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
db.Clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetIndex(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<MiiInfo>()};
LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
Common::UTF16ToUTF8(info.Name()));
const auto index = db.IndexOf(info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
rb.Push(index);
}
MiiManager db;
// Last read offsets of Get functions
std::array<u32, 4> offsets{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {

View File

@@ -0,0 +1,416 @@
// Copyright 2018 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/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/hle/service/mii/mii_manager.h"
namespace Service::Mii {
namespace {
constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
// This value was retrieved from HW test
constexpr MiiStoreData DEFAULT_MII = {
{
0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
},
{'y', 'u', 'z', 'u', '\0'},
Common::UUID{1, 0},
0,
0,
};
// Default values taken from multiple real databases
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
constexpr std::array<const char*, 4> SOURCE_NAMES{
"Database",
"Default",
"Account",
"Friend",
};
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
std::array<T, DestArraySize> out{};
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
return out;
}
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
MiiStoreBitFields bf{};
std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
return {
data.uuid,
ResizeArray<char16_t, 10, 11>(data.name),
static_cast<u8>(bf.font_region.Value()),
static_cast<u8>(bf.favorite_color.Value()),
static_cast<u8>(bf.gender.Value()),
static_cast<u8>(bf.height.Value()),
static_cast<u8>(bf.weight.Value()),
static_cast<u8>(bf.mii_type.Value()),
static_cast<u8>(bf.mii_region.Value()),
static_cast<u8>(bf.face_type.Value()),
static_cast<u8>(bf.face_color.Value()),
static_cast<u8>(bf.face_wrinkle.Value()),
static_cast<u8>(bf.face_makeup.Value()),
static_cast<u8>(bf.hair_type.Value()),
static_cast<u8>(bf.hair_color.Value()),
static_cast<bool>(bf.hair_flip.Value()),
static_cast<u8>(bf.eye_type.Value()),
static_cast<u8>(bf.eye_color.Value()),
static_cast<u8>(bf.eye_scale.Value()),
static_cast<u8>(bf.eye_aspect.Value()),
static_cast<u8>(bf.eye_rotate.Value()),
static_cast<u8>(bf.eye_x.Value()),
static_cast<u8>(bf.eye_y.Value()),
static_cast<u8>(bf.eyebrow_type.Value()),
static_cast<u8>(bf.eyebrow_color.Value()),
static_cast<u8>(bf.eyebrow_scale.Value()),
static_cast<u8>(bf.eyebrow_aspect.Value()),
static_cast<u8>(bf.eyebrow_rotate.Value()),
static_cast<u8>(bf.eyebrow_x.Value()),
static_cast<u8>(bf.eyebrow_y.Value()),
static_cast<u8>(bf.nose_type.Value()),
static_cast<u8>(bf.nose_scale.Value()),
static_cast<u8>(bf.nose_y.Value()),
static_cast<u8>(bf.mouth_type.Value()),
static_cast<u8>(bf.mouth_color.Value()),
static_cast<u8>(bf.mouth_scale.Value()),
static_cast<u8>(bf.mouth_aspect.Value()),
static_cast<u8>(bf.mouth_y.Value()),
static_cast<u8>(bf.facial_hair_color.Value()),
static_cast<u8>(bf.beard_type.Value()),
static_cast<u8>(bf.mustache_type.Value()),
static_cast<u8>(bf.mustache_scale.Value()),
static_cast<u8>(bf.mustache_y.Value()),
static_cast<u8>(bf.glasses_type.Value()),
static_cast<u8>(bf.glasses_color.Value()),
static_cast<u8>(bf.glasses_scale.Value()),
static_cast<u8>(bf.glasses_y.Value()),
static_cast<u8>(bf.mole_type.Value()),
static_cast<u8>(bf.mole_scale.Value()),
static_cast<u8>(bf.mole_x.Value()),
static_cast<u8>(bf.mole_y.Value()),
0x00,
};
}
MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
MiiStoreData out{};
out.name = ResizeArray<char16_t, 11, 10>(info.name);
out.uuid = info.uuid;
MiiStoreBitFields bf{};
bf.hair_type.Assign(info.hair_type);
bf.mole_type.Assign(info.mole_type);
bf.height.Assign(info.height);
bf.hair_flip.Assign(info.hair_flip);
bf.weight.Assign(info.weight);
bf.hair_color.Assign(info.hair_color);
bf.gender.Assign(info.gender);
bf.eye_color.Assign(info.eye_color);
bf.eyebrow_color.Assign(info.eyebrow_color);
bf.mouth_color.Assign(info.mouth_color);
bf.facial_hair_color.Assign(info.facial_hair_color);
bf.mii_type.Assign(info.mii_type);
bf.glasses_color.Assign(info.glasses_color);
bf.font_region.Assign(info.font_region);
bf.eye_type.Assign(info.eye_type);
bf.mii_region.Assign(info.mii_region);
bf.mouth_type.Assign(info.mouth_type);
bf.glasses_scale.Assign(info.glasses_scale);
bf.eye_y.Assign(info.eye_y);
bf.mustache_type.Assign(info.mustache_type);
bf.eyebrow_type.Assign(info.eyebrow_type);
bf.beard_type.Assign(info.beard_type);
bf.nose_type.Assign(info.nose_type);
bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
bf.nose_y.Assign(info.nose_y);
bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
bf.mouth_y.Assign(info.mouth_y);
bf.eye_rotate.Assign(info.eye_rotate);
bf.mustache_y.Assign(info.mustache_y);
bf.eye_aspect.Assign(info.eye_aspect_ratio);
bf.glasses_y.Assign(info.glasses_y);
bf.eye_scale.Assign(info.eye_scale);
bf.mole_x.Assign(info.mole_x);
bf.mole_y.Assign(info.mole_y);
bf.glasses_type.Assign(info.glasses_type);
bf.face_type.Assign(info.face_type);
bf.favorite_color.Assign(info.favorite_color);
bf.face_wrinkle.Assign(info.face_wrinkle);
bf.face_color.Assign(info.face_color);
bf.eye_x.Assign(info.eye_x);
bf.face_makeup.Assign(info.face_makeup);
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
bf.eyebrow_scale.Assign(info.eyebrow_scale);
bf.eyebrow_y.Assign(info.eyebrow_y);
bf.eyebrow_x.Assign(info.eyebrow_x);
bf.mouth_scale.Assign(info.mouth_scale);
bf.nose_scale.Assign(info.nose_scale);
bf.mole_scale.Assign(info.mole_scale);
bf.mustache_scale.Assign(info.mustache_scale);
std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
return out;
}
} // namespace
std::ostream& operator<<(std::ostream& os, Source source) {
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
return os;
}
std::u16string MiiInfo::Name() const {
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
}
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
}
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
return !operator==(lhs, rhs);
}
std::u16string MiiStoreData::Name() const {
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
}
MiiManager::MiiManager() = default;
MiiManager::~MiiManager() = default;
MiiInfo MiiManager::CreateRandom(RandomParameters params) {
LOG_WARNING(Service_Mii,
"(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
params.unknown_1, params.unknown_2, params.unknown_3);
return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
}
MiiInfo MiiManager::CreateDefault(u32 index) {
const auto new_mii = CreateMiiWithUniqueUUID();
database.miis.at(index) = new_mii;
EnsureDatabasePartition();
return ConvertStoreDataToInfo(new_mii);
}
bool MiiManager::CheckUpdatedFlag() const {
return updated_flag;
}
void MiiManager::ResetUpdatedFlag() {
updated_flag = false;
}
bool MiiManager::IsTestModeEnabled() const {
return is_test_mode_enabled;
}
bool MiiManager::Empty() const {
return Size() == 0;
}
bool MiiManager::Full() const {
return Size() == MAX_MIIS;
}
void MiiManager::Clear() {
updated_flag = true;
std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
}
u32 MiiManager::Size() const {
return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
[](const MiiStoreData& elem) { return elem.uuid; }));
}
MiiInfo MiiManager::GetInfo(u32 index) const {
return ConvertStoreDataToInfo(GetStoreData(index));
}
MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
return {GetInfo(index), Source::Database};
}
MiiStoreData MiiManager::GetStoreData(u32 index) const {
return database.miis.at(index);
}
MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
return {GetStoreData(index), Source::Database};
}
bool MiiManager::Remove(Common::UUID uuid) {
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
if (iter == database.miis.end())
return false;
updated_flag = true;
*iter = MiiStoreData{};
EnsureDatabasePartition();
return true;
}
u32 MiiManager::IndexOf(Common::UUID uuid) const {
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
if (iter == database.miis.end())
return INVALID_INDEX;
return static_cast<u32>(std::distance(database.miis.begin(), iter));
}
u32 MiiManager::IndexOf(const MiiInfo& info) const {
const auto iter =
std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
return ConvertStoreDataToInfo(elem) == info;
});
if (iter == database.miis.end())
return INVALID_INDEX;
return static_cast<u32>(std::distance(database.miis.begin(), iter));
}
bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
const auto index = IndexOf(uuid);
if (index == INVALID_INDEX || new_index >= MAX_MIIS)
return false;
updated_flag = true;
const auto moving = database.miis[index];
const auto replacing = database.miis[new_index];
if (replacing.uuid) {
database.miis[index] = replacing;
database.miis[new_index] = moving;
} else {
database.miis[index] = MiiStoreData{};
database.miis[new_index] = moving;
}
EnsureDatabasePartition();
return true;
}
bool MiiManager::AddOrReplace(const MiiStoreData& data) {
const auto index = IndexOf(data.uuid);
updated_flag = true;
if (index == INVALID_INDEX) {
const auto size = Size();
if (size == MAX_MIIS)
return false;
database.miis[size] = data;
} else {
database.miis[index] = data;
}
return true;
}
bool MiiManager::DestroyFile() {
database = DEFAULT_MII_DATABASE;
updated_flag = false;
return DeleteFile();
}
bool MiiManager::DeleteFile() {
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
return FileUtil::Exists(path) && FileUtil::Delete(path);
}
void MiiManager::WriteToFile() {
const auto raw_path =
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
FileUtil::Delete(raw_path);
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
if (!FileUtil::CreateFullPath(path)) {
LOG_WARNING(Service_Mii,
"Failed to create full path of MiiDatabase.dat. Create the directory "
"nand/system/save/8000000000000030 to mitigate this "
"issue.");
return;
}
FileUtil::IOFile save(path, "wb");
if (!save.IsOpen()) {
LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
"made in current session will be saved.");
return;
}
save.Resize(sizeof(MiiDatabase));
if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
"and/or regenerated on next run.");
save.Resize(0);
}
}
void MiiManager::ReadFromFile() {
FileUtil::IOFile save(
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
if (!save.IsOpen()) {
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
"blank Mii database with no Miis.");
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
return;
}
if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
"Mii database with no Miis.");
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
return;
}
EnsureDatabasePartition();
}
MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
auto new_mii = DEFAULT_MII;
do {
new_mii.uuid = Common::UUID::Generate();
} while (IndexOf(new_mii.uuid) != INVALID_INDEX);
return new_mii;
}
void MiiManager::EnsureDatabasePartition() {
std::stable_partition(database.miis.begin(), database.miis.end(),
[](const MiiStoreData& elem) { return elem.uuid; });
}
} // namespace Service::Mii

View File

@@ -0,0 +1,273 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/uuid.h"
namespace Service::Mii {
constexpr std::size_t MAX_MIIS = 100;
constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
struct RandomParameters {
u32 unknown_1;
u32 unknown_2;
u32 unknown_3;
};
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
enum class Source : u32 {
Database = 0,
Default = 1,
Account = 2,
Friend = 3,
};
std::ostream& operator<<(std::ostream& os, Source source);
struct MiiInfo {
Common::UUID uuid;
std::array<char16_t, 11> name;
u8 font_region;
u8 favorite_color;
u8 gender;
u8 height;
u8 weight;
u8 mii_type;
u8 mii_region;
u8 face_type;
u8 face_color;
u8 face_wrinkle;
u8 face_makeup;
u8 hair_type;
u8 hair_color;
bool hair_flip;
u8 eye_type;
u8 eye_color;
u8 eye_scale;
u8 eye_aspect_ratio;
u8 eye_rotate;
u8 eye_x;
u8 eye_y;
u8 eyebrow_type;
u8 eyebrow_color;
u8 eyebrow_scale;
u8 eyebrow_aspect_ratio;
u8 eyebrow_rotate;
u8 eyebrow_x;
u8 eyebrow_y;
u8 nose_type;
u8 nose_scale;
u8 nose_y;
u8 mouth_type;
u8 mouth_color;
u8 mouth_scale;
u8 mouth_aspect_ratio;
u8 mouth_y;
u8 facial_hair_color;
u8 beard_type;
u8 mustache_type;
u8 mustache_scale;
u8 mustache_y;
u8 glasses_type;
u8 glasses_color;
u8 glasses_scale;
u8 glasses_y;
u8 mole_type;
u8 mole_scale;
u8 mole_x;
u8 mole_y;
INSERT_PADDING_BYTES(1);
std::u16string Name() const;
};
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<MiiInfo>,
"All bits of MiiInfo must contribute to its value.");
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
#pragma pack(push, 4)
struct MiiInfoElement {
MiiInfo info;
Source source;
};
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
struct MiiStoreBitFields {
union {
u32 word_0;
BitField<24, 8, u32> hair_type;
BitField<23, 1, u32> mole_type;
BitField<16, 7, u32> height;
BitField<15, 1, u32> hair_flip;
BitField<8, 7, u32> weight;
BitField<0, 7, u32> hair_color;
};
union {
u32 word_1;
BitField<31, 1, u32> gender;
BitField<24, 7, u32> eye_color;
BitField<16, 7, u32> eyebrow_color;
BitField<8, 7, u32> mouth_color;
BitField<0, 7, u32> facial_hair_color;
};
union {
u32 word_2;
BitField<31, 1, u32> mii_type;
BitField<24, 7, u32> glasses_color;
BitField<22, 2, u32> font_region;
BitField<16, 6, u32> eye_type;
BitField<14, 2, u32> mii_region;
BitField<8, 6, u32> mouth_type;
BitField<5, 3, u32> glasses_scale;
BitField<0, 5, u32> eye_y;
};
union {
u32 word_3;
BitField<29, 3, u32> mustache_type;
BitField<24, 5, u32> eyebrow_type;
BitField<21, 3, u32> beard_type;
BitField<16, 5, u32> nose_type;
BitField<13, 3, u32> mouth_aspect;
BitField<8, 5, u32> nose_y;
BitField<5, 3, u32> eyebrow_aspect;
BitField<0, 5, u32> mouth_y;
};
union {
u32 word_4;
BitField<29, 3, u32> eye_rotate;
BitField<24, 5, u32> mustache_y;
BitField<21, 3, u32> eye_aspect;
BitField<16, 5, u32> glasses_y;
BitField<13, 3, u32> eye_scale;
BitField<8, 5, u32> mole_x;
BitField<0, 5, u32> mole_y;
};
union {
u32 word_5;
BitField<24, 5, u32> glasses_type;
BitField<20, 4, u32> face_type;
BitField<16, 4, u32> favorite_color;
BitField<12, 4, u32> face_wrinkle;
BitField<8, 4, u32> face_color;
BitField<4, 4, u32> eye_x;
BitField<0, 4, u32> face_makeup;
};
union {
u32 word_6;
BitField<28, 4, u32> eyebrow_rotate;
BitField<24, 4, u32> eyebrow_scale;
BitField<20, 4, u32> eyebrow_y;
BitField<16, 4, u32> eyebrow_x;
BitField<12, 4, u32> mouth_scale;
BitField<8, 4, u32> nose_scale;
BitField<4, 4, u32> mole_scale;
BitField<0, 4, u32> mustache_scale;
};
};
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
"MiiStoreBitFields is not trivially copyable.");
struct MiiStoreData {
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
// not suitable for our uses.
std::array<u8, 0x1C> data;
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
std::array<char16_t, 10> name;
Common::UUID uuid;
u16 crc_1;
u16 crc_2;
std::u16string Name() const;
};
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
struct MiiStoreDataElement {
MiiStoreData data;
Source source;
};
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
struct MiiDatabase {
u32 magic; // 'NFDB'
std::array<MiiStoreData, MAX_MIIS> miis;
INSERT_PADDING_BYTES(1);
u8 count;
u16 crc;
};
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
#pragma pack(pop)
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
// with providing an easy interface for HLE emulation of the mii service.
class MiiManager {
public:
MiiManager();
~MiiManager();
MiiInfo CreateRandom(RandomParameters params);
MiiInfo CreateDefault(u32 index);
bool CheckUpdatedFlag() const;
void ResetUpdatedFlag();
bool IsTestModeEnabled() const;
bool Empty() const;
bool Full() const;
void Clear();
u32 Size() const;
MiiInfo GetInfo(u32 index) const;
MiiInfoElement GetInfoElement(u32 index) const;
MiiStoreData GetStoreData(u32 index) const;
MiiStoreDataElement GetStoreDataElement(u32 index) const;
bool Remove(Common::UUID uuid);
u32 IndexOf(Common::UUID uuid) const;
u32 IndexOf(const MiiInfo& info) const;
bool Move(Common::UUID uuid, u32 new_index);
bool AddOrReplace(const MiiStoreData& data);
bool DestroyFile();
bool DeleteFile();
private:
void WriteToFile();
void ReadFromFile();
MiiStoreData CreateMiiWithUniqueUUID() const;
void EnsureDatabasePartition();
MiiDatabase database;
bool updated_flag = false;
bool is_test_mode_enabled = false;
};
}; // namespace Service::Mii

View File

@@ -26,7 +26,7 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {
auto& kernel = Core::System::GetInstance().Kernel();
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IUser:NFCTagDetected");
}
@@ -67,9 +67,9 @@ public:
auto& kernel = Core::System::GetInstance().Kernel();
deactivate_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
availability_change_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent");
}
private:

View File

@@ -62,9 +62,9 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IRequest:Event1");
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IRequest:Event2");
}

View File

@@ -141,7 +141,7 @@ public:
auto& kernel = Core::System::GetInstance().Kernel();
finished_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::OneShot,
kernel, Kernel::ResetType::Automatic,
"IEnsureNetworkClockAvailabilityService:FinishEvent");
}

View File

@@ -129,7 +129,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"NVDRV::query_event");
}

View File

@@ -16,7 +16,7 @@ namespace Service::NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
auto& kernel = Core::System::GetInstance().Kernel();
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"BufferQueue NativeHandle");
}

View File

@@ -2,16 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <chrono>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/service/set/set.h"
#include "core/settings.h"
namespace Service::Set {
namespace {
constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
@@ -32,41 +31,35 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::ZH_HANT,
}};
constexpr std::size_t pre4_0_0_max_entries = 0xF;
constexpr std::size_t post4_0_0_max_entries = 0x40;
constexpr std::size_t pre4_0_0_max_entries = 15;
constexpr std::size_t post4_0_0_max_entries = 17;
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(num_language_codes));
}
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
const std::size_t copy_amount = std::min(requested_amount, max_size);
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
ctx.WriteBuffer(available_language_codes.data(), copy_size);
PushResponseLanguageCode(ctx, copy_amount);
}
} // Anonymous namespace
LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
return available_language_codes.at(index);
}
template <std::size_t size>
static std::array<LanguageCode, size> MakeLanguageCodeSubset() {
std::array<LanguageCode, size> arr;
std::copy_n(available_language_codes.begin(), size, arr.begin());
return arr;
}
static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
if (available_language_codes.size() > max_size) {
rb.Push(static_cast<u32>(max_size));
} else {
rb.Push(static_cast<u32>(available_language_codes.size()));
}
}
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
if (available_language_codes.size() > pre4_0_0_max_entries) {
ctx.WriteBuffer(MakeLanguageCodeSubset<pre4_0_0_max_entries>());
} else {
ctx.WriteBuffer(available_language_codes);
}
PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
}
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -87,12 +80,7 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
if (available_language_codes.size() > post4_0_0_max_entries) {
ctx.WriteBuffer(MakeLanguageCodeSubset<post4_0_0_max_entries>());
} else {
ctx.WriteBuffer(available_language_codes);
}
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
@@ -102,9 +90,9 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
}
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
LOG_DEBUG(Service_SET, "called");
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
}
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {

View File

@@ -17,7 +17,7 @@ namespace Service::VI {
Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} {
auto& kernel = Core::System::GetInstance().Kernel();
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
fmt::format("Display VSync Event {}", id));
}

View File

@@ -556,7 +556,7 @@ private:
} else {
// Wait the current thread until a buffer becomes available
ctx.SleepClientThread(
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
"IHOSBinderDriver::DequeueBuffer", -1,
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available

View File

@@ -21,8 +21,6 @@
#include "core/memory.h"
#include "core/settings.h"
#pragma optimize("", off)
namespace Loader {
namespace {
struct MODHeader {
@@ -41,7 +39,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const std::vector<u8> uncompressed_data =
Common::Compression::DecompressDataLZ4(compressed_data, header.size);
ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size,
ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size,
uncompressed_data.size());
return uncompressed_data;

View File

@@ -72,15 +72,6 @@ u8* GetPointer(VAddr vaddr);
std::string ReadCString(VAddr vaddr, std::size_t max_length);
enum class FlushMode {
/// Write back modified surfaces to RAM
Flush,
/// Remove region from the cache
Invalidate,
/// Write back modified surfaces to RAM, and also remove them from the cache
FlushAndInvalidate,
};
/**
* Mark each page touching the region as cached.
*/

View File

@@ -102,12 +102,6 @@ bool VerifyLogin(const std::string& username, const std::string& token) {
}
TelemetrySession::TelemetrySession() {
#ifdef ENABLE_WEB_SERVICE
backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
#else
backend = std::make_unique<Telemetry::NullVisitor>();
#endif
// Log one-time top-level information
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
@@ -175,9 +169,14 @@ TelemetrySession::~TelemetrySession() {
.count()};
AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
#ifdef ENABLE_WEB_SERVICE
auto backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
#else
auto backend = std::make_unique<Telemetry::NullVisitor>();
#endif
// Complete the session, submitting to web service if necessary
// This is just a placeholder to wrap up the session once the core completes and this is
// destroyed. This will be moved elsewhere once we are actually doing real I/O with the service.
field_collection.Accept(*backend);
if (Settings::values.enable_telemetry)
backend->Complete();
@@ -186,6 +185,8 @@ TelemetrySession::~TelemetrySession() {
bool TelemetrySession::SubmitTestcase() {
#ifdef ENABLE_WEB_SERVICE
auto backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
field_collection.Accept(*backend);
return backend->SubmitTestcase();
#else

View File

@@ -39,7 +39,6 @@ public:
private:
Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
};
/**

View File

@@ -6,15 +6,8 @@
#include <memory>
#include <vector>
#include "core/frontend/input.h"
#include "input_common/main.h"
union SDL_Event;
namespace Common {
class ParamPackage;
} // namespace Common
namespace InputCommon::Polling {
class DevicePoller;
enum class DeviceType;

View File

@@ -6,7 +6,6 @@
#include <atomic>
#include <cmath>
#include <functional>
#include <iterator>
#include <mutex>
#include <string>
#include <thread>
@@ -15,7 +14,6 @@
#include <utility>
#include <vector>
#include <SDL.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/param_package.h"
@@ -23,12 +21,10 @@
#include "core/frontend/input.h"
#include "input_common/sdl/sdl_impl.h"
namespace InputCommon {
namespace SDL {
namespace InputCommon::SDL {
static std::string GetGUID(SDL_Joystick* joystick) {
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
char guid_str[33];
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
return guid_str;
@@ -37,26 +33,27 @@ static std::string GetGUID(SDL_Joystick* joystick) {
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
static int SDLEventWatcher(void* userdata, SDL_Event* event) {
SDLState* sdl_state = reinterpret_cast<SDLState*>(userdata);
static int SDLEventWatcher(void* user_data, SDL_Event* event) {
auto* const sdl_state = static_cast<SDLState*>(user_data);
// Don't handle the event if we are configuring
if (sdl_state->polling) {
sdl_state->event_queue.Push(*event);
} else {
sdl_state->HandleGameControllerEvent(*event);
}
return 0;
}
class SDLJoystick {
public:
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {}
void SetButton(int button, bool value) {
std::lock_guard lock{mutex};
state.buttons[button] = value;
state.buttons.insert_or_assign(button, value);
}
bool GetButton(int button) const {
@@ -66,7 +63,7 @@ public:
void SetAxis(int axis, Sint16 value) {
std::lock_guard lock{mutex};
state.axes[axis] = value;
state.axes.insert_or_assign(axis, value);
}
float GetAxis(int axis) const {
@@ -93,7 +90,7 @@ public:
void SetHat(int hat, Uint8 direction) {
std::lock_guard lock{mutex};
state.hats[hat] = direction;
state.hats.insert_or_assign(hat, direction);
}
bool GetHatDirection(int hat, Uint8 direction) const {
@@ -118,10 +115,8 @@ public:
return sdl_joystick.get();
}
void SetSDLJoystick(SDL_Joystick* joystick,
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
sdl_joystick =
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
void SetSDLJoystick(SDL_Joystick* joystick) {
sdl_joystick.reset(joystick);
}
private:
@@ -136,59 +131,57 @@ private:
mutable std::mutex mutex;
};
/**
* Get the nth joystick with the corresponding GUID
*/
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() <= port) {
auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
[](SDL_Joystick*) {});
while (it->second.size() <= static_cast<std::size_t>(port)) {
auto joystick =
std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
it->second.emplace_back(std::move(joystick));
}
return it->second[port];
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
return joystick_map[guid].emplace_back(std::move(joystick));
}
/**
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
* it to a SDLJoystick with the same guid and that port
*/
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
auto map_it = joystick_map.find(guid);
const auto map_it = joystick_map.find(guid);
if (map_it != joystick_map.end()) {
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();
});
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...
auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) {
return !joystick->GetSDLJoystick();
});
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);
return *nullptr_it;
}
// There is no SDLJoystick without a mapped SDL_Joystick
// Create a new SDLJoystick
auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
const int port = static_cast<int>(map_it->second.size());
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
return map_it->second.emplace_back(std::move(joystick));
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
return joystick_map[guid].emplace_back(std::move(joystick));
}
@@ -215,17 +208,19 @@ void SDLState::InitJoystick(int joystick_index) {
(*it)->SetSDLJoystick(sdl_joystick);
return;
}
auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
const int port = static_cast<int>(joystick_guid_list.size());
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
joystick_guid_list.emplace_back(std::move(joystick));
}
void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
std::string guid = GetGUID(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
auto& joystick_guid_list = joystick_map[guid];
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) {
@@ -233,9 +228,10 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
});
joystick = *joystick_it;
}
// Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback
// which locks the mutex again
joystick->SetSDLJoystick(nullptr, [](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);
}
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -317,9 +313,10 @@ public:
trigger_if_greater(trigger_if_greater_) {}
bool GetStatus() const override {
float axis_value = joystick->GetAxis(axis);
if (trigger_if_greater)
const float axis_value = joystick->GetAxis(axis);
if (trigger_if_greater) {
return axis_value > threshold;
}
return axis_value < threshold;
}
@@ -444,7 +441,7 @@ public:
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);
float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
@@ -470,7 +467,7 @@ SDLState::SDLState() {
return;
}
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
LOG_ERROR(Input, "Failed to set Hint for background events", SDL_GetError());
LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
}
SDL_AddEventWatch(&SDLEventWatcher, this);
@@ -507,12 +504,12 @@ SDLState::~SDLState() {
}
}
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
Common::ParamPackage params({{"engine", "sdl"}});
switch (event.type) {
case SDL_JOYAXISMOTION: {
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis", event.jaxis.axis);
@@ -526,14 +523,14 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
break;
}
case SDL_JOYBUTTONUP: {
auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("button", event.jbutton.button);
break;
}
case SDL_JOYHATMOTION: {
auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("hat", event.jhat.hat);
@@ -607,8 +604,8 @@ public:
SDLPoller::Start();
// Reset stored axes
analog_xaxis = -1;
analog_yaxis = -1;
analog_x_axis = -1;
analog_y_axis = -1;
analog_axes_joystick = -1;
}
@@ -620,25 +617,25 @@ public:
}
// An analog device needs two axes, so we need to store the axis for later and wait for
// a second SDL event. The axes also must be from the same joystick.
int axis = event.jaxis.axis;
if (analog_xaxis == -1) {
analog_xaxis = axis;
const int axis = event.jaxis.axis;
if (analog_x_axis == -1) {
analog_x_axis = axis;
analog_axes_joystick = event.jaxis.which;
} else if (analog_yaxis == -1 && analog_xaxis != axis &&
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
analog_axes_joystick == event.jaxis.which) {
analog_yaxis = axis;
analog_y_axis = axis;
}
}
Common::ParamPackage params;
if (analog_xaxis != -1 && analog_yaxis != -1) {
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
if (analog_x_axis != -1 && analog_y_axis != -1) {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("engine", "sdl");
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis_x", analog_xaxis);
params.Set("axis_y", analog_yaxis);
analog_xaxis = -1;
analog_yaxis = -1;
params.Set("axis_x", analog_x_axis);
params.Set("axis_y", analog_y_axis);
analog_x_axis = -1;
analog_y_axis = -1;
analog_axes_joystick = -1;
return params;
}
@@ -646,8 +643,8 @@ public:
}
private:
int analog_xaxis = -1;
int analog_yaxis = -1;
int analog_x_axis = -1;
int analog_y_axis = -1;
SDL_JoystickID analog_axes_joystick = -1;
};
} // namespace Polling
@@ -667,5 +664,4 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
return pollers;
}
} // namespace SDL
} // namespace InputCommon
} // namespace InputCommon::SDL

View File

@@ -6,7 +6,10 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
#include "input_common/sdl/sdl.h"
@@ -16,9 +19,9 @@ using SDL_JoystickID = s32;
namespace InputCommon::SDL {
class SDLJoystick;
class SDLButtonFactory;
class SDLAnalogFactory;
class SDLButtonFactory;
class SDLJoystick;
class SDLState : public State {
public:
@@ -31,7 +34,13 @@ public:
/// Handle SDL_Events for joysticks from SDL_PollEvent
void HandleGameControllerEvent(const SDL_Event& event);
/// Get the nth joystick with the corresponding GUID
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
/**
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
* tie it to a SDLJoystick with the same guid and that port
*/
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
/// Get all DevicePoller that use the SDL backend for a specific device type

View File

@@ -3,6 +3,8 @@ add_library(video_core STATIC
dma_pusher.h
debug_utils/debug_utils.cpp
debug_utils/debug_utils.h
engines/engine_upload.cpp
engines/engine_upload.h
engines/fermi_2d.cpp
engines/fermi_2d.h
engines/kepler_compute.cpp

View File

@@ -40,6 +40,13 @@ bool DmaPusher::Step() {
}
const CommandList& command_list{dma_pushbuffer.front()};
ASSERT_OR_EXECUTE(!command_list.empty(), {
// Somehow the command_list is empty, in order to avoid a crash
// We ignore it and assume its size is 0.
dma_pushbuffer.pop();
dma_pushbuffer_subindex = 0;
return true;
});
const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
GPUVAddr dma_get = command_list_header.addr;
GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
@@ -105,6 +112,8 @@ bool DmaPusher::Step() {
dma_state.non_incrementing = false;
dma_increment_once = true;
break;
default:
break;
}
}
}

View File

@@ -0,0 +1,52 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "video_core/engines/engine_upload.h"
#include "video_core/memory_manager.h"
#include "video_core/textures/decoders.h"
namespace Tegra::Engines::Upload {
State::State(MemoryManager& memory_manager, Registers& regs)
: regs{regs}, memory_manager{memory_manager} {}
State::~State() = default;
void State::ProcessExec(const bool is_linear) {
write_offset = 0;
copy_size = regs.line_length_in * regs.line_count;
inner_buffer.resize(copy_size);
this->is_linear = is_linear;
}
void State::ProcessData(const u32 data, const bool is_last_call) {
const u32 sub_copy_size = std::min(4U, copy_size - write_offset);
std::memcpy(&inner_buffer[write_offset], &data, sub_copy_size);
write_offset += sub_copy_size;
if (!is_last_call) {
return;
}
const GPUVAddr address{regs.dest.Address()};
if (is_linear) {
memory_manager.WriteBlock(address, inner_buffer.data(), copy_size);
} else {
UNIMPLEMENTED_IF(regs.dest.z != 0);
UNIMPLEMENTED_IF(regs.dest.depth != 1);
UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 1);
UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 1);
const std::size_t dst_size = Tegra::Texture::CalculateSize(
true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 1);
tmp_buffer.resize(dst_size);
memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size);
Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, regs.dest.y,
regs.dest.BlockHeight(), copy_size, inner_buffer.data(),
tmp_buffer.data());
memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size);
}
}
} // namespace Tegra::Engines::Upload

View File

@@ -0,0 +1,73 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Tegra {
class MemoryManager;
}
namespace Tegra::Engines::Upload {
struct Registers {
u32 line_length_in;
u32 line_count;
struct {
u32 address_high;
u32 address_low;
u32 pitch;
union {
BitField<0, 4, u32> block_width;
BitField<4, 4, u32> block_height;
BitField<8, 4, u32> block_depth;
};
u32 width;
u32 height;
u32 depth;
u32 z;
u32 x;
u32 y;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
}
u32 BlockWidth() const {
return 1U << block_width.Value();
}
u32 BlockHeight() const {
return 1U << block_height.Value();
}
u32 BlockDepth() const {
return 1U << block_depth.Value();
}
} dest;
};
class State {
public:
State(MemoryManager& memory_manager, Registers& regs);
~State();
void ProcessExec(bool is_linear);
void ProcessData(u32 data, bool is_last_call);
private:
u32 write_offset = 0;
u32 copy_size = 0;
std::vector<u8> inner_buffer;
std::vector<u8> tmp_buffer;
bool is_linear = false;
Registers& regs;
MemoryManager& memory_manager;
};
} // namespace Tegra::Engines::Upload

View File

@@ -21,6 +21,12 @@ class RasterizerInterface;
namespace Tegra::Engines {
/**
* This Engine is known as G80_2D. Documentation can be found in:
* https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml
* https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_2d.xml.h
*/
#define FERMI2D_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::Fermi2D::Regs, field_name) / sizeof(u32))

View File

@@ -4,12 +4,21 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#include "video_core/textures/decoders.h"
namespace Tegra::Engines {
KeplerCompute::KeplerCompute(MemoryManager& memory_manager) : memory_manager{memory_manager} {}
KeplerCompute::KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
MemoryManager& memory_manager)
: system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, upload_state{
memory_manager,
regs.upload} {}
KeplerCompute::~KeplerCompute() = default;
@@ -20,14 +29,34 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
regs.reg_array[method_call.method] = method_call.argument;
switch (method_call.method) {
case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
upload_state.ProcessExec(regs.exec_upload.linear != 0);
break;
}
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
const bool is_last_call = method_call.IsLastCall();
upload_state.ProcessData(method_call.argument, is_last_call);
if (is_last_call) {
system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
}
break;
}
case KEPLER_COMPUTE_REG_INDEX(launch):
// Abort execution since compute shaders can be used to alter game memory (e.g. CUDA
// kernels)
UNREACHABLE_MSG("Compute shaders are not implemented");
ProcessLaunch();
break;
default:
break;
}
}
void KeplerCompute::ProcessLaunch() {
const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address();
memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description,
LaunchParams::NUM_LAUNCH_PARAMETERS * sizeof(u32));
const GPUVAddr code_loc = regs.code_loc.Address() + launch_description.program_start;
LOG_WARNING(HW_GPU, "Compute Kernel Execute at Address 0x{:016x}, STUBBED", code_loc);
}
} // namespace Tegra::Engines

View File

@@ -6,22 +6,40 @@
#include <array>
#include <cstddef>
#include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/engines/engine_upload.h"
#include "video_core/gpu.h"
namespace Core {
class System;
}
namespace Tegra {
class MemoryManager;
}
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
/**
* This Engine is known as GK104_Compute. Documentation can be found in:
* https://github.com/envytools/envytools/blob/master/rnndb/graph/gk104_compute.xml
* https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nve4_compute.xml.h
*/
#define KEPLER_COMPUTE_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32))
class KeplerCompute final {
public:
explicit KeplerCompute(MemoryManager& memory_manager);
explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
MemoryManager& memory_manager);
~KeplerCompute();
static constexpr std::size_t NumConstBuffers = 8;
@@ -31,30 +49,181 @@ public:
union {
struct {
INSERT_PADDING_WORDS(0xAF);
INSERT_PADDING_WORDS(0x60);
Upload::Registers upload;
struct {
union {
BitField<0, 1, u32> linear;
};
} exec_upload;
u32 data_upload;
INSERT_PADDING_WORDS(0x3F);
struct {
u32 address;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address) << 8));
}
} launch_desc_loc;
INSERT_PADDING_WORDS(0x1);
u32 launch;
INSERT_PADDING_WORDS(0xC48);
INSERT_PADDING_WORDS(0x4A7);
struct {
u32 address_high;
u32 address_low;
u32 limit;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} tsc;
INSERT_PADDING_WORDS(0x3);
struct {
u32 address_high;
u32 address_low;
u32 limit;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} tic;
INSERT_PADDING_WORDS(0x22);
struct {
u32 address_high;
u32 address_low;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} code_loc;
INSERT_PADDING_WORDS(0x3FE);
u32 texture_const_buffer_index;
INSERT_PADDING_WORDS(0x374);
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
struct LaunchParams {
static constexpr std::size_t NUM_LAUNCH_PARAMETERS = 0x40;
INSERT_PADDING_WORDS(0x8);
u32 program_start;
INSERT_PADDING_WORDS(0x2);
BitField<30, 1, u32> linked_tsc;
BitField<0, 31, u32> grid_dim_x;
union {
BitField<0, 16, u32> grid_dim_y;
BitField<16, 16, u32> grid_dim_z;
};
INSERT_PADDING_WORDS(0x3);
BitField<0, 16, u32> shared_alloc;
BitField<0, 31, u32> block_dim_x;
union {
BitField<0, 16, u32> block_dim_y;
BitField<16, 16, u32> block_dim_z;
};
union {
BitField<0, 8, u32> const_buffer_enable_mask;
BitField<29, 2, u32> cache_layout;
} memory_config;
INSERT_PADDING_WORDS(0x8);
struct {
u32 address_low;
union {
BitField<0, 8, u32> address_high;
BitField<15, 17, u32> size;
};
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high.Value()) << 32) |
address_low);
}
} const_buffer_config[8];
union {
BitField<0, 20, u32> local_pos_alloc;
BitField<27, 5, u32> barrier_alloc;
};
union {
BitField<0, 20, u32> local_neg_alloc;
BitField<24, 5, u32> gpr_alloc;
};
INSERT_PADDING_WORDS(0x11);
} launch_description;
struct {
u32 write_offset = 0;
u32 copy_size = 0;
std::vector<u8> inner_buffer;
} state{};
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
"KeplerCompute Regs has wrong size");
static_assert(sizeof(LaunchParams) == LaunchParams::NUM_LAUNCH_PARAMETERS * sizeof(u32),
"KeplerCompute LaunchParams has wrong size");
/// Write the value to the register identified by method.
void CallMethod(const GPU::MethodCall& method_call);
private:
Core::System& system;
VideoCore::RasterizerInterface& rasterizer;
MemoryManager& memory_manager;
Upload::State upload_state;
void ProcessLaunch();
};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
#define ASSERT_LAUNCH_PARAM_POSITION(field_name, position) \
static_assert(offsetof(KeplerCompute::LaunchParams, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(upload, 0x60);
ASSERT_REG_POSITION(exec_upload, 0x6C);
ASSERT_REG_POSITION(data_upload, 0x6D);
ASSERT_REG_POSITION(launch, 0xAF);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(code_loc, 0x582);
ASSERT_REG_POSITION(texture_const_buffer_index, 0x982);
ASSERT_LAUNCH_PARAM_POSITION(program_start, 0x8);
ASSERT_LAUNCH_PARAM_POSITION(grid_dim_x, 0xC);
ASSERT_LAUNCH_PARAM_POSITION(shared_alloc, 0x11);
ASSERT_LAUNCH_PARAM_POSITION(block_dim_x, 0x12);
ASSERT_LAUNCH_PARAM_POSITION(memory_config, 0x14);
ASSERT_LAUNCH_PARAM_POSITION(const_buffer_config, 0x1D);
#undef ASSERT_REG_POSITION

View File

@@ -14,9 +14,8 @@
namespace Tegra::Engines {
KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
MemoryManager& memory_manager)
: system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {}
KeplerMemory::KeplerMemory(Core::System& system, MemoryManager& memory_manager)
: system{system}, memory_manager{memory_manager}, upload_state{memory_manager, regs.upload} {}
KeplerMemory::~KeplerMemory() = default;
@@ -28,46 +27,18 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
switch (method_call.method) {
case KEPLERMEMORY_REG_INDEX(exec): {
ProcessExec();
upload_state.ProcessExec(regs.exec.linear != 0);
break;
}
case KEPLERMEMORY_REG_INDEX(data): {
ProcessData(method_call.argument, method_call.IsLastCall());
const bool is_last_call = method_call.IsLastCall();
upload_state.ProcessData(method_call.argument, is_last_call);
if (is_last_call) {
system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
}
break;
}
}
}
void KeplerMemory::ProcessExec() {
state.write_offset = 0;
state.copy_size = regs.line_length_in * regs.line_count;
state.inner_buffer.resize(state.copy_size);
}
void KeplerMemory::ProcessData(u32 data, bool is_last_call) {
const u32 sub_copy_size = std::min(4U, state.copy_size - state.write_offset);
std::memcpy(&state.inner_buffer[state.write_offset], &regs.data, sub_copy_size);
state.write_offset += sub_copy_size;
if (is_last_call) {
const GPUVAddr address{regs.dest.Address()};
if (regs.exec.linear != 0) {
memory_manager.WriteBlock(address, state.inner_buffer.data(), state.copy_size);
} else {
UNIMPLEMENTED_IF(regs.dest.z != 0);
UNIMPLEMENTED_IF(regs.dest.depth != 1);
UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 1);
UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 1);
const std::size_t dst_size = Tegra::Texture::CalculateSize(
true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 1);
std::vector<u8> tmp_buffer(dst_size);
memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size);
Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x,
regs.dest.y, regs.dest.BlockHeight(), state.copy_size,
state.inner_buffer.data(), tmp_buffer.data());
memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size);
}
system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
}
}
} // namespace Tegra::Engines

View File

@@ -10,6 +10,7 @@
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/engines/engine_upload.h"
#include "video_core/gpu.h"
namespace Core {
@@ -20,19 +21,20 @@ namespace Tegra {
class MemoryManager;
}
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
/**
* This Engine is known as P2MF. Documentation can be found in:
* https://github.com/envytools/envytools/blob/master/rnndb/graph/gk104_p2mf.xml
* https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nve4_p2mf.xml.h
*/
#define KEPLERMEMORY_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32))
class KeplerMemory final {
public:
KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
MemoryManager& memory_manager);
KeplerMemory(Core::System& system, MemoryManager& memory_manager);
~KeplerMemory();
/// Write the value to the register identified by method.
@@ -45,42 +47,7 @@ public:
struct {
INSERT_PADDING_WORDS(0x60);
u32 line_length_in;
u32 line_count;
struct {
u32 address_high;
u32 address_low;
u32 pitch;
union {
BitField<0, 4, u32> block_width;
BitField<4, 4, u32> block_height;
BitField<8, 4, u32> block_depth;
};
u32 width;
u32 height;
u32 depth;
u32 z;
u32 x;
u32 y;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
u32 BlockWidth() const {
return 1U << block_width.Value();
}
u32 BlockHeight() const {
return 1U << block_height.Value();
}
u32 BlockDepth() const {
return 1U << block_depth.Value();
}
} dest;
Upload::Registers upload;
struct {
union {
@@ -96,28 +63,17 @@ public:
};
} regs{};
struct {
u32 write_offset = 0;
u32 copy_size = 0;
std::vector<u8> inner_buffer;
} state{};
private:
Core::System& system;
VideoCore::RasterizerInterface& rasterizer;
MemoryManager& memory_manager;
void ProcessExec();
void ProcessData(u32 data, bool is_last_call);
Upload::State upload_state;
};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(line_length_in, 0x60);
ASSERT_REG_POSITION(line_count, 0x61);
ASSERT_REG_POSITION(dest, 0x62);
ASSERT_REG_POSITION(upload, 0x60);
ASSERT_REG_POSITION(exec, 0x6C);
ASSERT_REG_POSITION(data, 0x6D);
#undef ASSERT_REG_POSITION

View File

@@ -20,8 +20,8 @@ constexpr u32 MacroRegistersStart = 0xE00;
Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
MemoryManager& memory_manager)
: system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, macro_interpreter{
*this} {
: system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
macro_interpreter{*this}, upload_state{memory_manager, regs.upload} {
InitializeRegisterDefaults();
}
@@ -34,9 +34,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
// Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
// needed for ARMS.
for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
regs.viewports[viewport].depth_range_near = 0.0f;
regs.viewports[viewport].depth_range_far = 1.0f;
for (auto& viewport : regs.viewports) {
viewport.depth_range_near = 0.0f;
viewport.depth_range_far = 1.0f;
}
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
@@ -47,13 +47,13 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.blend.equation_a = Regs::Blend::Equation::Add;
regs.blend.factor_source_a = Regs::Blend::Factor::One;
regs.blend.factor_dest_a = Regs::Blend::Factor::Zero;
for (std::size_t blend_index = 0; blend_index < Regs::NumRenderTargets; blend_index++) {
regs.independent_blend[blend_index].equation_rgb = Regs::Blend::Equation::Add;
regs.independent_blend[blend_index].factor_source_rgb = Regs::Blend::Factor::One;
regs.independent_blend[blend_index].factor_dest_rgb = Regs::Blend::Factor::Zero;
regs.independent_blend[blend_index].equation_a = Regs::Blend::Equation::Add;
regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
for (auto& blend : regs.independent_blend) {
blend.equation_rgb = Regs::Blend::Equation::Add;
blend.factor_source_rgb = Regs::Blend::Factor::One;
blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
blend.equation_a = Regs::Blend::Equation::Add;
blend.factor_source_a = Regs::Blend::Factor::One;
blend.factor_dest_a = Regs::Blend::Factor::Zero;
}
regs.stencil_front_op_fail = Regs::StencilOp::Keep;
regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
@@ -75,11 +75,11 @@ void Maxwell3D::InitializeRegisterDefaults() {
// TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
// default of enabled fixes rendering here.
for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) {
regs.color_mask[color_mask].R.Assign(1);
regs.color_mask[color_mask].G.Assign(1);
regs.color_mask[color_mask].B.Assign(1);
regs.color_mask[color_mask].A.Assign(1);
for (auto& color_mask : regs.color_mask) {
color_mask.R.Assign(1);
color_mask.G.Assign(1);
color_mask.B.Assign(1);
color_mask.A.Assign(1);
}
// Commercial games seem to assume this value is enabled and nouveau sets this value manually.
@@ -178,13 +178,13 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
// Vertex buffer
if (method >= MAXWELL3D_REG_INDEX(vertex_array) &&
method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) {
method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * Regs::NumVertexArrays) {
dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2);
} else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) &&
method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) {
method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * Regs::NumVertexArrays) {
dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1);
} else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) &&
method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) {
method < MAXWELL3D_REG_INDEX(instanced_arrays) + Regs::NumVertexArrays) {
dirty_flags.vertex_array.set(method - MAXWELL3D_REG_INDEX(instanced_arrays));
}
}
@@ -253,6 +253,18 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
ProcessSyncPoint();
break;
}
case MAXWELL3D_REG_INDEX(exec_upload): {
upload_state.ProcessExec(regs.exec_upload.linear != 0);
break;
}
case MAXWELL3D_REG_INDEX(data_upload): {
const bool is_last_call = method_call.IsLastCall();
upload_state.ProcessData(method_call.argument, is_last_call);
if (is_last_call) {
dirty_flags.OnMemoryWrite();
}
break;
}
default:
break;
}
@@ -430,7 +442,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
const auto a_type = tic_entry.a_type.Value();
// TODO(Subv): Different data types for separate components are not supported
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
return tic_entry;
}

View File

@@ -6,6 +6,7 @@
#include <array>
#include <bitset>
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -14,6 +15,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/engines/engine_upload.h"
#include "video_core/gpu.h"
#include "video_core/macro_interpreter.h"
#include "video_core/textures/texture.h"
@@ -32,6 +34,12 @@ class RasterizerInterface;
namespace Tegra::Engines {
/**
* This Engine is known as GF100_3D. Documentation can be found in:
* https://github.com/envytools/envytools/blob/master/rnndb/graph/gf100_3d.xml
* https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h
*/
#define MAXWELL3D_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32))
@@ -51,6 +59,7 @@ public:
static constexpr std::size_t NumCBData = 16;
static constexpr std::size_t NumVertexArrays = 32;
static constexpr std::size_t NumVertexAttributes = 32;
static constexpr std::size_t NumVaryings = 31;
static constexpr std::size_t NumTextureSamplers = 32;
static constexpr std::size_t NumClipDistances = 8;
static constexpr std::size_t MaxShaderProgram = 6;
@@ -243,9 +252,10 @@ public:
return "10_10_10_2";
case Size::Size_11_11_10:
return "11_11_10";
default:
UNREACHABLE();
return {};
}
UNREACHABLE();
return {};
}
std::string TypeString() const {
@@ -579,7 +589,18 @@ public:
u32 bind;
} macros;
INSERT_PADDING_WORDS(0x69);
INSERT_PADDING_WORDS(0x17);
Upload::Registers upload;
struct {
union {
BitField<0, 1, u32> linear;
};
} exec_upload;
u32 data_upload;
INSERT_PADDING_WORDS(0x44);
struct {
union {
@@ -1088,6 +1109,7 @@ public:
} regs{};
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
struct State {
struct ConstBufferInfo {
@@ -1175,6 +1197,8 @@ private:
/// Interpreter for the macro codes uploaded to the GPU.
MacroInterpreter macro_interpreter;
Upload::State upload_state;
/// Retrieves information about a specific TIC entry from the TIC buffer.
Texture::TICEntry GetTICEntry(u32 tic_index) const;
@@ -1218,6 +1242,9 @@ private:
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(macros, 0x45);
ASSERT_REG_POSITION(upload, 0x60);
ASSERT_REG_POSITION(exec_upload, 0x6C);
ASSERT_REG_POSITION(data_upload, 0x6D);
ASSERT_REG_POSITION(sync_info, 0xB2);
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
ASSERT_REG_POSITION(rt, 0x200);

View File

@@ -83,57 +83,66 @@ void MaxwellDMA::HandleCopy() {
ASSERT(regs.exec.enable_2d == 1);
const std::size_t copy_size = regs.x_count * regs.y_count;
auto source_ptr{memory_manager.GetPointer(source)};
auto dst_ptr{memory_manager.GetPointer(dest)};
if (!source_ptr) {
LOG_ERROR(HW_GPU, "source_ptr is invalid");
return;
}
if (!dst_ptr) {
LOG_ERROR(HW_GPU, "dst_ptr is invalid");
return;
}
const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
// TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
// copying.
rasterizer.FlushRegion(ToCacheAddr(source_ptr), src_size);
// We have to invalidate the destination region to evict any outdated surfaces from the
// cache. We do this before actually writing the new data because the destination address
// might contain a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(ToCacheAddr(dst_ptr), dst_size);
};
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
ASSERT(regs.src_params.size_z == 1);
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
const std::size_t src_size = Texture::CalculateSize(
true, src_bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y,
regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
copy_size * src_bytes_per_pixel);
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
regs.src_params.size_x, src_bytes_per_pixel, source_ptr, dst_ptr,
regs.src_params.BlockHeight(), regs.src_params.pos_x,
regs.src_params.pos_y);
regs.src_params.size_x, src_bytes_per_pixel, read_buffer.data(),
write_buffer.data(), regs.src_params.BlockHeight(),
regs.src_params.pos_x, regs.src_params.pos_y);
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
} else {
ASSERT(regs.dst_params.size_z == 1);
ASSERT(regs.src_pitch == regs.x_count);
ASSERT(regs.dst_params.BlockDepth() == 1);
const u32 src_bpp = regs.src_pitch / regs.x_count;
const u32 src_bytes_per_pixel = regs.src_pitch / regs.x_count;
FlushAndInvalidate(regs.src_pitch * regs.y_count,
regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
const std::size_t dst_size = Texture::CalculateSize(
true, src_bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y,
regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
const std::size_t dst_layer_size = Texture::CalculateSize(
true, src_bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1,
regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
const std::size_t src_size = regs.src_pitch * regs.y_count;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
src_bpp, dst_ptr, source_ptr, regs.dst_params.BlockHeight());
src_bytes_per_pixel,
write_buffer.data() + dst_layer_size * regs.dst_params.pos_z,
read_buffer.data(), regs.dst_params.BlockHeight());
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
}
}

View File

@@ -6,6 +6,7 @@
#include <array>
#include <cstddef>
#include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -25,6 +26,11 @@ class RasterizerInterface;
namespace Tegra::Engines {
/**
* This Engine is known as GK104_Copy. Documentation can be found in:
* https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml
*/
class MaxwellDMA final {
public:
explicit MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
@@ -63,6 +69,16 @@ public:
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
enum class ComponentMode : u32 {
Src0 = 0,
Src1 = 1,
Src2 = 2,
Src3 = 3,
Const0 = 4,
Const1 = 5,
Zero = 6,
};
enum class CopyMode : u32 {
None = 0,
Unk1 = 1,
@@ -128,7 +144,26 @@ public:
u32 x_count;
u32 y_count;
INSERT_PADDING_WORDS(0xBB);
INSERT_PADDING_WORDS(0xB8);
u32 const0;
u32 const1;
union {
BitField<0, 4, ComponentMode> component0;
BitField<4, 4, ComponentMode> component1;
BitField<8, 4, ComponentMode> component2;
BitField<12, 4, ComponentMode> component3;
BitField<16, 2, u32> component_size;
BitField<20, 3, u32> src_num_components;
BitField<24, 3, u32> dst_num_components;
u32 SrcBytePerPixel() const {
return src_num_components.Value() * component_size.Value();
}
u32 DstBytePerPixel() const {
return dst_num_components.Value() * component_size.Value();
}
} swizzle_config;
Parameters dst_params;
@@ -149,6 +184,9 @@ private:
MemoryManager& memory_manager;
std::vector<u8> read_buffer;
std::vector<u8> write_buffer;
/// Performs the copy from the source buffer to the destination buffer as configured in the
/// registers.
void HandleCopy();
@@ -165,6 +203,9 @@ ASSERT_REG_POSITION(src_pitch, 0x104);
ASSERT_REG_POSITION(dst_pitch, 0x105);
ASSERT_REG_POSITION(x_count, 0x106);
ASSERT_REG_POSITION(y_count, 0x107);
ASSERT_REG_POSITION(const0, 0x1C0);
ASSERT_REG_POSITION(const1, 0x1C1);
ASSERT_REG_POSITION(swizzle_config, 0x1C2);
ASSERT_REG_POSITION(dst_params, 0x1C3);
ASSERT_REG_POSITION(src_params, 0x1CA);

View File

@@ -98,6 +98,10 @@ union Attribute {
BitField<22, 2, u64> element;
BitField<24, 6, Index> index;
BitField<47, 3, AttributeSize> size;
bool IsPhysical() const {
return element == 0 && static_cast<u64>(index.Value()) == 0;
}
} fmt20;
union {
@@ -499,6 +503,11 @@ enum class SystemVariable : u64 {
CircularQueueEntryAddressHigh = 0x63,
};
enum class PhysicalAttributeDirection : u64 {
Input = 0,
Output = 1,
};
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -520,6 +529,11 @@ union Instruction {
BitField<39, 8, Register> gpr39;
BitField<48, 16, u64> opcode;
union {
BitField<8, 8, Register> gpr;
BitField<20, 24, s64> offset;
} gmem;
union {
BitField<20, 16, u64> imm20_16;
BitField<20, 19, u64> imm20_19;
@@ -587,6 +601,7 @@ union Instruction {
} alu;
union {
BitField<38, 1, u64> idx;
BitField<51, 1, u64> saturate;
BitField<52, 2, IpaSampleMode> sample_mode;
BitField<54, 2, IpaInterpMode> interp_mode;
@@ -802,15 +817,24 @@ union Instruction {
union {
BitField<48, 3, UniformType> type;
BitField<46, 2, u64> cache_mode;
BitField<20, 24, s64> immediate_offset;
} ldg;
union {
BitField<48, 3, UniformType> type;
BitField<46, 2, u64> cache_mode;
BitField<20, 24, s64> immediate_offset;
} stg;
union {
BitField<32, 1, PhysicalAttributeDirection> direction;
BitField<47, 3, AttributeSize> size;
BitField<20, 11, u64> address;
} al2p;
union {
BitField<53, 3, UniformType> type;
BitField<52, 1, u64> extended;
} generic;
union {
BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3;
@@ -1371,11 +1395,14 @@ public:
LD_L,
LD_S,
LD_C,
LD, // Load from generic memory
LDG, // Load from global memory
ST_A,
ST_L,
ST_S,
LDG, // Load from global memory
STG, // Store in global memory
ST, // Store in generic memory
STG, // Store in global memory
AL2P, // Transforms attribute memory into physical memory
TEX,
TEX_B, // Texture Load Bindless
TXQ, // Texture Query
@@ -1641,11 +1668,14 @@ private:
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
INST("100-------------", Id::LD, Type::Memory, "LD"),
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"),
INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
INST("101-------------", Id::ST, Type::Memory, "ST"),
INST("1110111011011---", Id::STG, Type::Memory, "STG"),
INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),

View File

@@ -35,9 +35,9 @@ GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{ren
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager);
kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager);
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(system, rasterizer, *memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
}
GPU::~GPU() = default;

View File

@@ -44,7 +44,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
renderer.Rasterizer().FlushRegion(data->addr, data->size);
} else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) {
renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
} else if (const auto data = std::get_if<EndProcessingCommand>(&next.data)) {
} else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
return;
} else {
UNREACHABLE();
@@ -118,7 +118,7 @@ void SynchState::WaitForSynchronization(u64 fence) {
// Wait for the GPU to be idle (all commands to be executed)
{
MICROPROFILE_SCOPE(GPU_wait);
std::unique_lock<std::mutex> lock{synchronization_mutex};
std::unique_lock lock{synchronization_mutex};
synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; });
}
}

View File

@@ -81,12 +81,6 @@ struct CommandDataContainer {
CommandDataContainer(CommandData&& data, u64 next_fence)
: data{std::move(data)}, fence{next_fence} {}
CommandDataContainer& operator=(const CommandDataContainer& t) {
data = std::move(t.data);
fence = t.fence;
return *this;
}
CommandData data;
u64 fence{};
};
@@ -109,7 +103,7 @@ struct SynchState final {
void TrySynchronize() {
if (IsSynchronized()) {
std::lock_guard<std::mutex> lock{synchronization_mutex};
std::lock_guard lock{synchronization_mutex};
synchronization_condition.notify_one();
}
}

View File

@@ -118,10 +118,12 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
static_cast<u32>(opcode.operation.Value()));
}
// An instruction with the Exit flag will not actually
// cause an exit if it's executed inside a delay slot.
// TODO(Blinkhawk): Reversed to always exit. The behavior explained above requires further
// testing on the MME code.
if (opcode.is_exit) {
// Exit has a delay slot, execute the next instruction
// Note: Executing an exit during a branch delay slot will cause the instruction at the
// branch target to be executed before exiting.
Step(offset, true);
return false;
}

View File

@@ -25,6 +25,8 @@ MemoryManager::MemoryManager(VideoCore::RasterizerInterface& rasterizer) : raste
UpdatePageTableForVMA(initial_vma);
}
MemoryManager::~MemoryManager() = default;
GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
const u64 aligned_size{Common::AlignUp(size, page_size)};
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
@@ -199,11 +201,11 @@ const u8* MemoryManager::GetPointer(GPUVAddr addr) const {
return {};
}
bool MemoryManager::IsBlockContinous(const GPUVAddr start, const std::size_t size) {
bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t size) const {
const GPUVAddr end = start + size;
const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start));
const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end));
const std::size_t range = static_cast<std::size_t>(host_ptr_end - host_ptr_start);
const auto range = static_cast<std::size_t>(host_ptr_end - host_ptr_start);
return range == size;
}

View File

@@ -47,7 +47,8 @@ struct VirtualMemoryArea {
class MemoryManager final {
public:
MemoryManager(VideoCore::RasterizerInterface& rasterizer);
explicit MemoryManager(VideoCore::RasterizerInterface& rasterizer);
~MemoryManager();
GPUVAddr AllocateSpace(u64 size, u64 align);
GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align);
@@ -65,18 +66,18 @@ public:
u8* GetPointer(GPUVAddr addr);
const u8* GetPointer(GPUVAddr addr) const;
// Returns true if the block is continous in host memory, false otherwise
bool IsBlockContinous(const GPUVAddr start, const std::size_t size);
/// Returns true if the block is continuous in host memory, false otherwise
bool IsBlockContinuous(GPUVAddr start, std::size_t size) const;
/**
* ReadBlock and WriteBlock are full read and write operations over virtual
* GPU Memory. It's important to use these when GPU memory may not be continous
* GPU Memory. It's important to use these when GPU memory may not be continuous
* in the Host Memory counterpart. Note: This functions cause Host GPU Memory
* Flushes and Invalidations, respectively to each operation.
*/
void ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const;
void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size);
void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size);
void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const;
void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size);
/**
* ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
@@ -88,9 +89,9 @@ public:
* WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture
* being flushed.
*/
void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const;
void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size);
void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size);
void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const;
void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size);
private:
using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>;
@@ -111,10 +112,10 @@ private:
/**
* Maps an unmanaged host memory pointer at a given address.
*
* @param target The guest address to start the mapping at.
* @param memory The memory to be mapped.
* @param size Size of the mapping.
* @param state MemoryState tag to attach to the VMA.
* @param target The guest address to start the mapping at.
* @param memory The memory to be mapped.
* @param size Size of the mapping in bytes.
* @param backing_addr The base address of the range to back this mapping.
*/
VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr);
@@ -124,7 +125,7 @@ private:
/// Converts a VMAHandle to a mutable VMAIter.
VMAIter StripIterConstness(const VMAHandle& iter);
/// Marks as the specfied VMA as allocated.
/// Marks as the specified VMA as allocated.
VMAIter Allocate(VMAIter vma);
/**

View File

@@ -37,9 +37,6 @@ public:
/// Gets the size of the shader in guest memory, required for cache management
virtual std::size_t GetSizeInBytes() const = 0;
/// Wriets any cached resources back to memory
virtual void Flush() = 0;
/// Sets whether the cached object should be considered registered
void SetIsRegistered(bool registered) {
is_registered = registered;
@@ -147,8 +144,9 @@ protected:
object->SetIsRegistered(false);
rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
const CacheAddr addr = object->GetCacheAddr();
interval_cache.subtract({GetInterval(object), ObjectSet{object}});
map_cache.erase(object->GetCacheAddr());
map_cache.erase(addr);
}
/// Returns a ticks counter used for tracking when cached objects were last modified
@@ -158,6 +156,8 @@ protected:
return ++modified_ticks;
}
virtual void FlushObjectInner(const T& object) = 0;
/// Flushes the specified object, updating appropriate cache state as needed
void FlushObject(const T& object) {
std::lock_guard lock{mutex};
@@ -165,7 +165,7 @@ protected:
if (!object->IsDirty()) {
return;
}
object->Flush();
FlushObjectInner(object);
object->MarkAsModified(false, *this);
}

View File

@@ -42,9 +42,6 @@ public:
return alignment;
}
// We do not have to flush this cache as things in it are never modified by us.
void Flush() override {}
private:
VAddr cpu_addr{};
std::size_t size{};
@@ -75,6 +72,9 @@ public:
protected:
void AlignBuffer(std::size_t alignment);
// We do not have to flush this cache as things in it are never modified by us.
void FlushObjectInner(const std::shared_ptr<CachedBufferEntry>& object) override {}
private:
OGLStreamBuffer stream_buffer;

View File

@@ -21,11 +21,21 @@ T GetInteger(GLenum pname) {
Device::Device() {
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
has_variable_aoffi = TestVariableAoffi();
}
Device::Device(std::nullptr_t) {
uniform_buffer_alignment = 0;
max_vertex_attributes = 16;
max_varyings = 15;
has_variable_aoffi = true;
}
bool Device::TestVariableAoffi() {
const GLchar* AOFFI_TEST = R"(#version 430 core
// This is a unit test, please ignore me on apitrace bug reports.
uniform sampler2D tex;
uniform ivec2 variable_offset;
void main() {

View File

@@ -5,17 +5,27 @@
#pragma once
#include <cstddef>
#include "common/common_types.h"
namespace OpenGL {
class Device {
public:
Device();
explicit Device();
explicit Device(std::nullptr_t);
std::size_t GetUniformBufferAlignment() const {
return uniform_buffer_alignment;
}
u32 GetMaxVertexAttributes() const {
return max_vertex_attributes;
}
u32 GetMaxVaryings() const {
return max_varyings;
}
bool HasVariableAoffi() const {
return has_variable_aoffi;
}
@@ -24,6 +34,8 @@ private:
static bool TestVariableAoffi();
std::size_t uniform_buffer_alignment{};
u32 max_vertex_attributes{};
u32 max_varyings{};
bool has_variable_aoffi{};
};

View File

@@ -46,7 +46,7 @@ public:
/// Reloads the global region from guest memory
void Reload(u32 size_);
void Flush() override;
void Flush();
private:
VAddr cpu_addr{};
@@ -65,6 +65,11 @@ public:
GlobalRegion GetGlobalRegion(const GLShader::GlobalMemoryEntry& descriptor,
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage);
protected:
void FlushObjectInner(const GlobalRegion& object) override {
object->Flush();
}
private:
GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const;
GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, u32 size);

View File

@@ -98,9 +98,11 @@ struct FramebufferCacheKey {
}
};
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info)
: res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system},
screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) {
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
ScreenInfo& info)
: res_cache{*this}, shader_cache{*this, system, emu_window, device},
global_cache{*this}, system{system}, screen_info{info},
buffer_cache(*this, STREAM_BUFFER_SIZE) {
OpenGLState::ApplyDefaultState();
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
@@ -261,8 +263,8 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
// MakeQuadArray always generates u32 indexes
params.index_format = GL_UNSIGNED_INT;
params.count = (regs.vertex_buffer.count / 4) * 6;
params.index_buffer_offset =
primitive_assembler.MakeQuadArray(regs.vertex_buffer.first, params.count);
params.index_buffer_offset = primitive_assembler.MakeQuadArray(
regs.vertex_buffer.first, regs.vertex_buffer.count);
}
return params;
}
@@ -305,6 +307,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
case Maxwell::ShaderProgram::Geometry:
shader_program_manager->UseTrivialGeometryShader();
break;
default:
break;
}
continue;
}
@@ -920,8 +924,8 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
viewport.y = viewport_rect.bottom;
viewport.width = viewport_rect.GetWidth();
viewport.height = viewport_rect.GetHeight();
viewport.depth_range_far = regs.viewports[i].depth_range_far;
viewport.depth_range_near = regs.viewports[i].depth_range_near;
viewport.depth_range_far = src.depth_range_far;
viewport.depth_range_near = src.depth_range_near;
}
state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0;
state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0;
@@ -1133,7 +1137,9 @@ void RasterizerOpenGL::SyncTransformFeedback() {
void RasterizerOpenGL::SyncPointState() {
const auto& regs = system.GPU().Maxwell3D().regs;
state.point.size = regs.point_size;
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
// in OpenGL).
state.point.size = std::max(1.0f, regs.point_size);
}
void RasterizerOpenGL::SyncPolygonOffset() {

View File

@@ -48,7 +48,8 @@ struct FramebufferCacheKey;
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
public:
explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info);
explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
ScreenInfo& info);
~RasterizerOpenGL() override;
void DrawArrays() override;

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