Compare commits

...

85 Commits

Author SHA1 Message Date
Ameer J
5f8d6bc504 vulkan_device: Blacklist RADV on RDNA2 from VK_EXT_vertex_input_dynamic_state
RDNA2 devices running under the RADV driver were crashing when VK_EXT_vertex_input_dynamic_state was enabled.

Blacklisting these devices until a proper fix is established.
2022-02-25 23:09:03 -05:00
Mai M
2f45e999d8 Merge pull request #7933 from german77/am_update
service: am: Update enum names to match documentation
2022-02-21 20:42:01 -05:00
Narr the Reg
d44464829b service: am: Update enum names to match documentation 2022-02-21 18:00:50 -06:00
bunnei
efe50d88ec Merge pull request #7913 from voidanix/anv-fix
vulkan_device: fix missing format in ANV
2022-02-21 14:34:27 -07:00
bunnei
21f5912ec9 Merge pull request #7919 from bunnei/phys-mem-updates
core: hle: kernel: KPageTable: Improve Un/MapPhysicalMemory.
2022-02-21 13:39:05 -07:00
bunnei
8d46c3cc66 Merge pull request #7920 from bunnei/fix-unmap-pages
core: hle: kernel: KPageTable: Fix UnmapPages.
2022-02-21 13:38:52 -07:00
voidanix
7712e46d64 vulkan_device: fix missing format in ANV
Currently Mesa's ANV driver does not support
VK_FORMAT_B5G6R5_UNORM_PACK16, implement an alternative for it.
2022-02-21 09:21:41 +01:00
bunnei
92b2e92620 fixup! core: hle: kernel: KPageTable: Improve Un/MapPhysicalMemory. 2022-02-19 00:14:27 -08:00
bunnei
2984695265 Merge pull request #7867 from german77/amiibo
nfp: Improve amiibo support
2022-02-19 00:57:47 -07:00
bunnei
c9260a75f6 core: hle: kernel: KPageTable: Fix UnmapPages.
- Fixes a logic bug in KPageTable::UnmapPages.
2022-02-18 23:48:16 -08:00
bunnei
1a16d055df core: hle: kernel: KPageTable: Improve Un/MapPhysicalMemory.
- Improves the implementations of MapPhysicalMemory and UnmapPhysicalMemory to more closely reflect latest HOS.
2022-02-18 23:42:27 -08:00
bunnei
83a84f1c2d Merge pull request #7900 from german77/enter
yuzu: config: Fix mapping issues with the enter key
2022-02-18 15:47:34 -07:00
Mai M
90a4591563 Merge pull request #7909 from Wunkolo/null-visit-ctor
common: Add NullVisitor default constructor
2022-02-18 17:44:02 -05:00
Wunkolo
768fdb269e common: Add NullVisitor default constructor
Addresses https://github.com/yuzu-emu/yuzu/issues/7881 to fix linux
builds.

`YUZU_NON_COPYABLE` deletes the `T(const T&)` constructor which will
cause the implicitly defined default ctor/dtor to no-longer generate.
2022-02-17 06:28:19 -08:00
Mai M
c48b9668f0 Merge pull request #7866 from xerpi/svc-OutputDebugString32-CreateCodeMemory32-ControlCodeMemory32
kernel: svc: Add OutputDebugString32, CreateCodeMemory32, ControlCodeMemory32
2022-02-16 22:49:56 -05:00
bunnei
027ff7847c Merge pull request #7878 from german77/mnpp
service/mnpp: Stub mnpp_app
2022-02-16 18:42:49 -07:00
Morph
4514325b9c Merge pull request #7899 from Kelebek1/test
file_sys: Dump patched exefs rather than base
2022-02-16 16:37:09 -05:00
bunnei
b5fd9c58cd Merge pull request #7877 from lat9nq/upd_rev
audio_core: Update current process revision
2022-02-15 13:08:40 -07:00
bunnei
910a0fa58e Merge pull request #7891 from Morph1984/buffer_to_string_view
common: fs_util: Add buffer to string view utility functions
2022-02-15 12:02:23 -07:00
Narr the Reg
1e21f5f872 yuzu: config: Fix mapping issues with the enter key 2022-02-15 11:08:11 -06:00
Kelebek1
e1201abc1e Dump patched exefs rather than base 2022-02-15 04:52:28 +00:00
Sergi Granell
c3242abe95 kernel: svc: Add OutputDebugString32, CreateCodeMemory32, ControlCodeMemory32
Very straightforward, they are just wrappers to the 64-bit version of
the SVC.
2022-02-15 00:45:19 +01:00
bunnei
d81cdd9114 Merge pull request #7871 from german77/svc2
svc: Set unique names for function tables
2022-02-14 16:32:54 -07:00
Mai M
45bcd7f9b8 Merge pull request #7890 from Morph1984/utf8-console
debugger: console: Set console output codepage to UTF-8
2022-02-13 19:54:05 -05:00
Morph
4390370a19 common: fs_util: Add buffer to string view utility functions
These functions allow to construct a string view from an input buffer, avoiding the copy done by the non string view counterparts. However, callers must be cognizant of the viewed buffer's lifetime to avoid a use-after-free.
2022-02-13 18:53:21 -05:00
Morph
54c7af9902 debugger: console: Set console output codepage to UTF-8
This allows the console to display multi-byte encoded characters.
2022-02-13 18:18:05 -05:00
german77
b57d61010f nfp: Allow files without password data 2022-02-13 13:52:34 -06:00
Narr the Reg
eaca010ee9 Merge pull request #7887 from lat9nq/stub-is-usb-full-key
hid: Stub IsUsbFullKeyControllerEnabled
2022-02-12 22:22:38 -06:00
lat9nq
c5d22952bf hid: Stub IsUsbFullKeyControllerEnabled
Used by Splatoon 2, when opening the inventory from a LAN battle lobby.

Reference: https://switchbrew.org/wiki/HID_services
2022-02-12 15:42:50 -05:00
lat9nq
81806603eb audio_core: Update current process revision
Update CURRENT_PROCESS_REVISION from REV9 to REVA.

Used by Nintendo Entertainment System - Nintendo Switch Online 6.0.0 and
Super Nintendo Entertainment System - Nintendo Switch Online 3.0.0.
2022-02-11 00:56:13 -05:00
bunnei
ca9da569ce Merge pull request #7852 from Morph1984/new-uuid
common: Revise and fix the UUID implementation
2022-02-10 21:52:13 -07:00
Narr the Reg
6705439cf3 service/mnpp: Stub mnpp_app
Used in Super Nintendo Entertainment System™ - Nintendo Switch Online
2022-02-10 21:55:28 -06:00
Morph
3799c820ca common: uuid: Use sizeof(u64) instead of 8 in Hash() 2022-02-10 15:03:49 -05:00
bunnei
1105614b86 Merge pull request #7861 from german77/user_features
yuzu: New hotkeys and mute audio on background
2022-02-10 10:06:58 -07:00
Narr the Reg
6a1ad03153 nfp: Separate nfc tag from amiibo data 2022-02-10 10:58:37 -06:00
bunnei
fc01074f89 Merge pull request #7860 from german77/no-more-drift
yuzu: Add auto center on right click
2022-02-09 17:52:39 -07:00
bunnei
bcd666b86b Merge pull request #7870 from yuzu-emu/fix-codememory-size
hle: kernel: KCodeMemory: Correct m_page_group number of pages.
2022-02-09 11:29:32 -07:00
Narr the Reg
d0c7c3f64f svc: Set unique names for function tables 2022-02-08 21:03:31 -06:00
bunnei
862dddf8c9 hle: kernel: KCodeMemory: Remove unused QueryMemory. 2022-02-08 18:49:41 -08:00
bunnei
d134ca68c6 hle: kernel: KCodeMemory: Correct m_page_group number of pages.
Credits to @xerpi for finding this issue and pointing it out on #7519.
2022-02-08 18:47:11 -08:00
german77
e35c2fd5d0 nfp: Address compiler issues 2022-02-08 18:52:44 -06:00
Narr the Reg
29f9a454eb nfp: Validate amiibo files 2022-02-08 14:09:30 -06:00
german77
41b65d38fa yuzu: Allow to open and remove the amiibo 2022-02-08 10:08:04 -06:00
german77
fc9abd3c62 nfp: Improve implementation 2022-02-08 10:08:04 -06:00
german77
c001a2af25 nfp: Move IUser class to header and add missing enum and structs 2022-02-07 09:18:22 -06:00
german77
3d24eb54ec nfp: Sort functions by command number 2022-02-07 09:18:22 -06:00
german77
ab93b4c66d yuzu: Mute audio when in background 2022-02-06 20:46:58 -06:00
german77
49eb78497b yuzu: Add docked, GPU accuracy and adapting filter hotkeys 2022-02-06 20:46:58 -06:00
german77
6a4ab3e0d2 yuzu: Add auto center on right click 2022-02-06 19:56:03 -06:00
Morph
b720009dc0 Merge pull request #7847 from tech-ticks/master
service: pm: Implement AtmosphereGetProcessInfo
2022-02-05 19:22:45 -05:00
Morph
36910e9020 Merge pull request #7851 from lat9nq/cmd-add-motion
yuzu-cmd: config: Support motion inputs
2022-02-05 19:22:18 -05:00
bunnei
0456ed6b4e Merge pull request #7849 from Morph1984/qt-frameless-window
main: Always remove the frameless window flag when restoring UI state
2022-02-05 15:18:48 -07:00
Morph
ec4d7f71fe common: uuid: Return an invalid UUID if conversion from string fails
The string constructor of UUID states:
Should the input string not meet the above requirements, an assert will be triggered and an invalid UUID is set instead.
2022-02-05 13:56:21 -05:00
Morph
25db62ce15 general: Rename NewUUID to UUID, and remove the previous UUID impl
This completes the removal of the old UUID implementation.
2022-02-05 13:56:21 -05:00
Morph
dfe11d72e3 profile: Migrate to the new UUID implementation 2022-02-05 13:56:21 -05:00
Morph
d94dcaefa0 common: uuid: Add AsU128()
This copies the internal bytes of the UUID into a u128 for backwards compatibility. This should not be used.
2022-02-05 13:56:21 -05:00
Morph
f0340b8d22 hle: ipc_helpers: Ignore -Wclass-memaccess
This warning is triggered by GCC when copying into non-trivially default constructible types, as it uses the more restrictive std::is_trivial (which includes std::is_trivially_default_constructible) to determine whether memcpy is safe instead of std::is_trivially_copyable.
2022-02-05 13:56:20 -05:00
Morph
ee0547e4c4 service: Migrate to the new UUID implementation 2022-02-05 13:18:46 -05:00
Morph
cb30fe50cd input/hid: Migrate to the new UUID implementation 2022-02-05 13:18:41 -05:00
Morph
3271099fea common: Implement NewUUID
This is a fixed and revised implementation of UUID that uses an array of bytes as its internal representation of a UUID instead of a u128 (which was an array of 2 u64s).
In addition to this, the generation of RFC 4122 Version 4 compliant UUIDs is also implemented.
2022-02-05 13:18:31 -05:00
bunnei
5cb1c2ad84 Merge pull request #7842 from german77/vibration_test
yuzu: config: Vibrate the controller while configuring vibration strength
2022-02-05 02:57:26 -07:00
lat9nq
928380ebf9 config: Support motion inputs
Motion inputs were not being read in by the config when yuzu-cmd boots
up. This adds support for those.

While we're at it, make a reference to the current player controls to
improve readability. Also updates the if statements in the Analog and
Button loops with curly braces to keep the style consistent.
2022-02-05 02:31:55 -05:00
bunnei
0ec5b9bff2 Merge pull request #7839 from german77/battery
yuzu: ui: Improve battery symbols
2022-02-04 18:23:35 -07:00
Mai M
7ad17ae397 Merge pull request #7848 from Morph1984/unused-core-include
input_common: Remove unused core include
2022-02-03 23:21:32 -05:00
Morph
edbfbf2f2f main: Always remove the frameless window flag when restoring UI state
For unknown reasons, this flag may persist after the application has been closed.
Removing this flag when restoring the UI state ensures that a frameless window will not be shown on startup.
2022-02-03 23:12:49 -05:00
Morph
fbefcf7280 input_common: Remove unused core include 2022-02-03 22:44:28 -05:00
tech-ticks
16bf50e610 service: pm: Implement AtmosphereGetProcessInfo 2022-02-04 01:41:36 +01:00
bunnei
fc8aef7323 Merge pull request #7811 from german77/analog-mod
input_common: Use attributes for analog range modifiers
2022-02-03 12:21:11 -07:00
bunnei
8ec32e9755 Merge pull request #7814 from FernandoS27/another-bug-in-my-schedule
Vulkan: Fix Scheduler Chunks when their FuncType is 0.
2022-02-02 19:15:06 -07:00
bunnei
03186af6a1 Merge pull request #7835 from bunnei/page-table-lock
hle: kernel: KPageTable: Migrate locks to KScopedLightLock.
2022-02-02 17:58:55 -07:00
Morph
d68eb751c5 Merge pull request #7838 from lioncash/noncopy
common_types: Remove NonCopyable struct
2022-02-02 16:04:26 -05:00
Narr the Reg
694c078655 yuzu: config: Vibrate the controller while configuring vibration strength 2022-02-02 14:54:24 -06:00
bunnei
9484519ec3 Merge pull request #7834 from german77/repeat
yuzu: Disable auto repeat on hotkeys again
2022-02-02 13:20:18 -07:00
Narr the Reg
064aa3de11 yuzu: ui: Improve battery symbols 2022-02-02 13:18:06 -06:00
Lioncash
ade44ac2ea common_types: Remove NonCopyable struct
Now that we're moved over to the YUZU_ defines, we can get rid of this
struct.
2022-02-02 13:19:01 -05:00
Lioncash
f785f73e92 general: Replace NonCopyable struct with equivalents 2022-02-02 13:17:12 -05:00
bunnei
09400e4f4e Merge pull request #7806 from ameerj/atomic64-fallbacks
shaders: Implement U32x2 atomic fallbacks when device does not support int64
2022-02-02 10:57:22 -07:00
Lioncash
76d83ffbec general: Move deleted copy/move constructor/assignment operators to public interface
This allows for better compiler errors, where the compiler will state a
copy or move couldn't occur due to the relevant function being deleted.

Previously a compiler would warn about the relevant function not being
accessible (which, while true, isn't as informative as it could be).
2022-02-02 12:17:08 -05:00
bunnei
995e27e9b7 hle: kernel: KPageTable: Migrate locks to KScopedLightLock.
- More accurately reflects real kernel behavior by using guest locks.
2022-02-01 19:34:24 -08:00
Narr the Reg
330b31ae2e yuzu: Disable auto repeat on hotkeys again 2022-02-01 21:00:37 -06:00
Fernando Sahmkow
35ed051742 Vulkan: Fix Scheduler Chunks when their FuncType is 0. 2022-01-31 17:08:08 +01:00
german77
5bd0c1517f input_common: Use attributes for analog range modifiers 2022-01-30 22:42:40 -06:00
ameerj
e394e1ecc4 emit_glsl_atomic: Implement 32x2 fallback atomic ops 2022-01-29 19:56:03 -05:00
ameerj
90a0506d56 lower_int64_to_int32: Add 64-bit atomic fallbacks 2022-01-29 19:56:02 -05:00
ameerj
ad58d7eae7 shaders: Add U64->U32x2 Atomic fallback functions 2022-01-29 19:55:53 -05:00
112 changed files with 3489 additions and 1090 deletions

View File

@@ -15,7 +15,9 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
} // namespace Audren
constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9');
constexpr u8 BASE_REVISION = '0';
constexpr u32_le CURRENT_PROCESS_REVISION =
Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA));
constexpr std::size_t MAX_MIX_BUFFERS = 24;
constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
constexpr std::size_t MAX_CHANNEL_COUNT = 6;

View File

@@ -46,13 +46,3 @@ using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space
using u128 = std::array<std::uint64_t, 2>;
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
// An inheritable class to disallow the copy constructor and operator= functions
class NonCopyable {
protected:
constexpr NonCopyable() = default;
~NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};

View File

@@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) {
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
}
std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
}
std::string ToUTF8String(std::u8string_view u8_string) {
return std::string{u8_string.begin(), u8_string.end()};
}
@@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) {
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
}
std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
return std::string_view{reinterpret_cast<const char*>(buffer.data())};
}
std::string PathToUTF8String(const std::filesystem::path& path) {
return ToUTF8String(path.u8string());
}

View File

@@ -37,6 +37,15 @@ concept IsChar = std::same_as<T, char>;
*/
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
/**
* Same as BufferToU8String, but returns a string view of the buffer.
*
* @param buffer Buffer of bytes
*
* @returns UTF-8 encoded std::u8string_view.
*/
[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
/**
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
*
@@ -57,6 +66,15 @@ concept IsChar = std::same_as<T, char>;
*/
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
/**
* Same as BufferToUTF8String, but returns a string view of the buffer.
*
* @param buffer Buffer of bytes
*
* @returns UTF-8 encoded std::string_view.
*/
[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
/**
* Converts a filesystem path to a UTF-8 encoded std::string.
*

View File

@@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, Migration) \
SUB(Service, Mii) \
SUB(Service, MM) \
SUB(Service, MNPP) \
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \

View File

@@ -76,6 +76,7 @@ enum class Class : u8 {
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
Service_MM, ///< The MM (Multimedia) service
Service_MNPP, ///< The MNPP service
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service

View File

@@ -10,11 +10,65 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default;
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
u64 address) const {
// Setup invalid defaults.
out_entry.phys_addr = 0;
out_entry.block_size = page_size;
out_context.next_page = 0;
// Validate that we can read the actual entry.
const auto page = address / page_size;
if (page >= backing_addr.size()) {
return false;
}
// Validate that the entry is mapped.
const auto phys_addr = backing_addr[page];
if (phys_addr == 0) {
return false;
}
// Populate the results.
out_entry.phys_addr = phys_addr + address;
out_context.next_page = page + 1;
out_context.next_offset = address + page_size;
return true;
}
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
// Setup invalid defaults.
out_entry.phys_addr = 0;
out_entry.block_size = page_size;
// Validate that we can read the actual entry.
const auto page = context.next_page;
if (page >= backing_addr.size()) {
return false;
}
// Validate that the entry is mapped.
const auto phys_addr = backing_addr[page];
if (phys_addr == 0) {
return false;
}
// Populate the results.
out_entry.phys_addr = phys_addr + context.next_offset;
context.next_page = page + 1;
context.next_offset += page_size;
return true;
}
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
const std::size_t num_page_table_entries{1ULL
<< (address_space_width_in_bits - page_size_in_bits)};
pointers.resize(num_page_table_entries);
backing_addr.resize(num_page_table_entries);
current_address_space_width_in_bits = address_space_width_in_bits;
page_size = 1ULL << page_size_in_bits;
}
} // namespace Common

View File

@@ -27,6 +27,16 @@ enum class PageType : u8 {
* mimics the way a real CPU page table works.
*/
struct PageTable {
struct TraversalEntry {
u64 phys_addr{};
std::size_t block_size{};
};
struct TraversalContext {
u64 next_page{};
u64 next_offset{};
};
/// Number of bits reserved for attribute tagging.
/// This can be at most the guaranteed alignment of the pointers in the page table.
static constexpr int ATTRIBUTE_BITS = 2;
@@ -89,6 +99,10 @@ struct PageTable {
PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default;
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
u64 address) const;
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
/**
* Resizes the page table to be able to accommodate enough pages within
* a given address space.
@@ -96,9 +110,9 @@ struct PageTable {
* @param address_space_width_in_bits The address size width in bits.
* @param page_size_in_bits The page size in bits.
*/
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
size_t GetAddressSpaceBits() const {
std::size_t GetAddressSpaceBits() const {
return current_address_space_width_in_bits;
}
@@ -110,9 +124,11 @@ struct PageTable {
VirtualBuffer<u64> backing_addr;
size_t current_address_space_width_in_bits;
std::size_t current_address_space_width_in_bits{};
u8* fastmem_arena;
u8* fastmem_arena{};
std::size_t page_size{};
};
} // namespace Common

View File

@@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Common::Telemetry {
@@ -28,7 +29,7 @@ struct VisitorInterface;
/**
* Interface class for telemetry data fields.
*/
class FieldInterface : NonCopyable {
class FieldInterface {
public:
virtual ~FieldInterface() = default;
@@ -52,14 +53,15 @@ public:
template <typename T>
class Field : public FieldInterface {
public:
YUZU_NON_COPYABLE(Field);
Field(FieldType type_, std::string name_, T value_)
: name(std::move(name_)), type(type_), value(std::move(value_)) {}
Field(const Field&) = default;
Field& operator=(const Field&) = default;
~Field() override = default;
Field(Field&&) = default;
Field& operator=(Field&& other) = default;
Field(Field&&) noexcept = default;
Field& operator=(Field&& other) noexcept = default;
void Accept(VisitorInterface& visitor) const override;
@@ -98,9 +100,15 @@ private:
/**
* Collection of data fields that have been logged.
*/
class FieldCollection final : NonCopyable {
class FieldCollection final {
public:
YUZU_NON_COPYABLE(FieldCollection);
FieldCollection() = default;
~FieldCollection() = default;
FieldCollection(FieldCollection&&) noexcept = default;
FieldCollection& operator=(FieldCollection&&) noexcept = default;
/**
* Accept method for the visitor pattern, visits each field in the collection.
@@ -133,7 +141,7 @@ private:
* Telemetry fields visitor interface class. A backend to log to a web service should implement
* this interface.
*/
struct VisitorInterface : NonCopyable {
struct VisitorInterface {
virtual ~VisitorInterface() = default;
virtual void Visit(const Field<bool>& field) = 0;
@@ -160,8 +168,11 @@ struct VisitorInterface : NonCopyable {
* Empty implementation of VisitorInterface that drops all fields. Used when a functional
* backend implementation is not available.
*/
struct NullVisitor : public VisitorInterface {
~NullVisitor() = default;
struct NullVisitor final : public VisitorInterface {
YUZU_NON_COPYABLE(NullVisitor);
NullVisitor() = default;
~NullVisitor() override = default;
void Visit(const Field<bool>& /*field*/) override {}
void Visit(const Field<double>& /*field*/) override {}

View File

@@ -1,23 +1,25 @@
// Copyright 2018 yuzu Emulator Project
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <bit>
#include <optional>
#include <random>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/tiny_mt.h"
#include "common/uuid.h"
namespace Common {
namespace {
bool IsHexDigit(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
constexpr size_t RawStringSize = sizeof(UUID) * 2;
constexpr size_t FormattedStringSize = RawStringSize + 4;
u8 HexCharToByte(char c) {
std::optional<u8> HexCharToByte(char c) {
if (c >= '0' && c <= '9') {
return static_cast<u8>(c - '0');
}
@@ -28,60 +30,184 @@ u8 HexCharToByte(char c) {
return static_cast<u8>(c - 'A' + 10);
}
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
return u8{0};
return std::nullopt;
}
std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
std::array<u8, 0x10> uuid;
for (size_t i = 0; i < RawStringSize; i += 2) {
const auto upper = HexCharToByte(raw_string[i]);
const auto lower = HexCharToByte(raw_string[i + 1]);
if (!upper || !lower) {
return {};
}
uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower);
}
return uuid;
}
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
std::array<u8, 0x10> uuid;
size_t i = 0;
// Process the first 8 characters.
const auto* str = formatted_string.data();
for (; i < 4; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the next 4 characters.
++str;
for (; i < 6; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the next 4 characters.
++str;
for (; i < 8; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the next 4 characters.
++str;
for (; i < 10; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the last 12 characters.
++str;
for (; i < 16; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
return uuid;
}
std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) {
const auto length = uuid_string.length();
if (length == 0) {
return {};
}
// Check if the input string contains 32 hexadecimal characters.
if (length == RawStringSize) {
return ConstructFromRawString(uuid_string);
}
// Check if the input string has the length of a RFC 4122 formatted UUID string.
if (length == FormattedStringSize) {
return ConstructFromFormattedString(uuid_string);
}
ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length);
return {};
}
} // Anonymous namespace
u128 HexStringToU128(std::string_view hex_string) {
const size_t length = hex_string.length();
UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {}
// Detect "0x" prefix.
const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
const size_t offset = has_0x_prefix ? 2 : 0;
// Check length.
if (length > 32 + offset) {
ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
return INVALID_UUID;
}
u64 lo = 0;
u64 hi = 0;
for (size_t i = 0; i < length - offset; ++i) {
const char c = hex_string[length - 1 - i];
if (!IsHexDigit(c)) {
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
return INVALID_UUID;
}
if (i < 16) {
lo |= u64{HexCharToByte(c)} << (i * 4);
}
if (i >= 16) {
hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
}
}
return u128{lo, hi};
std::string UUID::RawString() const {
return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}"
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
uuid[15]);
}
UUID UUID::Generate() {
std::string UUID::FormattedString() const {
return fmt::format("{:02x}{:02x}{:02x}{:02x}"
"-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-"
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
uuid[15]);
}
size_t UUID::Hash() const noexcept {
u64 upper_hash;
u64 lower_hash;
std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
return upper_hash ^ std::rotl(lower_hash, 1);
}
u128 UUID::AsU128() const {
u128 uuid_old;
std::memcpy(&uuid_old, uuid.data(), sizeof(UUID));
return uuid_old;
}
UUID UUID::MakeRandom() {
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)};
return MakeRandomWithSeed(device());
}
std::string UUID::Format() const {
return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
UUID UUID::MakeRandomWithSeed(u32 seed) {
// Create and initialize our RNG.
TinyMT rng;
rng.Initialize(seed);
UUID uuid;
// Populate the UUID with random bytes.
rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
return uuid;
}
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]);
UUID UUID::MakeRandomRFC4122V4() {
auto uuid = MakeRandom();
// According to Proposed Standard RFC 4122 Section 4.4, we must:
// 1. Set the two most significant bits (bits 6 and 7) of the
// clock_seq_hi_and_reserved to zero and one, respectively.
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
// 2. Set the four most significant bits (bits 12 through 15) of the
// time_hi_and_version field to the 4-bit version number from Section 4.1.3.
uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF);
return uuid;
}
} // namespace Common

View File

@@ -1,9 +1,11 @@
// Copyright 2018 yuzu Emulator Project
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <functional>
#include <string>
#include <string_view>
@@ -11,69 +13,119 @@
namespace Common {
constexpr u128 INVALID_UUID{{0, 0}};
/**
* Converts a hex string to a 128-bit unsigned integer.
*
* The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
*
* This function will assert and return INVALID_UUID under the following conditions:
* - If the hex string is more than 32 characters long
* - If the hex string contains non-hexadecimal characters
*
* @param hex_string Hexadecimal string
*
* @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
*/
[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid;
UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
explicit UUID(std::string_view hex_string) {
uuid = HexStringToU128(hex_string);
std::array<u8, 0x10> uuid{};
/// Constructs an invalid UUID.
constexpr UUID() = default;
/// Constructs a UUID from a reference to a 128 bit array.
constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {}
/**
* Constructs a UUID from either:
* 1. A 32 hexadecimal character string representing the bytes of the UUID
* 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
*
* The input string may contain uppercase or lowercase characters, but they must:
* 1. Contain valid hexadecimal characters (0-9, a-f, A-F)
* 2. Not contain the "0x" hexadecimal prefix
*
* Should the input string not meet the above requirements,
* an assert will be triggered and an invalid UUID is set instead.
*/
explicit UUID(std::string_view uuid_string);
~UUID() = default;
constexpr UUID(const UUID&) noexcept = default;
constexpr UUID(UUID&&) noexcept = default;
constexpr UUID& operator=(const UUID&) noexcept = default;
constexpr UUID& operator=(UUID&&) noexcept = default;
/**
* Returns whether the stored UUID is valid or not.
*
* @returns True if the stored UUID is valid, false otherwise.
*/
constexpr bool IsValid() const {
return uuid != std::array<u8, 0x10>{};
}
[[nodiscard]] constexpr explicit operator bool() const {
return uuid != INVALID_UUID;
/**
* Returns whether the stored UUID is invalid or not.
*
* @returns True if the stored UUID is invalid, false otherwise.
*/
constexpr bool IsInvalid() const {
return !IsValid();
}
[[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
return uuid == rhs.uuid;
/**
* Returns a 32 hexadecimal character string representing the bytes of the UUID.
*
* @returns A 32 hexadecimal character string of the UUID.
*/
std::string RawString() const;
/**
* Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
*
* @returns A RFC 4122 formatted UUID string.
*/
std::string FormattedString() const;
/**
* Returns a 64-bit hash of the UUID for use in hash table data structures.
*
* @returns A 64-bit hash of the UUID.
*/
size_t Hash() const noexcept;
/// DO NOT USE. Copies the contents of the UUID into a u128.
u128 AsU128() const;
/**
* Creates a default UUID "yuzu Default UID".
*
* @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID".
*/
static constexpr UUID MakeDefault() {
return UUID{
{'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'},
};
}
[[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
/**
* Creates a random UUID.
*
* @returns A random UUID.
*/
static UUID MakeRandom();
// TODO(ogniK): Properly generate uuids based on RFC-4122
[[nodiscard]] static UUID Generate();
/**
* Creates a random UUID with a seed.
*
* @param seed A seed to initialize the Mersenne-Twister RNG
*
* @returns A random UUID.
*/
static UUID MakeRandomWithSeed(u32 seed);
// Set the UUID to {0,0} to be considered an invalid user
constexpr void Invalidate() {
uuid = INVALID_UUID;
}
/**
* Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
*
* @returns A random UUID that is RFC 4122 Version 4 compliant.
*/
static UUID MakeRandomRFC4122V4();
[[nodiscard]] constexpr bool IsInvalid() const {
return uuid == INVALID_UUID;
}
[[nodiscard]] constexpr bool IsValid() const {
return !IsInvalid();
}
// TODO(ogniK): Properly generate a Nintendo ID
[[nodiscard]] constexpr u64 GetNintendoID() const {
return uuid[0];
}
[[nodiscard]] std::string Format() const;
[[nodiscard]] std::string FormatSwitch() const;
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
/// An invalid UUID. This UUID has all its bytes set to 0.
constexpr UUID InvalidUUID = {};
} // namespace Common
@@ -82,7 +134,7 @@ namespace std {
template <>
struct hash<Common::UUID> {
size_t operator()(const Common::UUID& uuid) const noexcept {
return uuid.uuid[1] ^ uuid.uuid[0];
return uuid.Hash();
}
};

View File

@@ -467,6 +467,8 @@ add_library(core STATIC
hle/service/mii/types.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/nfc.cpp

View File

@@ -6,6 +6,7 @@
#include <array>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
@@ -24,8 +25,11 @@ class CPUInterruptHandler;
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
/// Generic ARMv8 CPU interface
class ARM_Interface : NonCopyable {
class ARM_Interface {
public:
YUZU_NON_COPYABLE(ARM_Interface);
YUZU_NON_MOVEABLE(ARM_Interface);
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_)
: system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{

View File

@@ -128,15 +128,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (exefs == nullptr)
return exefs;
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
}
}
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
@@ -179,6 +170,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
}
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
}
}
return exefs;
}

View File

@@ -12,6 +12,7 @@
#include <type_traits>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
@@ -29,8 +30,11 @@ enum class VfsEntryType {
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
// is provided for convenience, but if the Vfs implementation has any additional state or
// functionality, they will need to override.
class VfsFilesystem : NonCopyable {
class VfsFilesystem {
public:
YUZU_NON_COPYABLE(VfsFilesystem);
YUZU_NON_MOVEABLE(VfsFilesystem);
explicit VfsFilesystem(VirtualDir root);
virtual ~VfsFilesystem();
@@ -77,8 +81,12 @@ protected:
};
// A class representing a file in an abstract filesystem.
class VfsFile : NonCopyable {
class VfsFile {
public:
YUZU_NON_COPYABLE(VfsFile);
YUZU_NON_MOVEABLE(VfsFile);
VfsFile() = default;
virtual ~VfsFile();
// Retrieves the file name.
@@ -176,8 +184,12 @@ public:
};
// A class representing a directory in an abstract filesystem.
class VfsDirectory : NonCopyable {
class VfsDirectory {
public:
YUZU_NON_COPYABLE(VfsDirectory);
YUZU_NON_MOVEABLE(VfsDirectory);
VfsDirectory() = default;
virtual ~VfsDirectory();
// Retrives the file located at path as if the current directory was root. Returns nullptr if

View File

@@ -13,8 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Common::UUID>)> callback) const {
Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user.GetValue())
.value_or(Common::UUID{Common::INVALID_UUID}));
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
}

View File

@@ -10,6 +10,7 @@
#include <mutex>
#include <unordered_map>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"

View File

@@ -269,7 +269,8 @@ void EmulatedController::ReloadInput() {
}
// Use a common UUID for TAS
const auto tas_uuid = Common::UUID{0x0, 0x7A5};
static constexpr Common::UUID TAS_UUID = Common::UUID{
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
// Register TAS devices. No need to force update
for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
@@ -278,8 +279,8 @@ void EmulatedController::ReloadInput() {
}
tas_button_devices[index]->SetCallback({
.on_change =
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
SetButton(callback, index, tas_uuid);
[this, index](const Common::Input::CallbackStatus& callback) {
SetButton(callback, index, TAS_UUID);
},
});
}
@@ -290,8 +291,8 @@ void EmulatedController::ReloadInput() {
}
tas_stick_devices[index]->SetCallback({
.on_change =
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
SetStick(callback, index, tas_uuid);
[this, index](const Common::Input::CallbackStatus& callback) {
SetStick(callback, index, TAS_UUID);
},
});
}
@@ -884,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
}
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
}
void EmulatedController::SetLedPattern() {
for (auto& device : output_devices) {
if (!device) {

View File

@@ -13,8 +13,6 @@
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/point.h"
#include "common/quaternion.h"
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
@@ -301,16 +299,23 @@ public:
/**
* Sends a specific vibration to the output device
* @return returns true if vibration had no errors
* @return true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
/**
* Sends a small vibration to the output device
* @return returns true if SetVibration was successfull
* @return true if SetVibration was successfull
*/
bool TestVibration(std::size_t device_index);
/**
* Sets the desired data to be polled from a controller
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
* @return true if SetPollingMode was successfull
*/
bool SetPollingMode(Common::Input::PollingMode polling_mode);
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;

View File

@@ -6,6 +6,7 @@
#include <memory>
#include "common/common_funcs.h"
#include "core/hid/hid_types.h"
namespace Core::HID {

View File

@@ -28,7 +28,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta
if (value > 0.8f) {
battery = Common::Input::BatteryLevel::Full;
}
if (value >= 1.0f) {
if (value >= 0.95f) {
battery = Common::Input::BatteryLevel::Charging;
}
break;

View File

@@ -404,6 +404,11 @@ inline s32 RequestParser::Pop() {
return static_cast<s32>(Pop<u32>());
}
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
template <typename T>
void RequestParser::PopRaw(T& value) {
static_assert(std::is_trivially_copyable_v<T>,
@@ -411,6 +416,9 @@ void RequestParser::PopRaw(T& value) {
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
template <typename T>
T RequestParser::PopRaw() {

View File

@@ -20,8 +20,6 @@ class KernelCore;
class KProcess;
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
YUZU_NON_COPYABLE(CLASS); \
YUZU_NON_MOVEABLE(CLASS); \
\
private: \
friend class ::Kernel::KClassTokenGenerator; \
@@ -32,6 +30,9 @@ private:
} \
\
public: \
YUZU_NON_COPYABLE(CLASS); \
YUZU_NON_MOVEABLE(CLASS); \
\
using BaseClass = BASE_CLASS; \
static constexpr TypeObj GetStaticTypeObj() { \
constexpr ClassTokenType Token = ClassToken(); \
@@ -224,9 +225,9 @@ private:
template <typename T>
class KScopedAutoObject {
public:
YUZU_NON_COPYABLE(KScopedAutoObject);
public:
constexpr KScopedAutoObject() = default;
constexpr KScopedAutoObject(T* o) : m_obj(o) {

View File

@@ -16,13 +16,12 @@ class KernelCore;
class KProcess;
class KAutoObjectWithListContainer {
public:
YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
public:
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
public:
class ListAccessor : public KScopedLightLock {
public:
explicit ListAccessor(KAutoObjectWithListContainer* container)
@@ -48,7 +47,6 @@ public:
friend class ListAccessor;
public:
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
void Initialize() {}

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
@@ -28,8 +29,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
auto& page_table = m_owner->PageTable();
// Construct the page group.
KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(addr, size))
@@ -143,4 +143,4 @@ ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
return ResultSuccess;
}
} // namespace Kernel
} // namespace Kernel

View File

@@ -22,13 +22,12 @@ namespace Kernel {
class KernelCore;
class KHandleTable {
public:
YUZU_NON_COPYABLE(KHandleTable);
YUZU_NON_MOVEABLE(KHandleTable);
public:
static constexpr size_t MaxTableSize = 1024;
public:
explicit KHandleTable(KernelCore& kernel_);
~KHandleTable();

View File

@@ -8,6 +8,7 @@
#include <mutex>
#include <tuple>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
@@ -20,8 +21,11 @@ namespace Kernel {
class KPageLinkedList;
class KMemoryManager final : NonCopyable {
class KMemoryManager final {
public:
YUZU_NON_COPYABLE(KMemoryManager);
YUZU_NON_MOVEABLE(KMemoryManager);
enum class Pool : u32 {
Application = 0,
Applet = 1,
@@ -88,26 +92,13 @@ public:
}
private:
class Impl final : NonCopyable {
private:
using RefCount = u16;
private:
KPageHeap heap;
Pool pool{};
class Impl final {
public:
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
YUZU_NON_COPYABLE(Impl);
YUZU_NON_MOVEABLE(Impl);
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
std::size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
public:
Impl() = default;
~Impl() = default;
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
@@ -130,6 +121,21 @@ private:
constexpr VAddr GetEndAddress() const {
return heap.GetEndAddress();
}
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
std::size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
private:
using RefCount = u16;
KPageHeap heap;
Pool pool{};
};
private:

View File

@@ -5,6 +5,7 @@
#pragma once
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "core/hle/kernel/k_memory_region_type.h"
@@ -13,11 +14,13 @@ namespace Kernel {
class KMemoryRegionAllocator;
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>,
NonCopyable {
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
friend class KMemoryRegionTree;
public:
YUZU_NON_COPYABLE(KMemoryRegion);
YUZU_NON_MOVEABLE(KMemoryRegion);
constexpr KMemoryRegion() = default;
constexpr KMemoryRegion(u64 address_, u64 last_address_)
: address{address_}, last_address{last_address_} {}
@@ -29,6 +32,8 @@ public:
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
type_id_) {}
~KMemoryRegion() = default;
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
@@ -39,16 +44,6 @@ public:
}
}
private:
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
address = a;
pair_address = p;
last_address = la;
attributes = r;
type_id = t;
}
public:
constexpr u64 GetAddress() const {
return address;
}
@@ -108,6 +103,14 @@ public:
}
private:
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
address = a;
pair_address = p;
last_address = la;
attributes = r;
type_id = t;
}
u64 address{};
u64 last_address{};
u64 pair_address{};
@@ -115,8 +118,25 @@ private:
u32 type_id{};
};
class KMemoryRegionTree final : NonCopyable {
class KMemoryRegionTree final {
private:
using TreeType =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
public:
YUZU_NON_COPYABLE(KMemoryRegionTree);
YUZU_NON_MOVEABLE(KMemoryRegionTree);
using value_type = TreeType::value_type;
using size_type = TreeType::size_type;
using difference_type = TreeType::difference_type;
using pointer = TreeType::pointer;
using const_pointer = TreeType::const_pointer;
using reference = TreeType::reference;
using const_reference = TreeType::const_reference;
using iterator = TreeType::iterator;
using const_iterator = TreeType::const_iterator;
struct DerivedRegionExtents {
const KMemoryRegion* first_region{};
const KMemoryRegion* last_region{};
@@ -140,29 +160,9 @@ public:
}
};
private:
using TreeType =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
public:
using value_type = TreeType::value_type;
using size_type = TreeType::size_type;
using difference_type = TreeType::difference_type;
using pointer = TreeType::pointer;
using const_pointer = TreeType::const_pointer;
using reference = TreeType::reference;
using const_reference = TreeType::const_reference;
using iterator = TreeType::iterator;
using const_iterator = TreeType::const_iterator;
private:
TreeType m_tree{};
KMemoryRegionAllocator& memory_region_allocator;
public:
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
~KMemoryRegionTree() = default;
public:
KMemoryRegion* FindModifiable(u64 address) {
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
return std::addressof(*it);
@@ -241,7 +241,6 @@ public:
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
}
public:
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
@@ -252,7 +251,6 @@ public:
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
}
public:
// Iterator accessors.
iterator begin() {
return m_tree.begin();
@@ -322,13 +320,21 @@ public:
iterator nfind(const_reference ref) const {
return m_tree.nfind(ref);
}
private:
TreeType m_tree{};
KMemoryRegionAllocator& memory_region_allocator;
};
class KMemoryRegionAllocator final : NonCopyable {
class KMemoryRegionAllocator final {
public:
YUZU_NON_COPYABLE(KMemoryRegionAllocator);
YUZU_NON_MOVEABLE(KMemoryRegionAllocator);
static constexpr size_t MaxMemoryRegions = 200;
constexpr KMemoryRegionAllocator() = default;
constexpr ~KMemoryRegionAllocator() = default;
template <typename... Args>
KMemoryRegion* Allocate(Args&&... args) {

View File

@@ -8,14 +8,44 @@
#include <vector>
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_bitmap.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
class KPageHeap final : NonCopyable {
class KPageHeap final {
public:
YUZU_NON_COPYABLE(KPageHeap);
YUZU_NON_MOVEABLE(KPageHeap);
KPageHeap() = default;
~KPageHeap() = default;
constexpr VAddr GetAddress() const {
return heap_address;
}
constexpr std::size_t GetSize() const {
return heap_size;
}
constexpr VAddr GetEndAddress() const {
return GetAddress() + GetSize();
}
constexpr std::size_t GetPageOffset(VAddr block) const {
return (block - GetAddress()) / PageSize;
}
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
VAddr AllocateBlock(s32 index, bool random);
void Free(VAddr addr, std::size_t num_pages);
void UpdateUsedSize() {
used_size = heap_size - (GetNumFreePages() * PageSize);
}
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
const auto target_pages{std::max(num_pages, align_pages)};
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
@@ -45,21 +75,13 @@ public:
}
private:
static constexpr std::size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
class Block final : NonCopyable {
private:
KPageBitmap bitmap;
VAddr heap_address{};
uintptr_t end_offset{};
std::size_t block_shift{};
std::size_t next_block_shift{};
class Block final {
public:
YUZU_NON_COPYABLE(Block);
YUZU_NON_MOVEABLE(Block);
Block() = default;
~Block() = default;
constexpr std::size_t GetShift() const {
return block_shift;
@@ -129,7 +151,6 @@ private:
return heap_address + (offset << GetShift());
}
public:
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
std::size_t cur_block_shift,
std::size_t next_block_shift) {
@@ -139,35 +160,15 @@ private:
return KPageBitmap::CalculateManagementOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
private:
KPageBitmap bitmap;
VAddr heap_address{};
uintptr_t end_offset{};
std::size_t block_shift{};
std::size_t next_block_shift{};
};
public:
KPageHeap() = default;
constexpr VAddr GetAddress() const {
return heap_address;
}
constexpr std::size_t GetSize() const {
return heap_size;
}
constexpr VAddr GetEndAddress() const {
return GetAddress() + GetSize();
}
constexpr std::size_t GetPageOffset(VAddr block) const {
return (block - GetAddress()) / PageSize;
}
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
VAddr AllocateBlock(s32 index, bool random);
void Free(VAddr addr, std::size_t num_pages);
void UpdateUsedSize() {
used_size = heap_size - (GetNumFreePages() * PageSize);
}
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
private:
constexpr std::size_t GetNumFreePages() const {
std::size_t num_free{};
@@ -180,6 +181,11 @@ private:
void FreeBlock(VAddr block, s32 index);
static constexpr std::size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
VAddr heap_address{};
std::size_t heap_size{};
std::size_t used_size{};

View File

@@ -41,27 +41,12 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
}
}
constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
if (info.GetAddress() < addr) {
return addr;
}
return info.GetAddress();
}
constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
std::size_t size{info.GetSize()};
if (info.GetAddress() < start) {
size -= start - info.GetAddress();
}
if (info.GetEndAddress() > end) {
size -= info.GetEndAddress() - end;
}
return size;
}
} // namespace
KPageTable::KPageTable(Core::System& system_) : system{system_} {}
KPageTable::KPageTable(Core::System& system_)
: general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {}
KPageTable::~KPageTable() = default;
ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
bool enable_aslr, VAddr code_addr,
@@ -282,7 +267,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify that the destination memory is unmapped.
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
@@ -300,7 +285,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
}
ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
const std::size_t num_pages{size / PageSize};
@@ -337,7 +322,7 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t
}
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
if (!size) {
return ResultSuccess;
@@ -371,7 +356,7 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
KPageTable& src_page_table, VAddr src_addr) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
const std::size_t num_pages{size / PageSize};
@@ -397,150 +382,477 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
return ResultSuccess;
}
ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
// Lock the physical memory lock.
std::lock_guard phys_lk(map_physical_memory_lock);
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
// Lock the table.
std::lock_guard lock{page_table_lock};
// Calculate the last address for convenience.
const VAddr last_address = address + size - 1;
std::size_t mapped_size{};
const VAddr end_addr{addr + size};
// Define iteration variables.
VAddr cur_address;
std::size_t mapped_size;
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state != KMemoryState::Free) {
mapped_size += GetSizeInRange(info, addr, end_addr);
}
});
// The entire mapping process can be retried.
while (true) {
// Check if the memory is already mapped.
{
// Lock the table.
KScopedLightLock lk(general_lock);
if (mapped_size == size) {
return ResultSuccess;
}
// Iterate over the memory.
cur_address = address;
mapped_size = 0;
const std::size_t remaining_size{size - mapped_size};
const std::size_t remaining_pages{remaining_size / PageSize};
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Reserve the memory from the process resource limit.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
remaining_size);
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
return ResultLimitReached;
}
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
KPageLinkedList page_linked_list;
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
if (info.GetState() != KMemoryState::Free) {
mapped_size += (last_address + 1 - cur_address);
}
break;
}
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
memory_pool, allocation_option));
// Track the memory if it's mapped.
if (info.GetState() != KMemoryState::Free) {
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
}
// We succeeded, so commit the memory reservation.
memory_reservation.Commit();
// Map the memory.
auto node{page_linked_list.Nodes().begin()};
PAddr map_addr{node->GetAddress()};
std::size_t src_num_pages{node->GetNumPages()};
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state != KMemoryState::Free) {
return;
}
std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize};
VAddr dst_addr{GetAddressInRange(info, addr)};
while (dst_num_pages) {
if (!src_num_pages) {
node = std::next(node);
map_addr = node->GetAddress();
src_num_pages = node->GetNumPages();
// Advance.
cur_address = info.GetEndAddress();
++it;
}
const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map,
map_addr);
dst_addr += num_pages * PageSize;
map_addr += num_pages * PageSize;
src_num_pages -= num_pages;
dst_num_pages -= num_pages;
// If the size mapped is the size requested, we've nothing to do.
R_SUCCEED_IF(size == mapped_size);
}
});
mapped_physical_memory_size += remaining_size;
// Allocate and map the memory.
{
// Reserve the memory from the process resource limit.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(),
LimitableResource::PhysicalMemory, size - mapped_size);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
const std::size_t num_pages{size / PageSize};
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryState::Normal,
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
// Allocate pages for the new memory.
KPageLinkedList page_linked_list;
R_TRY(system.Kernel().MemoryManager().Allocate(
page_linked_list, (size - mapped_size) / PageSize, memory_pool, allocation_option));
return ResultSuccess;
// Map the memory.
{
// Lock the table.
KScopedLightLock lk(general_lock);
size_t num_allocator_blocks = 0;
// Verify that nobody has mapped memory since we first checked.
{
// Iterate over the memory.
size_t checked_mapped_size = 0;
cur_address = address;
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
const bool is_free = info.GetState() == KMemoryState::Free;
if (is_free) {
if (info.GetAddress() < address) {
++num_allocator_blocks;
}
if (last_address < info.GetLastAddress()) {
++num_allocator_blocks;
}
}
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
if (!is_free) {
checked_mapped_size += (last_address + 1 - cur_address);
}
break;
}
// Track the memory if it's mapped.
if (!is_free) {
checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
// If the size now isn't what it was before, somebody mapped or unmapped
// concurrently. If this happened, retry.
if (mapped_size != checked_mapped_size) {
continue;
}
}
// Reset the current tracking address, and make sure we clean up on failure.
cur_address = address;
auto unmap_guard = detail::ScopeExit([&] {
if (cur_address > address) {
const VAddr last_unmap_address = cur_address - 1;
// Iterate, unmapping the pages.
cur_address = address;
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// If the memory state is free, we mapped it and need to unmap it.
if (info.GetState() == KMemoryState::Free) {
// Determine the range to unmap.
const size_t cur_pages =
std::min(VAddr(info.GetEndAddress()) - cur_address,
last_unmap_address + 1 - cur_address) /
PageSize;
// Unmap.
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
OperationType::Unmap)
.IsSuccess());
}
// Check if we're done.
if (last_unmap_address <= info.GetLastAddress()) {
break;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
}
});
// Iterate over the memory.
auto pg_it = page_linked_list.Nodes().begin();
PAddr pg_phys_addr = pg_it->GetAddress();
size_t pg_pages = pg_it->GetNumPages();
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// If it's unmapped, we need to map it.
if (info.GetState() == KMemoryState::Free) {
// Determine the range to map.
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
last_address + 1 - cur_address) /
PageSize;
// While we have pages to map, map them.
while (map_pages > 0) {
// Check if we're at the end of the physical block.
if (pg_pages == 0) {
// Ensure there are more pages to map.
ASSERT(pg_it != page_linked_list.Nodes().end());
// Advance our physical block.
++pg_it;
pg_phys_addr = pg_it->GetAddress();
pg_pages = pg_it->GetNumPages();
}
// Map whatever we can.
const size_t cur_pages = std::min(pg_pages, map_pages);
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
OperationType::Map, pg_phys_addr));
// Advance.
cur_address += cur_pages * PageSize;
map_pages -= cur_pages;
pg_phys_addr += cur_pages * PageSize;
pg_pages -= cur_pages;
}
}
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
break;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
// We succeeded, so commit the memory reservation.
memory_reservation.Commit();
// Increase our tracked mapped size.
mapped_physical_memory_size += (size - mapped_size);
// Update the relevant memory blocks.
block_manager->Update(address, size / PageSize, KMemoryState::Free,
KMemoryPermission::None, KMemoryAttribute::None,
KMemoryState::Normal, KMemoryPermission::UserReadWrite,
KMemoryAttribute::None);
// Cancel our guard.
unmap_guard.Cancel();
return ResultSuccess;
}
}
}
}
ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
// Lock the physical memory lock.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
const VAddr end_addr{addr + size};
ResultCode result{ResultSuccess};
std::size_t mapped_size{};
// Lock the table.
KScopedLightLock lk(general_lock);
// Verify that the region can be unmapped
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state == KMemoryState::Normal) {
if (info.attribute != KMemoryAttribute::None) {
result = ResultInvalidCurrentMemory;
return;
// Calculate the last address for convenience.
const VAddr last_address = address + size - 1;
// Define iteration variables.
VAddr cur_address = 0;
std::size_t mapped_size = 0;
std::size_t num_allocator_blocks = 0;
// Check if the memory is mapped.
{
// Iterate over the memory.
cur_address = address;
mapped_size = 0;
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// Verify the memory's state.
const bool is_normal = info.GetState() == KMemoryState::Normal &&
info.GetAttribute() == KMemoryAttribute::None;
const bool is_free = info.GetState() == KMemoryState::Free;
R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
if (is_normal) {
R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
if (info.GetAddress() < address) {
++num_allocator_blocks;
}
if (last_address < info.GetLastAddress()) {
++num_allocator_blocks;
}
}
mapped_size += GetSizeInRange(info, addr, end_addr);
} else if (info.state != KMemoryState::Free) {
result = ResultInvalidCurrentMemory;
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
if (is_normal) {
mapped_size += (last_address + 1 - cur_address);
}
break;
}
// Track the memory if it's mapped.
if (is_normal) {
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
});
if (result.IsError()) {
return result;
// If there's nothing mapped, we've nothing to do.
R_SUCCEED_IF(mapped_size == 0);
}
if (!mapped_size) {
return ResultSuccess;
// Make a page group for the unmap region.
KPageLinkedList pg;
{
auto& impl = this->PageTableImpl();
// Begin traversal.
Common::PageTable::TraversalContext context;
Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
bool cur_valid = false;
Common::PageTable::TraversalEntry next_entry;
bool next_valid = false;
size_t tot_size = 0;
cur_address = address;
next_valid = impl.BeginTraversal(next_entry, context, cur_address);
next_entry.block_size =
(next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1)));
// Iterate, building the group.
while (true) {
if ((!next_valid && !cur_valid) ||
(next_valid && cur_valid &&
next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
cur_entry.block_size += next_entry.block_size;
} else {
if (cur_valid) {
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize));
}
// Update tracking variables.
tot_size += cur_entry.block_size;
cur_entry = next_entry;
cur_valid = next_valid;
}
if (cur_entry.block_size + tot_size >= size) {
break;
}
next_valid = impl.ContinueTraversal(next_entry, context);
}
// Add the last block.
if (cur_valid) {
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize));
}
}
ASSERT(pg.GetNumPages() == mapped_size / PageSize);
// Unmap each region within the range
KPageLinkedList page_linked_list;
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state == KMemoryState::Normal) {
const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
const std::size_t block_num_pages{block_size / PageSize};
const VAddr block_addr{GetAddressInRange(info, addr)};
// Reset the current tracking address, and make sure we clean up on failure.
cur_address = address;
auto remap_guard = detail::ScopeExit([&] {
if (cur_address > address) {
const VAddr last_map_address = cur_address - 1;
cur_address = address;
AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
// Iterate over the memory we unmapped.
auto it = block_manager->FindIterator(cur_address);
auto pg_it = pg.Nodes().begin();
PAddr pg_phys_addr = pg_it->GetAddress();
size_t pg_pages = pg_it->GetNumPages();
if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
OperationType::Unmap);
result.IsError()) {
return;
while (true) {
// Get the memory info for the pages we unmapped, convert to property.
const KMemoryInfo info = it->GetMemoryInfo();
// If the memory is normal, we unmapped it and need to re-map it.
if (info.GetState() == KMemoryState::Normal) {
// Determine the range to map.
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
last_map_address + 1 - cur_address) /
PageSize;
// While we have pages to map, map them.
while (map_pages > 0) {
// Check if we're at the end of the physical block.
if (pg_pages == 0) {
// Ensure there are more pages to map.
ASSERT(pg_it != pg.Nodes().end());
// Advance our physical block.
++pg_it;
pg_phys_addr = pg_it->GetAddress();
pg_pages = pg_it->GetNumPages();
}
// Map whatever we can.
const size_t cur_pages = std::min(pg_pages, map_pages);
ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(),
OperationType::Map, pg_phys_addr) == ResultSuccess);
// Advance.
cur_address += cur_pages * PageSize;
map_pages -= cur_pages;
pg_phys_addr += cur_pages * PageSize;
pg_pages -= cur_pages;
}
}
// Check if we're done.
if (last_map_address <= info.GetLastAddress()) {
break;
}
// Advance.
++it;
}
}
});
if (result.IsError()) {
return result;
// Iterate over the memory, unmapping as we go.
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// If the memory state is normal, we need to unmap it.
if (info.GetState() == KMemoryState::Normal) {
// Determine the range to unmap.
const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
last_address + 1 - cur_address) /
PageSize;
// Unmap.
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap));
}
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
break;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
const std::size_t num_pages{size / PageSize};
system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool,
allocation_option);
block_manager->Update(addr, num_pages, KMemoryState::Free);
// Release the memory resource.
mapped_physical_memory_size -= mapped_size;
auto process{system.Kernel().CurrentProcess()};
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
mapped_physical_memory_size -= mapped_size;
// Update memory blocks.
system.Kernel().MemoryManager().Free(pg, size / PageSize, memory_pool, allocation_option);
block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None);
// We succeeded.
remap_guard.Cancel();
return ResultSuccess;
}
ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
@@ -579,7 +891,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
}
ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
@@ -622,6 +934,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
KMemoryPermission perm) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
for (const auto& node : page_linked_list.Nodes()) {
@@ -650,7 +964,7 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Check the memory state.
R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
@@ -667,12 +981,13 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
}
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
for (const auto& node : page_linked_list.Nodes()) {
const std::size_t num_pages{(addr - cur_addr) / PageSize};
if (const auto result{
Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
OperationType::Unmap)};
result.IsError()) {
return result;
}
@@ -691,7 +1006,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Check the memory state.
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None,
@@ -712,7 +1027,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
const size_t num_pages = size / PageSize;
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify we can change the memory permission.
KMemoryState old_state;
@@ -766,7 +1081,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
}
KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
return block_manager->FindBlock(addr).GetMemoryInfo();
}
@@ -781,7 +1096,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
}
ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState state{};
KMemoryAttribute attribute{};
@@ -799,7 +1114,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
}
ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState state{};
@@ -818,7 +1133,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
const size_t num_pages = size / PageSize;
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify we can change the memory permission.
KMemoryState old_state;
@@ -847,7 +1162,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
KMemoryAttribute::SetMask);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify we can change the memory attribute.
KMemoryState old_state;
@@ -878,7 +1193,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Only process page tables are allowed to set heap size.
ASSERT(!this->IsKernel());
@@ -889,15 +1204,15 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
}
ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Lock the physical memory lock.
std::lock_guard phys_lk(map_physical_memory_lock);
// Lock the physical memory mutex.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
// Try to perform a reduction in heap, instead of an extension.
VAddr cur_address{};
std::size_t allocation_size{};
{
// Lock the table.
std::lock_guard lk(page_table_lock);
KScopedLightLock lk(general_lock);
// Validate that setting heap size is possible at all.
R_UNLESS(!is_kernel, ResultOutOfMemory);
@@ -962,7 +1277,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Map the pages.
{
// Lock the table.
std::lock_guard lk(page_table_lock);
KScopedLightLock lk(general_lock);
// Ensure that the heap hasn't changed since we began executing.
ASSERT(cur_address == current_heap_end);
@@ -1004,7 +1319,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, KMemoryState state,
KMemoryPermission perm, PAddr map_addr) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
if (!CanContain(region_start, region_num_pages * PageSize, state)) {
return ResultInvalidCurrentMemory;
@@ -1035,7 +1350,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
}
ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState(
@@ -1058,7 +1373,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
}
ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState(
@@ -1081,7 +1396,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
}
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
@@ -1108,7 +1423,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
}
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;

View File

@@ -5,11 +5,12 @@
#pragma once
#include <memory>
#include <mutex>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
@@ -22,9 +23,13 @@ namespace Kernel {
class KMemoryBlockManager;
class KPageTable final : NonCopyable {
class KPageTable final {
public:
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
explicit KPageTable(Core::System& system_);
~KPageTable();
ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
VAddr code_addr, std::size_t code_size,
@@ -142,11 +147,12 @@ private:
}
bool IsLockedByCurrentThread() const {
return true;
return general_lock.IsLockedByCurrentThread();
}
std::recursive_mutex page_table_lock;
std::mutex map_physical_memory_lock;
mutable KLightLock general_lock;
mutable KLightLock map_physical_memory_lock;
std::unique_ptr<KMemoryBlockManager> block_manager;
public:
@@ -205,7 +211,7 @@ public:
return alias_code_region_end - alias_code_region_start;
}
size_t GetNormalMemorySize() {
std::lock_guard lk(page_table_lock);
KScopedLightLock lk(general_lock);
return GetHeapSize() + mapped_physical_memory_size;
}
constexpr std::size_t GetAddressSpaceWidth() const {
@@ -247,7 +253,9 @@ public:
constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
return !IsOutsideASLRRegion(address, size);
}
constexpr PAddr GetPhysicalAddr(VAddr addr) {
PAddr GetPhysicalAddr(VAddr addr) {
ASSERT(IsLockedByCurrentThread());
const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
ASSERT(backing_addr);
return backing_addr + addr;

View File

@@ -7,6 +7,7 @@
#include <atomic>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Kernel {
@@ -15,13 +16,17 @@ class KernelCore;
namespace impl {
class KSlabHeapImpl final : NonCopyable {
class KSlabHeapImpl final {
public:
YUZU_NON_COPYABLE(KSlabHeapImpl);
YUZU_NON_MOVEABLE(KSlabHeapImpl);
struct Node {
Node* next{};
};
constexpr KSlabHeapImpl() = default;
constexpr ~KSlabHeapImpl() = default;
void Initialize(std::size_t size) {
ASSERT(head == nullptr);
@@ -64,9 +69,13 @@ private:
} // namespace impl
class KSlabHeapBase : NonCopyable {
class KSlabHeapBase {
public:
YUZU_NON_COPYABLE(KSlabHeapBase);
YUZU_NON_MOVEABLE(KSlabHeapBase);
constexpr KSlabHeapBase() = default;
constexpr ~KSlabHeapBase() = default;
constexpr bool Contains(uintptr_t addr) const {
return start <= addr && addr < end;

View File

@@ -396,7 +396,7 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle
// Get the process id.
*out_process_id = process->GetId();
return ResultInvalidHandle;
return ResultSuccess;
}
static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
@@ -645,6 +645,10 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
LOG_DEBUG(Debug_Emulated, "{}", str);
}
static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
OutputDebugString(system, address, len);
}
/// Gets system/memory information for the current process
static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
u64 info_sub_id) {
@@ -1404,7 +1408,7 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
}
static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}",
static_cast<void*>(out), address, size);
// Get kernel instance.
auto& kernel = system.Kernel();
@@ -1438,6 +1442,10 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr
return ResultSuccess;
}
static ResultCode CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
return CreateCodeMemory(system, out, address, size);
}
static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
VAddr address, size_t size, Svc::MemoryPermission perm) {
@@ -1517,6 +1525,12 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
return ResultSuccess;
}
static ResultCode ControlCodeMemory32(Core::System& system, Handle code_memory_handle,
u32 operation, u64 address, u64 size,
Svc::MemoryPermission perm) {
return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
}
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
@@ -2559,9 +2573,9 @@ struct FunctionDef {
} // namespace
static const FunctionDef SVC_Table_32[] = {
{0x00, nullptr, "Unknown"},
{0x00, nullptr, "Unknown0"},
{0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
{0x02, nullptr, "Unknown"},
{0x02, nullptr, "SetMemoryPermission32"},
{0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
{0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
{0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
@@ -2591,97 +2605,97 @@ static const FunctionDef SVC_Table_32[] = {
{0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
{0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
{0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
{0x20, nullptr, "Unknown"},
{0x20, nullptr, "SendSyncRequestLight32"},
{0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
{0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
{0x23, nullptr, "Unknown"},
{0x23, nullptr, "SendAsyncRequestWithUserBuffer32"},
{0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
{0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
{0x26, SvcWrap32<Break32>, "Break32"},
{0x27, nullptr, "OutputDebugString32"},
{0x28, nullptr, "Unknown"},
{0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"},
{0x28, nullptr, "ReturnFromException32"},
{0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
{0x2a, nullptr, "Unknown"},
{0x2b, nullptr, "Unknown"},
{0x2a, nullptr, "FlushEntireDataCache32"},
{0x2b, nullptr, "FlushDataCache32"},
{0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
{0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
{0x2e, nullptr, "Unknown"},
{0x2f, nullptr, "Unknown"},
{0x30, nullptr, "Unknown"},
{0x31, nullptr, "Unknown"},
{0x2e, nullptr, "GetDebugFutureThreadInfo32"},
{0x2f, nullptr, "GetLastThreadInfo32"},
{0x30, nullptr, "GetResourceLimitLimitValue32"},
{0x31, nullptr, "GetResourceLimitCurrentValue32"},
{0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
{0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
{0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
{0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
{0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},
{0x3a, nullptr, "Unknown"},
{0x3b, nullptr, "Unknown"},
{0x3c, nullptr, "Unknown"},
{0x3d, nullptr, "Unknown"},
{0x3e, nullptr, "Unknown"},
{0x3f, nullptr, "Unknown"},
{0x37, nullptr, "GetResourceLimitPeakValue32"},
{0x38, nullptr, "Unknown38"},
{0x39, nullptr, "CreateIoPool32"},
{0x3a, nullptr, "CreateIoRegion32"},
{0x3b, nullptr, "Unknown3b"},
{0x3c, nullptr, "KernelDebug32"},
{0x3d, nullptr, "ChangeKernelTraceState32"},
{0x3e, nullptr, "Unknown3e"},
{0x3f, nullptr, "Unknown3f"},
{0x40, nullptr, "CreateSession32"},
{0x41, nullptr, "AcceptSession32"},
{0x42, nullptr, "Unknown"},
{0x42, nullptr, "ReplyAndReceiveLight32"},
{0x43, nullptr, "ReplyAndReceive32"},
{0x44, nullptr, "Unknown"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"},
{0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "Unknown"},
{0x49, nullptr, "Unknown"},
{0x4a, nullptr, "Unknown"},
{0x4b, nullptr, "Unknown"},
{0x4c, nullptr, "Unknown"},
{0x4d, nullptr, "Unknown"},
{0x4e, nullptr, "Unknown"},
{0x4f, nullptr, "Unknown"},
{0x50, nullptr, "Unknown"},
{0x51, nullptr, "Unknown"},
{0x52, nullptr, "Unknown"},
{0x53, nullptr, "Unknown"},
{0x54, nullptr, "Unknown"},
{0x55, nullptr, "Unknown"},
{0x56, nullptr, "Unknown"},
{0x57, nullptr, "Unknown"},
{0x58, nullptr, "Unknown"},
{0x59, nullptr, "Unknown"},
{0x5a, nullptr, "Unknown"},
{0x5b, nullptr, "Unknown"},
{0x5c, nullptr, "Unknown"},
{0x5d, nullptr, "Unknown"},
{0x5e, nullptr, "Unknown"},
{0x46, nullptr, "MapIoRegion32"},
{0x47, nullptr, "UnmapIoRegion32"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe32"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"},
{0x4a, nullptr, "SetUnsafeLimit32"},
{0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"},
{0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"},
{0x4d, nullptr, "SleepSystem32"},
{0x4e, nullptr, "ReadWriteRegister32"},
{0x4f, nullptr, "SetProcessActivity32"},
{0x50, nullptr, "CreateSharedMemory32"},
{0x51, nullptr, "MapTransferMemory32"},
{0x52, nullptr, "UnmapTransferMemory32"},
{0x53, nullptr, "CreateInterruptEvent32"},
{0x54, nullptr, "QueryPhysicalAddress32"},
{0x55, nullptr, "QueryIoMapping32"},
{0x56, nullptr, "CreateDeviceAddressSpace32"},
{0x57, nullptr, "AttachDeviceAddressSpace32"},
{0x58, nullptr, "DetachDeviceAddressSpace32"},
{0x59, nullptr, "MapDeviceAddressSpaceByForce32"},
{0x5a, nullptr, "MapDeviceAddressSpaceAligned32"},
{0x5b, nullptr, "MapDeviceAddressSpace32"},
{0x5c, nullptr, "UnmapDeviceAddressSpace32"},
{0x5d, nullptr, "InvalidateProcessDataCache32"},
{0x5e, nullptr, "StoreProcessDataCache32"},
{0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
{0x60, nullptr, "Unknown"},
{0x61, nullptr, "Unknown"},
{0x62, nullptr, "Unknown"},
{0x63, nullptr, "Unknown"},
{0x64, nullptr, "Unknown"},
{0x60, nullptr, "StoreProcessDataCache32"},
{0x61, nullptr, "BreakDebugProcess32"},
{0x62, nullptr, "TerminateDebugProcess32"},
{0x63, nullptr, "GetDebugEvent32"},
{0x64, nullptr, "ContinueDebugEvent32"},
{0x65, nullptr, "GetProcessList32"},
{0x66, nullptr, "Unknown"},
{0x67, nullptr, "Unknown"},
{0x68, nullptr, "Unknown"},
{0x69, nullptr, "Unknown"},
{0x6A, nullptr, "Unknown"},
{0x6B, nullptr, "Unknown"},
{0x6C, nullptr, "Unknown"},
{0x6D, nullptr, "Unknown"},
{0x6E, nullptr, "Unknown"},
{0x66, nullptr, "GetThreadList"},
{0x67, nullptr, "GetDebugThreadContext32"},
{0x68, nullptr, "SetDebugThreadContext32"},
{0x69, nullptr, "QueryDebugProcessMemory32"},
{0x6A, nullptr, "ReadDebugProcessMemory32"},
{0x6B, nullptr, "WriteDebugProcessMemory32"},
{0x6C, nullptr, "SetHardwareBreakPoint32"},
{0x6D, nullptr, "GetDebugThreadParam32"},
{0x6E, nullptr, "Unknown6E"},
{0x6f, nullptr, "GetSystemInfo32"},
{0x70, nullptr, "CreatePort32"},
{0x71, nullptr, "ManageNamedPort32"},
{0x72, nullptr, "ConnectToPort32"},
{0x73, nullptr, "SetProcessMemoryPermission32"},
{0x74, nullptr, "Unknown"},
{0x75, nullptr, "Unknown"},
{0x76, nullptr, "Unknown"},
{0x74, nullptr, "MapProcessMemory32"},
{0x75, nullptr, "UnmapProcessMemory32"},
{0x76, nullptr, "QueryProcessMemory32"},
{0x77, nullptr, "MapProcessCodeMemory32"},
{0x78, nullptr, "UnmapProcessCodeMemory32"},
{0x79, nullptr, "Unknown"},
{0x7A, nullptr, "Unknown"},
{0x79, nullptr, "CreateProcess32"},
{0x7A, nullptr, "StartProcess32"},
{0x7B, nullptr, "TerminateProcess32"},
{0x7C, nullptr, "GetProcessInfo32"},
{0x7D, nullptr, "CreateResourceLimit32"},
@@ -2754,7 +2768,7 @@ static const FunctionDef SVC_Table_32[] = {
};
static const FunctionDef SVC_Table_64[] = {
{0x00, nullptr, "Unknown"},
{0x00, nullptr, "Unknown0"},
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
@@ -2809,23 +2823,23 @@ static const FunctionDef SVC_Table_64[] = {
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},
{0x3A, nullptr, "Unknown"},
{0x3B, nullptr, "Unknown"},
{0x37, nullptr, "GetResourceLimitPeakValue"},
{0x38, nullptr, "Unknown38"},
{0x39, nullptr, "CreateIoPool"},
{0x3A, nullptr, "CreateIoRegion"},
{0x3B, nullptr, "Unknown3B"},
{0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
{0x3E, nullptr, "Unknown"},
{0x3F, nullptr, "Unknown"},
{0x3E, nullptr, "Unknown3e"},
{0x3F, nullptr, "Unknown3f"},
{0x40, nullptr, "CreateSession"},
{0x41, nullptr, "AcceptSession"},
{0x42, nullptr, "ReplyAndReceiveLight"},
{0x43, nullptr, "ReplyAndReceive"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x46, nullptr, "MapIoRegion"},
{0x47, nullptr, "UnmapIoRegion"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
@@ -2864,7 +2878,7 @@ static const FunctionDef SVC_Table_64[] = {
{0x6B, nullptr, "WriteDebugProcessMemory"},
{0x6C, nullptr, "SetHardwareBreakPoint"},
{0x6D, nullptr, "GetDebugThreadParam"},
{0x6E, nullptr, "Unknown"},
{0x6E, nullptr, "Unknown6E"},
{0x6F, nullptr, "GetSystemInfo"},
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},

View File

@@ -669,4 +669,26 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
// Used by CreateCodeMemory32
template <ResultCode func(Core::System&, Handle*, u32, u32)>
void SvcWrap32(Core::System& system) {
Handle handle = 0;
const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw;
system.CurrentArmInterface().SetReg(1, handle);
FuncReturn(system, retval);
}
// Used by ControlCodeMemory32
template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
static_cast<Svc::MemoryPermission>(Param32(system, 6)))
.raw;
FuncReturn(system, retval);
}
} // namespace Kernel

View File

@@ -39,9 +39,9 @@ constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
// Thumbnails are hard coded to be at least this size
constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
static std::filesystem::path GetImagePath(Common::UUID uuid) {
static std::filesystem::path GetImagePath(const Common::UUID& uuid) {
return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch());
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
}
static constexpr u32 SanitizeJPEGSize(std::size_t size) {
@@ -290,7 +290,7 @@ public:
protected:
void Get(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -300,21 +300,21 @@ protected:
rb.PushRaw(profile_base);
} else {
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
user_id.Format());
user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
}
}
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
if (profile_manager.GetProfileBase(user_id, profile_base)) {
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(ResultSuccess);
rb.PushRaw(profile_base);
} else {
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
}
@@ -373,7 +373,7 @@ protected:
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
base.timestamp, base.user_uuid.RawString());
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
@@ -406,7 +406,7 @@ protected:
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
base.timestamp, base.user_uuid.RawString());
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
@@ -435,7 +435,7 @@ protected:
}
ProfileManager& profile_manager;
Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to.
Common::UUID user_id{}; ///< The user id this profile refers to.
};
class IProfile final : public IProfileCommon {
@@ -547,7 +547,7 @@ private:
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw<u64>(user_id.GetNintendoID());
rb.PushRaw<u64>(user_id.Hash());
}
void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
@@ -577,7 +577,7 @@ private:
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw<u64>(user_id.GetNintendoID());
rb.PushRaw<u64>(user_id.Hash());
}
void StoreOpenContext(Kernel::HLERequestContext& ctx) {
@@ -587,7 +587,7 @@ private:
}
std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
Common::UUID user_id{Common::INVALID_UUID};
Common::UUID user_id{};
};
// 6.0.0+
@@ -687,7 +687,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -718,7 +718,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -833,7 +833,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -875,7 +875,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
IPC::RequestParser rp{ctx};
const auto uuid = rp.PopRaw<Common::UUID>();
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.RawString());
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
// way of confirming things like the TID, we're going to assume a non zero value for the time
@@ -889,7 +889,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
const auto uuid = rp.PopRaw<Common::UUID>();
const auto tid = rp.Pop<u64_le>();
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.RawString(), tid);
StoreSaveDataThumbnail(ctx, uuid, tid);
}
@@ -903,7 +903,7 @@ void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx,
return;
}
if (!uuid) {
if (uuid.IsInvalid()) {
LOG_ERROR(Service_ACC, "User ID is not valid!");
rb.Push(ERR_INVALID_USER_ID);
return;
@@ -927,20 +927,20 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
IPC::ResponseBuilder rb{ctx, 6};
if (profile_manager->GetUserCount() != 1) {
rb.Push(ResultSuccess);
rb.PushRaw<u128>(Common::INVALID_UUID);
rb.PushRaw(Common::InvalidUUID);
return;
}
const auto user_list = profile_manager->GetAllUsers();
if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) {
rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code
rb.PushRaw<u128>(Common::INVALID_UUID);
rb.PushRaw(Common::InvalidUUID);
return;
}
// Select the first user we have
rb.Push(ResultSuccess);
rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
rb.PushRaw(profile_manager->GetUser(0)->uuid);
}
Module::Interface::Interface(std::shared_ptr<Module> module_,

View File

@@ -19,8 +19,8 @@ namespace FS = Common::FS;
using Common::UUID;
struct UserRaw {
UUID uuid{Common::INVALID_UUID};
UUID uuid2{Common::INVALID_UUID};
UUID uuid{};
UUID uuid2{};
u64 timestamp{};
ProfileUsername username{};
ProfileData extra_data{};
@@ -45,7 +45,7 @@ ProfileManager::ProfileManager() {
// Create an user if none are present
if (user_count == 0) {
CreateNewUser(UUID::Generate(), "yuzu");
CreateNewUser(UUID::MakeRandom(), "yuzu");
}
auto current =
@@ -101,7 +101,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
if (user_count == MAX_USERS) {
return ERROR_TOO_MANY_USERS;
}
if (!uuid) {
if (uuid.IsInvalid()) {
return ERROR_ARGUMENT_IS_NULL;
}
if (username[0] == 0x0) {
@@ -145,7 +145,7 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
/// Returns a users profile index based on their user id.
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
if (uuid.IsInvalid()) {
return std::nullopt;
}
@@ -250,9 +250,10 @@ UserIDArray ProfileManager::GetOpenUsers() const {
std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) {
if (p.is_open)
return p.user_uuid;
return UUID{Common::INVALID_UUID};
return Common::InvalidUUID;
});
std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
std::stable_partition(output.begin(), output.end(),
[](const UUID& uuid) { return uuid.IsValid(); });
return output;
}
@@ -299,7 +300,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
profiles[*index] = ProfileInfo{};
std::stable_partition(profiles.begin(), profiles.end(),
[](const ProfileInfo& profile) { return profile.user_uuid; });
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
return true;
}
@@ -361,7 +362,7 @@ void ProfileManager::ParseUserSaveFile() {
}
std::stable_partition(profiles.begin(), profiles.end(),
[](const ProfileInfo& profile) { return profile.user_uuid; });
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
}
void ProfileManager::WriteUserSaveFile() {

View File

@@ -35,7 +35,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 {
Common::UUID user_uuid{Common::INVALID_UUID};
Common::UUID user_uuid{};
ProfileUsername username{};
u64 creation_time{};
ProfileData data{}; // TODO(ognik): Work out what this is
@@ -49,7 +49,7 @@ struct ProfileBase {
// Zero out all the fields to make the profile slot considered "Empty"
void Invalidate() {
user_uuid.Invalidate();
user_uuid = {};
timestamp = 0;
username.fill(0);
}
@@ -103,7 +103,7 @@ private:
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count{};
Common::UUID last_opened_user{Common::INVALID_UUID};
Common::UUID last_opened_user{};
};
}; // namespace Service::Account

View File

@@ -55,7 +55,7 @@ constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
struct LaunchParameterAccountPreselectedUser {
u32_le magic;
u32_le is_account_selected;
u128 current_user;
Common::UUID current_user;
INSERT_PADDING_BYTES(0x70);
};
static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
@@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
if (messages.empty()) {
on_new_message->GetWritableEvent().Clear();
return AppletMessage::NoMessage;
return AppletMessage::None;
}
auto msg = messages.front();
messages.pop();
@@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
}
void AppletMessageQueue::RequestExit() {
PushMessage(AppletMessage::ExitRequested);
PushMessage(AppletMessage::Exit);
}
void AppletMessageQueue::FocusStateChanged() {
@@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
const auto message = msg_queue->PopMessage();
IPC::ResponseBuilder rb{ctx, 3};
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
if (message == AppletMessageQueue::AppletMessage::None) {
LOG_ERROR(Service_AM, "Message queue is empty");
rb.Push(ERR_NO_MESSAGES);
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
@@ -1453,8 +1453,8 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
Account::ProfileManager profile_manager{};
const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
ASSERT(uuid);
params.current_user = uuid->uuid;
ASSERT(uuid.has_value() && uuid->IsValid());
params.current_user = *uuid;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};

View File

@@ -22,6 +22,7 @@ class NVFlinger;
namespace Service::AM {
// This is nn::settings::Language
enum SystemLanguage {
Japanese = 0,
English = 1, // en-US
@@ -41,16 +42,44 @@ enum SystemLanguage {
// 4.0.0+
SimplifiedChinese = 15,
TraditionalChinese = 16,
// 10.1.0+
BrazilianPortuguese = 17,
};
class AppletMessageQueue {
public:
// This is nn::am::AppletMessage
enum class AppletMessage : u32 {
NoMessage = 0,
ExitRequested = 4,
None = 0,
ChangeIntoForeground = 1,
ChangeIntoBackground = 2,
Exit = 4,
ApplicationExited = 6,
FocusStateChanged = 15,
Resume = 16,
DetectShortPressingHomeButton = 20,
DetectLongPressingHomeButton = 21,
DetectShortPressingPowerButton = 22,
DetectMiddlePressingPowerButton = 23,
DetectLongPressingPowerButton = 24,
RequestToPrepareSleep = 25,
FinishedSleepSequence = 26,
SleepRequiredByHighTemperature = 27,
SleepRequiredByLowBattery = 28,
AutoPowerDown = 29,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
DetectReceivingCecSystemStandby = 32,
SdCardRemoved = 33,
LaunchApplicationRequested = 50,
RequestToDisplay = 51,
ShowApplicationLogo = 55,
HideApplicationLogo = 56,
ForceHideApplicationLogo = 57,
FloatingApplicationDetected = 60,
DetectShortPressingCaptureButton = 90,
AlbumScreenShotTaken = 92,
AlbumRecordingSaved = 93,
};
explicit AppletMessageQueue(Core::System& system);
@@ -179,11 +208,14 @@ public:
~ICommonStateGetter() override;
private:
// This is nn::oe::FocusState
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
Background = 3,
};
// This is nn::oe::OperationMode
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,

View File

@@ -62,11 +62,11 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
if (uuid.has_value() && uuid->IsValid()) {
output.result = 0;
output.uuid_selected = uuid->uuid;
output.uuid_selected = *uuid;
} else {
status = ERR_USER_CANCELLED_SELECTION;
output.result = ERR_USER_CANCELLED_SELECTION.raw;
output.uuid_selected = Common::INVALID_UUID;
output.uuid_selected = Common::InvalidUUID;
}
final_data = std::vector<u8>(sizeof(UserSelectionOutput));

View File

@@ -27,7 +27,7 @@ static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has inco
struct UserSelectionOutput {
u64 result;
u128 uuid_selected;
Common::UUID uuid_selected;
};
static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");

View File

@@ -17,8 +17,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con
Controller::Controller(Core::Timing::CoreTiming& core_timing_)
: core_timing{core_timing_}, configs{
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION},
} {}
Controller::~Controller() = default;
@@ -63,13 +63,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
PerformanceConfiguration::Config15,
}};
SetPerformanceConfiguration(PerformanceMode::Docked,
SetPerformanceConfiguration(PerformanceMode::Boost,
BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
}
PerformanceMode Controller::GetCurrentPerformanceMode() const {
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
: PerformanceMode::Handheld;
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
: PerformanceMode::Normal;
}
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {

View File

@@ -32,15 +32,18 @@ enum class PerformanceConfiguration : u32 {
Config16 = 0x9222000C,
};
// This is nn::oe::CpuBoostMode
enum class CpuBoostMode : u32 {
Disabled = 0,
Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16
Partial = 2, // GPU Only -> Config 15 or 16
Normal = 0, // Boost mode disabled
FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16
Partial = 2, // GPU Only -> Config 15 or 16
};
enum class PerformanceMode : u8 {
Handheld = 0,
Docked = 1,
// This is nn::oe::PerformanceMode
enum class PerformanceMode : s32 {
Invalid = -1,
Normal = 0,
Boost = 1,
};
// Class to manage the state and change of the emulated system performance.

View File

@@ -173,7 +173,7 @@ private:
const auto uuid = rp.PopRaw<Common::UUID>();
LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
uuid.Format());
uuid.RawString());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -186,7 +186,7 @@ private:
[[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
const auto pid = rp.Pop<u64>();
LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
uuid.Format(), pid);
uuid.RawString(), pid);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -312,7 +312,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
IPC::RequestParser rp{ctx};
auto uuid = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.RawString());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);

View File

@@ -320,7 +320,7 @@ Hid::Hid(Core::System& system_)
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
{310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
{400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
{401, nullptr, "EnableUsbFullKeyController"},
{402, nullptr, "IsUsbFullKeyControllerConnected"},
{403, nullptr, "HasBattery"},
@@ -1673,6 +1673,16 @@ void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(false);
}
void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};

View File

@@ -159,6 +159,7 @@ private:
void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);

View File

@@ -118,16 +118,6 @@ u16 GenerateCrc16(const void* data, std::size_t size) {
return Common::swap16(static_cast<u16>(crc));
}
Common::UUID GenerateValidUUID() {
auto uuid{Common::UUID::Generate()};
// Bit 7 must be set, and bit 6 unset for the UUID to be valid
uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL;
uuid.uuid[1] |= 0x0000000000000080ULL;
return uuid;
}
template <typename T>
T GetRandomValue(T min, T max) {
std::random_device device;
@@ -383,7 +373,7 @@ MiiStoreData::MiiStoreData() = default;
MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
const Common::UUID& user_id) {
data.name = name;
data.uuid = GenerateValidUUID();
data.uuid = Common::UUID::MakeRandomRFC4122V4();
std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
data_crc = GenerateCrc16(data.data.data(), sizeof(data));

View File

@@ -202,7 +202,7 @@ struct MiiStoreData {
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
Name name{};
Common::UUID uuid{Common::INVALID_UUID};
Common::UUID uuid{};
} data;
u16 data_crc{};
@@ -326,7 +326,7 @@ public:
ResultCode GetIndex(const MiiInfo& info, u32& index);
private:
const Common::UUID user_id{Common::INVALID_UUID};
const Common::UUID user_id{};
u64 update_counter{};
};

View File

@@ -0,0 +1,45 @@
// Copyright 2022 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/mnpp/mnpp_app.h"
#include "core/hle/service/sm/sm.h"
namespace Service::MNPP {
class MNPP_APP final : public ServiceFramework<MNPP_APP> {
public:
explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &MNPP_APP::Unknown0, "unknown0"},
{1, &MNPP_APP::Unknown1, "unknown1"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Unknown0(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MNPP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Unknown1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MNPP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager);
}
} // namespace Service::MNPP

View File

@@ -0,0 +1,20 @@
// Copyright 2022 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Core {
class System;
}
namespace Service::SM {
class ServiceManager;
}
namespace Service::MNPP {
/// Registers all MNPP services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::MNPP

File diff suppressed because it is too large Load Diff

View File

@@ -7,15 +7,132 @@
#include <array>
#include <vector>
#include "common/common_funcs.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
}
class KReadableEvent;
} // namespace Kernel
namespace Core::HID {
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFP {
enum class ServiceType : u32 {
User,
Debug,
System,
};
enum class State : u32 {
NonInitialized,
Initialized,
};
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unaviable,
Finalized,
};
enum class ModelType : u32 {
Amiibo,
};
enum class MountTarget : u32 {
Rom,
Ram,
All,
};
enum class AmiiboType : u8 {
Figure,
Card,
Yarn,
};
enum class AmiiboSeries : u8 {
SuperSmashBros,
SuperMario,
ChibiRobo,
YoshiWoollyWorld,
Splatoon,
AnimalCrossing,
EightBitMario,
Skylanders,
Unknown8,
TheLegendOfZelda,
ShovelKnight,
Unknown11,
Kiby,
Pokemon,
MarioSportsSuperstars,
MonsterHunter,
BoxBoy,
Pikmin,
FireEmblem,
Metroid,
Others,
MegaMan,
Diablo
};
using TagUuid = std::array<u8, 10>;
struct TagInfo {
TagUuid uuid;
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
s32 protocol;
u32 tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
struct CommonInfo {
u16 last_write_year;
u8 last_write_month;
u8 last_write_day;
u16 write_counter;
u16 version;
u32 application_area_size;
INSERT_PADDING_BYTES(0x34);
};
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
struct ModelInfo {
u16 character_id;
u8 character_variant;
AmiiboType amiibo_type;
u16 model_number;
AmiiboSeries series;
u8 fixed; // Must be 02
INSERT_PADDING_BYTES(0x4); // Unknown
INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash
INSERT_PADDING_BYTES(0x14); // SHA256-HMAC
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
struct RegisterInfo {
Service::Mii::MiiInfo mii_char_info;
u16 first_write_year;
u8 first_write_month;
u8 first_write_day;
std::array<u8, 11> amiibo_name;
u8 unknown;
INSERT_PADDING_BYTES(0x98);
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
@@ -24,34 +141,131 @@ public:
const char* name);
~Interface() override;
struct ModelInfo {
std::array<u8, 0x8> amiibo_identification_block;
INSERT_PADDING_BYTES(0x38);
struct EncryptedAmiiboFile {
u16 crypto_init; // Must be A5 XX
u16 write_count; // Number of times the amiibo has been written?
INSERT_PADDING_BYTES(0x20); // System crypts
INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash
ModelInfo model_info; // This struct is bigger than documentation
INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
struct AmiiboFile {
std::array<u8, 10> uuid;
INSERT_PADDING_BYTES(0x4a);
ModelInfo model_info;
struct NTAG215Password {
u32 PWD; // Password to allow write access
u16 PACK; // Password acknowledge reply
u16 RFUI; // Reserved for future use
};
static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
struct NTAG215File {
TagUuid uuid; // Unique serial number
u16 lock_bytes; // Set defined pages as read only
u32 compability_container; // Defines available memory
EncryptedAmiiboFile user_memory; // Writable data
u32 dynamic_lock; // Dynamic lock
u32 CFG0; // Defines memory protected by password
u32 CFG1; // Defines number of verification attempts
NTAG215Password password; // Password data
};
static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
void CreateUserInterface(Kernel::HLERequestContext& ctx);
bool LoadAmiibo(const std::vector<u8>& buffer);
Kernel::KReadableEvent& GetNFCEvent();
const AmiiboFile& GetAmiiboBuffer() const;
void CloseAmiibo();
void Initialize();
void Finalize();
ResultCode StartDetection(s32 protocol_);
ResultCode StopDetection();
ResultCode Mount();
ResultCode Unmount();
ResultCode GetTagInfo(TagInfo& tag_info) const;
ResultCode GetCommonInfo(CommonInfo& common_info) const;
ResultCode GetModelInfo(ModelInfo& model_info) const;
ResultCode GetRegisterInfo(RegisterInfo& register_info) const;
ResultCode OpenApplicationArea(u32 access_id);
ResultCode GetApplicationArea(std::vector<u8>& data) const;
ResultCode SetApplicationArea(const std::vector<u8>& data);
ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
u64 GetHandle() const;
DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
protected:
std::shared_ptr<Module> module;
private:
/// Validates that the amiibo file is not corrupted
bool IsAmiiboValid() const;
bool AmiiboApplicationDataExist(u32 access_id) const;
std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
/// return password needed to allow write access to protected memory
u32 GetTagPassword(const TagUuid& uuid) const;
const Core::HID::NpadIdType npad_id;
DeviceState device_state{DeviceState::Unaviable};
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* nfc_tag_load;
AmiiboFile amiibo{};
Kernel::KEvent* activate_event;
Kernel::KEvent* deactivate_event;
NTAG215File tag_data{};
s32 protocol;
bool is_application_area_initialized{};
u32 application_area_id;
std::vector<u8> application_area_data;
};
};
class IUser final : public ServiceFramework<IUser> {
public:
explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
private:
void Initialize(Kernel::HLERequestContext& ctx);
void Finalize(Kernel::HLERequestContext& ctx);
void ListDevices(Kernel::HLERequestContext& ctx);
void StartDetection(Kernel::HLERequestContext& ctx);
void StopDetection(Kernel::HLERequestContext& ctx);
void Mount(Kernel::HLERequestContext& ctx);
void Unmount(Kernel::HLERequestContext& ctx);
void OpenApplicationArea(Kernel::HLERequestContext& ctx);
void GetApplicationArea(Kernel::HLERequestContext& ctx);
void SetApplicationArea(Kernel::HLERequestContext& ctx);
void CreateApplicationArea(Kernel::HLERequestContext& ctx);
void GetTagInfo(Kernel::HLERequestContext& ctx);
void GetRegisterInfo(Kernel::HLERequestContext& ctx);
void GetCommonInfo(Kernel::HLERequestContext& ctx);
void GetModelInfo(Kernel::HLERequestContext& ctx);
void AttachActivateEvent(Kernel::HLERequestContext& ctx);
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
void GetState(Kernel::HLERequestContext& ctx);
void GetDeviceState(Kernel::HLERequestContext& ctx);
void GetNpadId(Kernel::HLERequestContext& ctx);
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;
// TODO(german77): We should have a vector of interfaces
Module::Interface& nfp_interface;
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::NFP

View File

@@ -59,7 +59,7 @@ void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequ
LOG_WARNING(Service_NS,
"(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
unknown, application_id, user_account_uid.Format());
unknown, application_id, user_account_uid.RawString());
IPC::ResponseBuilder rb{ctx, 12};
rb.Push(ResultSuccess);

View File

@@ -91,6 +91,8 @@ public:
{4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
{5, nullptr, "HookToCreateApplicationProgress"},
{6, nullptr, "ClearHook"},
{65000, &DebugMonitor::AtmosphereGetProcessInfo, "AtmosphereGetProcessInfo"},
{65001, nullptr, "AtmosphereGetCurrentLimitInfo"},
};
// clang-format on
@@ -125,6 +127,49 @@ private:
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
}
void AtmosphereGetProcessInfo(Kernel::HLERequestContext& ctx) {
// https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/pm/source/impl/pm_process_manager.cpp#L614
// This implementation is incomplete; only a handle to the process is returned.
IPC::RequestParser rp{ctx};
const auto pid = rp.PopRaw<u64>();
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) {
return proc->GetProcessID() == pid;
});
if (!process.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultProcessNotFound);
return;
}
struct ProgramLocation {
u64 program_id;
u8 storage_id;
};
static_assert(sizeof(ProgramLocation) == 0x10, "ProgramLocation has an invalid size");
struct OverrideStatus {
u64 keys_held;
u64 flags;
};
static_assert(sizeof(OverrideStatus) == 0x10, "OverrideStatus has an invalid size");
OverrideStatus override_status{};
ProgramLocation program_location{
.program_id = (*process)->GetProgramID(),
.storage_id = 0,
};
IPC::ResponseBuilder rb{ctx, 10, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(*process);
rb.PushRaw(program_location);
rb.PushRaw(override_status);
}
const Kernel::KernelCore& kernel;
};

View File

@@ -39,6 +39,7 @@
#include "core/hle/service/mig/mig.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mm/mm_u.h"
#include "core/hle/service/mnpp/mnpp_app.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfp/nfp.h"
@@ -265,6 +266,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Migration::InstallInterfaces(*sm, system);
Mii::InstallInterfaces(*sm, system);
MM::InstallInterfaces(*sm, system);
MNPP::InstallInterfaces(*sm, system);
NCM::InstallInterfaces(*sm, system);
NFC::InstallInterfaces(*sm, system);
NFP::InstallInterfaces(*sm, system);

View File

@@ -36,7 +36,7 @@ struct SteadyClockTimePoint {
}
static SteadyClockTimePoint GetRandom() {
return {0, Common::UUID::Generate()};
return {0, Common::UUID::MakeRandom()};
}
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");

View File

@@ -49,7 +49,7 @@ public:
}
private:
Common::UUID clock_source_id{Common::UUID::Generate()};
Common::UUID clock_source_id{Common::UUID::MakeRandom()};
bool is_initialized{};
};

View File

@@ -45,7 +45,7 @@ struct TimeManager::Impl final {
time_zone_content_manager{system} {
const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {});
SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
Clock::SystemClockContext clock_context{};

View File

@@ -28,10 +28,10 @@ class Layer;
/// Represents a single display type
class Display {
public:
YUZU_NON_COPYABLE(Display);
YUZU_NON_MOVEABLE(Display);
public:
/// Constructs a display with a given unique ID and name.
///
/// @param id The unique ID for this display.

View File

@@ -11,6 +11,7 @@
#include <utility>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs.h"
@@ -139,8 +140,11 @@ std::string GetResultStatusString(ResultStatus status);
std::ostream& operator<<(std::ostream& os, ResultStatus status);
/// Interface for loading an application
class AppLoader : NonCopyable {
class AppLoader {
public:
YUZU_NON_COPYABLE(AppLoader);
YUZU_NON_MOVEABLE(AppLoader);
struct LoadParameters {
s32 main_thread_priority;
u64 main_thread_stack_size;

View File

@@ -248,7 +248,7 @@ bool GCAdapter::Setup() {
std::size_t port = 0;
for (GCController& pad : pads) {
pad.identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = port++,
.pad = 0,
};

View File

@@ -9,17 +9,17 @@
namespace InputCommon {
constexpr PadIdentifier key_identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};
constexpr PadIdentifier keyboard_key_identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 1,
.pad = 0,
};
constexpr PadIdentifier keyboard_modifier_identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 1,
.pad = 1,
};

View File

@@ -20,7 +20,7 @@ constexpr int motion_wheel_y = 4;
constexpr int touch_axis_x = 10;
constexpr int touch_axis_y = 11;
constexpr PadIdentifier identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};

View File

@@ -181,11 +181,10 @@ public:
case SDL_JOYSTICK_POWER_EMPTY:
return BatteryLevel::Empty;
case SDL_JOYSTICK_POWER_LOW:
return BatteryLevel::Critical;
case SDL_JOYSTICK_POWER_MEDIUM:
return BatteryLevel::Low;
case SDL_JOYSTICK_POWER_FULL:
case SDL_JOYSTICK_POWER_MEDIUM:
return BatteryLevel::Medium;
case SDL_JOYSTICK_POWER_FULL:
case SDL_JOYSTICK_POWER_MAX:
return BatteryLevel::Full;
case SDL_JOYSTICK_POWER_UNKNOWN:
@@ -503,7 +502,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
Common::Input::VibrationError SDLDriver::SetRumble(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
const auto joystick =
GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port));
GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
const auto process_amplitude_exp = [](f32 amplitude, f32 factor) {
return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF;
};
@@ -600,7 +599,7 @@ Common::ParamPackage SDLDriver::BuildParamPackageForAnalog(PadIdentifier identif
Common::ParamPackage params;
params.Set("engine", GetEngineName());
params.Set("port", static_cast<int>(identifier.port));
params.Set("guid", identifier.guid.Format());
params.Set("guid", identifier.guid.RawString());
params.Set("axis_x", axis_x);
params.Set("axis_y", axis_y);
params.Set("offset_x", offset_x);
@@ -812,7 +811,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
PreSetAxis(identifier, binding_left_x.value.axis);
PreSetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
mapping.insert_or_assign(Settings::NativeAnalog::LStick,
BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
binding_left_y.value.axis,
@@ -823,7 +822,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
PreSetAxis(identifier, binding_left_x.value.axis);
PreSetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
mapping.insert_or_assign(Settings::NativeAnalog::LStick,
BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
binding_left_y.value.axis,
@@ -838,7 +837,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
PreSetAxis(identifier, binding_right_x.value.axis);
PreSetAxis(identifier, binding_right_y.value.axis);
const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis);
const auto right_offset_y = -GetAxis(identifier, binding_right_y.value.axis);
const auto right_offset_y = GetAxis(identifier, binding_right_y.value.axis);
mapping.insert_or_assign(Settings::NativeAnalog::RStick,
BuildParamPackageForAnalog(identifier, binding_right_x.value.axis,
binding_right_y.value.axis, right_offset_x,

View File

@@ -8,7 +8,7 @@
namespace InputCommon {
constexpr PadIdentifier identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};

View File

@@ -353,7 +353,7 @@ PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const {
Common::UUID UDPClient::GetHostUUID(const std::string& host) const {
const auto ip = boost::asio::ip::make_address_v4(host);
const auto hex_host = fmt::format("{:06x}", ip.to_uint());
const auto hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint());
return Common::UUID{hex_host};
}
@@ -385,7 +385,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
Common::ParamPackage identifier{};
identifier.Set("engine", GetEngineName());
identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad));
identifier.Set("guid", pad_identifier.guid.Format());
identifier.Set("guid", pad_identifier.guid.RawString());
identifier.Set("port", static_cast<int>(pad_identifier.port));
identifier.Set("pad", static_cast<int>(pad_identifier.pad));
devices.emplace_back(identifier);

View File

@@ -126,7 +126,7 @@ private:
struct ClientConnection {
ClientConnection();
~ClientConnection();
Common::UUID uuid{"7F000001"};
Common::UUID uuid{"00000000-0000-0000-0000-00007F000001"};
std::string host{"127.0.0.1"};
u16 port{26760};
s8 active{-1};

View File

@@ -167,12 +167,34 @@ public:
}
void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
modifier_status = button_callback.button_status.value;
const auto& new_status = button_callback.button_status;
const bool new_button_value = new_status.inverted ? !new_status.value : new_status.value;
modifier_status.toggle = new_status.toggle;
// Update button status with current
if (!modifier_status.toggle) {
modifier_status.locked = false;
if (modifier_status.value != new_button_value) {
modifier_status.value = new_button_value;
}
} else {
// Toggle button and lock status
if (new_button_value && !modifier_status.locked) {
modifier_status.locked = true;
modifier_status.value = !modifier_status.value;
}
// Unlock button ready for next press
if (!new_button_value && modifier_status.locked) {
modifier_status.locked = false;
}
}
UpdateStatus();
}
void UpdateStatus() {
const float coef = modifier_status ? modifier_scale : 1.0f;
const float coef = modifier_status.value ? modifier_scale : 1.0f;
bool r = right_status;
bool l = left_status;
@@ -266,7 +288,7 @@ public:
if (down_status) {
--y;
}
const float coef = modifier_status ? modifier_scale : 1.0f;
const float coef = modifier_status.value ? modifier_scale : 1.0f;
status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
return status;
@@ -287,9 +309,9 @@ private:
bool down_status{};
bool left_status{};
bool right_status{};
bool modifier_status{};
float last_x_axis_value{};
float last_y_axis_value{};
Common::Input::ButtonStatus modifier_status{};
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
std::chrono::time_point<std::chrono::steady_clock> last_update;
};

View File

@@ -4,7 +4,6 @@
#include <algorithm>
#include "common/settings.h"
#include "core/frontend/framebuffer_layout.h"
#include "input_common/helpers/touch_from_buttons.h"
namespace InputCommon {

View File

@@ -96,7 +96,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return false;
}
@@ -113,7 +113,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return false;
}
@@ -130,7 +130,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return 0.0f;
}
@@ -147,7 +147,7 @@ BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return BatteryLevel::Charging;
}
@@ -159,7 +159,7 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return {};
}

View File

@@ -16,7 +16,7 @@
// Pad Identifier of data source
struct PadIdentifier {
Common::UUID guid{Common::INVALID_UUID};
Common::UUID guid{};
std::size_t port{};
std::size_t pad{};
@@ -59,7 +59,7 @@ namespace std {
template <>
struct hash<PadIdentifier> {
size_t operator()(const PadIdentifier& pad_id) const noexcept {
u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0];
u64 hash_value = pad_id.guid.Hash();
hash_value ^= (static_cast<u64>(pad_id.port) << 32);
hash_value ^= static_cast<u64>(pad_id.pad);
return static_cast<size_t>(hash_value);

View File

@@ -57,7 +57,7 @@ void MappingFactory::RegisterButton(const MappingData& data) {
Common::ParamPackage new_input;
new_input.Set("engine", data.engine);
if (data.pad.guid.IsValid()) {
new_input.Set("guid", data.pad.guid.Format());
new_input.Set("guid", data.pad.guid.RawString());
}
new_input.Set("port", static_cast<int>(data.pad.port));
new_input.Set("pad", static_cast<int>(data.pad.pad));
@@ -93,7 +93,7 @@ void MappingFactory::RegisterStick(const MappingData& data) {
Common::ParamPackage new_input;
new_input.Set("engine", data.engine);
if (data.pad.guid.IsValid()) {
new_input.Set("guid", data.pad.guid.Format());
new_input.Set("guid", data.pad.guid.RawString());
}
new_input.Set("port", static_cast<int>(data.pad.port));
new_input.Set("pad", static_cast<int>(data.pad.pad));
@@ -138,7 +138,7 @@ void MappingFactory::RegisterMotion(const MappingData& data) {
Common::ParamPackage new_input;
new_input.Set("engine", data.engine);
if (data.pad.guid.IsValid()) {
new_input.Set("guid", data.pad.guid.Format());
new_input.Set("guid", data.pad.guid.RawString());
}
new_input.Set("port", static_cast<int>(data.pad.port));
new_input.Set("pad", static_cast<int>(data.pad.pad));

View File

@@ -181,7 +181,7 @@ public:
.raw_value = input_engine->GetAxis(identifier, axis_y),
.properties = properties_y,
};
// This is a workaround too keep compatibility with old yuzu versions. Vertical axis is
// This is a workaround to keep compatibility with old yuzu versions. Vertical axis is
// inverted on SDL compared to Nintendo
if (invert_axis_y) {
status.y.raw_value = -status.y.raw_value;

View File

@@ -372,6 +372,8 @@ void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 poin
ScalarU32 value);
void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
Register value);
void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
Register value);
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -412,6 +414,24 @@ void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& b
ScalarU32 offset, Register value);
void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, Register value);
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, ScalarF32 value);
void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -448,6 +468,17 @@ void EmitGlobalAtomicAnd64(EmitContext& ctx);
void EmitGlobalAtomicOr64(EmitContext& ctx);
void EmitGlobalAtomicXor64(EmitContext& ctx);
void EmitGlobalAtomicExchange64(EmitContext& ctx);
void EmitGlobalAtomicIAdd32x2(EmitContext& ctx);
void EmitGlobalAtomicSMin32x2(EmitContext& ctx);
void EmitGlobalAtomicUMin32x2(EmitContext& ctx);
void EmitGlobalAtomicSMax32x2(EmitContext& ctx);
void EmitGlobalAtomicUMax32x2(EmitContext& ctx);
void EmitGlobalAtomicInc32x2(EmitContext& ctx);
void EmitGlobalAtomicDec32x2(EmitContext& ctx);
void EmitGlobalAtomicAnd32x2(EmitContext& ctx);
void EmitGlobalAtomicOr32x2(EmitContext& ctx);
void EmitGlobalAtomicXor32x2(EmitContext& ctx);
void EmitGlobalAtomicExchange32x2(EmitContext& ctx);
void EmitGlobalAtomicAddF32(EmitContext& ctx);
void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
void EmitGlobalAtomicAddF32x2(EmitContext& ctx);

View File

@@ -311,6 +311,13 @@ void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 poin
ctx.LongAdd("ATOMS.EXCH.U64 {}.x,{},shared_mem[{}];", inst, value, pointer_offset);
}
void EmitSharedAtomicExchange32x2([[maybe_unused]] EmitContext& ctx,
[[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] ScalarU32 pointer_offset,
[[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, ScalarU32 value) {
Atom(ctx, inst, binding, offset, value, "ADD", "U32");
@@ -411,6 +418,62 @@ void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Val
Atom(ctx, inst, binding, offset, value, "EXCH", "U64");
}
void EmitStorageAtomicIAdd32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicSMin32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicUMin32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicSMax32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicUMax32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicAnd32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicOr32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicXor32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicExchange32x2([[maybe_unused]] EmitContext& ctx,
[[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] const IR::Value& binding,
[[maybe_unused]] ScalarU32 offset,
[[maybe_unused]] Register value) {
throw NotImplementedException("GLASM instruction");
}
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
ScalarU32 offset, ScalarF32 value) {
Atom(ctx, inst, binding, offset, value, "ADD", "F32");
@@ -537,6 +600,50 @@ void EmitGlobalAtomicExchange64(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicIAdd32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicSMin32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicUMin32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicSMax32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicUMax32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicInc32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicDec32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicAnd32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicOr32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicXor32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicExchange32x2(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}
void EmitGlobalAtomicAddF32(EmitContext&) {
throw NotImplementedException("GLASM instruction");
}

View File

@@ -105,6 +105,13 @@ void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_vi
pointer_offset, value, pointer_offset, value);
}
void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, pointer_offset, pointer_offset);
ctx.Add("smem[{}>>2]={}.x;smem[({}+4)>>2]={}.y;", pointer_offset, value, pointer_offset, value);
}
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
@@ -265,6 +272,97 @@ void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Val
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
ctx.var_alloc.Consume(offset));
ctx.Add("{}_ssbo{}[{}>>2]+={}.x;{}_ssbo{}[({}>>2)+1]+={}.y;", ctx.stage_name, binding.U32(),
ctx.var_alloc.Consume(offset), value, ctx.stage_name, binding.U32(),
ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
ctx.AddU32x2("{}=ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
ctx.var_alloc.Consume(offset));
ctx.Add("for(int "
"i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=uint(min(int({}_ssbo{}[({}>>2)+i]),int({}[i])));}}",
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
ctx.var_alloc.Consume(offset));
ctx.Add("for(int i=0;i<2;++i){{ "
"{}_ssbo{}[({}>>2)+i]=min({}_ssbo{}[({}>>2)+i],{}[i]);}}",
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
ctx.AddU32x2("{}=ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
ctx.var_alloc.Consume(offset));
ctx.Add("for(int "
"i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=uint(max(int({}_ssbo{}[({}>>2)+i]),int({}[i])));}}",
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
ctx.var_alloc.Consume(offset));
ctx.Add("for(int i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=max({}_ssbo{}[({}>>2)+i],{}[i]);}}",
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
ctx.AddU32x2("{}=uvec2(atomicAnd({}_ssbo{}[{}>>2],{}.x),atomicAnd({}_ssbo{}[({}>>2)+1],{}.y));",
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
ctx.AddU32x2("{}=uvec2(atomicOr({}_ssbo{}[{}>>2],{}.x),atomicOr({}_ssbo{}[({}>>2)+1],{}.y));",
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
ctx.AddU32x2("{}=uvec2(atomicXor({}_ssbo{}[{}>>2],{}.x),atomicXor({}_ssbo{}[({}>>2)+1],{}.y));",
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
ctx.AddU32x2("{}=uvec2(atomicExchange({}_ssbo{}[{}>>2],{}.x),atomicExchange({}_ssbo{}[({}>>2)+"
"1],{}.y));",
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
}
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value) {
SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd");
@@ -388,6 +486,50 @@ void EmitGlobalAtomicExchange64(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicIAdd32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicSMin32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicUMin32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicSMax32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicUMax32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicInc32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicDec32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicAnd32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicOr32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicXor32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicExchange32x2(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}
void EmitGlobalAtomicAddF32(EmitContext&) {
throw NotImplementedException("GLSL Instrucion");
}

View File

@@ -442,6 +442,8 @@ void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_vi
std::string_view value);
void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
std::string_view value);
void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
std::string_view value);
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -482,6 +484,24 @@ void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& b
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset, std::string_view value);
void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -518,6 +538,17 @@ void EmitGlobalAtomicAnd64(EmitContext& ctx);
void EmitGlobalAtomicOr64(EmitContext& ctx);
void EmitGlobalAtomicXor64(EmitContext& ctx);
void EmitGlobalAtomicExchange64(EmitContext& ctx);
void EmitGlobalAtomicIAdd32x2(EmitContext& ctx);
void EmitGlobalAtomicSMin32x2(EmitContext& ctx);
void EmitGlobalAtomicUMin32x2(EmitContext& ctx);
void EmitGlobalAtomicSMax32x2(EmitContext& ctx);
void EmitGlobalAtomicUMax32x2(EmitContext& ctx);
void EmitGlobalAtomicInc32x2(EmitContext& ctx);
void EmitGlobalAtomicDec32x2(EmitContext& ctx);
void EmitGlobalAtomicAnd32x2(EmitContext& ctx);
void EmitGlobalAtomicOr32x2(EmitContext& ctx);
void EmitGlobalAtomicXor32x2(EmitContext& ctx);
void EmitGlobalAtomicExchange32x2(EmitContext& ctx);
void EmitGlobalAtomicAddF32(EmitContext& ctx);
void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
void EmitGlobalAtomicAddF32x2(EmitContext& ctx);

View File

@@ -82,6 +82,17 @@ Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value&
ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result));
return original_value;
}
Id StorageAtomicU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
binding, offset, sizeof(u32[2]))};
const Id original_value{ctx.OpLoad(ctx.U32[2], pointer)};
const Id result{(ctx.*non_atomic_func)(ctx.U32[2], value, original_value)};
ctx.OpStore(pointer, result);
return original_value;
}
} // Anonymous namespace
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) {
@@ -141,7 +152,7 @@ Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
const auto [scope, semantics]{AtomicArgs(ctx)};
return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value);
}
LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
const Id pointer_1{SharedPointer(ctx, offset, 0)};
const Id pointer_2{SharedPointer(ctx, offset, 1)};
const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)};
@@ -152,6 +163,18 @@ Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2));
}
Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id offset, Id value) {
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
const Id pointer_1{SharedPointer(ctx, offset, 0)};
const Id pointer_2{SharedPointer(ctx, offset, 1)};
const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)};
const Id value_2{ctx.OpLoad(ctx.U32[1], pointer_2)};
const Id new_vector{ctx.OpBitcast(ctx.U32[2], value)};
ctx.OpStore(pointer_1, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 0U));
ctx.OpStore(pointer_2, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 1U));
return ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2);
}
Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd);
@@ -275,6 +298,56 @@ Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const
return original;
}
Id EmitStorageAtomicIAdd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpIAdd);
}
Id EmitStorageAtomicSMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpSMin);
}
Id EmitStorageAtomicUMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpUMin);
}
Id EmitStorageAtomicSMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpSMax);
}
Id EmitStorageAtomicUMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpUMax);
}
Id EmitStorageAtomicAnd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseAnd);
}
Id EmitStorageAtomicOr32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseOr);
}
Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseXor);
}
Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
const IR::Value& offset, Id value) {
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
binding, offset, sizeof(u32[2]))};
const Id original{ctx.OpLoad(ctx.U32[2], pointer)};
ctx.OpStore(pointer, value);
return original;
}
Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
const Id ssbo{ctx.ssbos[binding.U32()].U32};
@@ -418,6 +491,50 @@ Id EmitGlobalAtomicExchange64(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicIAdd32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicSMin32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicUMin32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicSMax32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicUMax32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicInc32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicDec32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicAnd32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicOr32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicXor32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicExchange32x2(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
Id EmitGlobalAtomicAddF32(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}

View File

@@ -335,6 +335,7 @@ Id EmitSharedAtomicOr32(EmitContext& ctx, Id pointer_offset, Id value);
Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value);
Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value);
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value);
Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id pointer_offset, Id value);
Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
@@ -375,6 +376,24 @@ Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::
Id value);
Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicIAdd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicSMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicUMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicSMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicUMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicAnd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicOr32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
const IR::Value& offset, Id value);
Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value);
Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
@@ -411,6 +430,17 @@ Id EmitGlobalAtomicAnd64(EmitContext& ctx);
Id EmitGlobalAtomicOr64(EmitContext& ctx);
Id EmitGlobalAtomicXor64(EmitContext& ctx);
Id EmitGlobalAtomicExchange64(EmitContext& ctx);
Id EmitGlobalAtomicIAdd32x2(EmitContext& ctx);
Id EmitGlobalAtomicSMin32x2(EmitContext& ctx);
Id EmitGlobalAtomicUMin32x2(EmitContext& ctx);
Id EmitGlobalAtomicSMax32x2(EmitContext& ctx);
Id EmitGlobalAtomicUMax32x2(EmitContext& ctx);
Id EmitGlobalAtomicInc32x2(EmitContext& ctx);
Id EmitGlobalAtomicDec32x2(EmitContext& ctx);
Id EmitGlobalAtomicAnd32x2(EmitContext& ctx);
Id EmitGlobalAtomicOr32x2(EmitContext& ctx);
Id EmitGlobalAtomicXor32x2(EmitContext& ctx);
Id EmitGlobalAtomicExchange32x2(EmitContext& ctx);
Id EmitGlobalAtomicAddF32(EmitContext& ctx);
Id EmitGlobalAtomicAddF16x2(EmitContext& ctx);
Id EmitGlobalAtomicAddF32x2(EmitContext& ctx);

View File

@@ -118,6 +118,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::SharedAtomicXor32:
case Opcode::SharedAtomicExchange32:
case Opcode::SharedAtomicExchange64:
case Opcode::SharedAtomicExchange32x2:
case Opcode::GlobalAtomicIAdd32:
case Opcode::GlobalAtomicSMin32:
case Opcode::GlobalAtomicUMin32:
@@ -138,6 +139,15 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::GlobalAtomicOr64:
case Opcode::GlobalAtomicXor64:
case Opcode::GlobalAtomicExchange64:
case Opcode::GlobalAtomicIAdd32x2:
case Opcode::GlobalAtomicSMin32x2:
case Opcode::GlobalAtomicUMin32x2:
case Opcode::GlobalAtomicSMax32x2:
case Opcode::GlobalAtomicUMax32x2:
case Opcode::GlobalAtomicAnd32x2:
case Opcode::GlobalAtomicOr32x2:
case Opcode::GlobalAtomicXor32x2:
case Opcode::GlobalAtomicExchange32x2:
case Opcode::GlobalAtomicAddF32:
case Opcode::GlobalAtomicAddF16x2:
case Opcode::GlobalAtomicAddF32x2:
@@ -165,6 +175,15 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::StorageAtomicOr64:
case Opcode::StorageAtomicXor64:
case Opcode::StorageAtomicExchange64:
case Opcode::StorageAtomicIAdd32x2:
case Opcode::StorageAtomicSMin32x2:
case Opcode::StorageAtomicUMin32x2:
case Opcode::StorageAtomicSMax32x2:
case Opcode::StorageAtomicUMax32x2:
case Opcode::StorageAtomicAnd32x2:
case Opcode::StorageAtomicOr32x2:
case Opcode::StorageAtomicXor32x2:
case Opcode::StorageAtomicExchange32x2:
case Opcode::StorageAtomicAddF32:
case Opcode::StorageAtomicAddF16x2:
case Opcode::StorageAtomicAddF32x2:

View File

@@ -341,6 +341,7 @@ OPCODE(SharedAtomicOr32, U32, U32,
OPCODE(SharedAtomicXor32, U32, U32, U32, )
OPCODE(SharedAtomicExchange32, U32, U32, U32, )
OPCODE(SharedAtomicExchange64, U64, U32, U64, )
OPCODE(SharedAtomicExchange32x2, U32x2, U32, U32x2, )
OPCODE(GlobalAtomicIAdd32, U32, U64, U32, )
OPCODE(GlobalAtomicSMin32, U32, U64, U32, )
@@ -362,6 +363,15 @@ OPCODE(GlobalAtomicAnd64, U64, U64,
OPCODE(GlobalAtomicOr64, U64, U64, U64, )
OPCODE(GlobalAtomicXor64, U64, U64, U64, )
OPCODE(GlobalAtomicExchange64, U64, U64, U64, )
OPCODE(GlobalAtomicIAdd32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicSMin32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicUMin32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicSMax32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicUMax32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicAnd32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicOr32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicXor32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicExchange32x2, U32x2, U32x2, U32x2, )
OPCODE(GlobalAtomicAddF32, F32, U64, F32, )
OPCODE(GlobalAtomicAddF16x2, U32, U64, F16x2, )
OPCODE(GlobalAtomicAddF32x2, U32, U64, F32x2, )
@@ -390,6 +400,15 @@ OPCODE(StorageAtomicAnd64, U64, U32,
OPCODE(StorageAtomicOr64, U64, U32, U32, U64, )
OPCODE(StorageAtomicXor64, U64, U32, U32, U64, )
OPCODE(StorageAtomicExchange64, U64, U32, U32, U64, )
OPCODE(StorageAtomicIAdd32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicSMin32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicUMin32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicSMax32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicUMax32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicAnd32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicOr32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicXor32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicExchange32x2, U32x2, U32, U32, U32x2, )
OPCODE(StorageAtomicAddF32, F32, U32, U32, F32, )
OPCODE(StorageAtomicAddF16x2, U32, U32, U32, F16x2, )
OPCODE(StorageAtomicAddF32x2, U32, U32, U32, F32x2, )

View File

@@ -360,6 +360,15 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::GlobalAtomicOr64:
case IR::Opcode::GlobalAtomicXor64:
case IR::Opcode::GlobalAtomicExchange64:
case IR::Opcode::GlobalAtomicIAdd32x2:
case IR::Opcode::GlobalAtomicSMin32x2:
case IR::Opcode::GlobalAtomicUMin32x2:
case IR::Opcode::GlobalAtomicSMax32x2:
case IR::Opcode::GlobalAtomicUMax32x2:
case IR::Opcode::GlobalAtomicAnd32x2:
case IR::Opcode::GlobalAtomicOr32x2:
case IR::Opcode::GlobalAtomicXor32x2:
case IR::Opcode::GlobalAtomicExchange32x2:
case IR::Opcode::GlobalAtomicAddF32:
case IR::Opcode::GlobalAtomicAddF16x2:
case IR::Opcode::GlobalAtomicAddF32x2:
@@ -597,6 +606,15 @@ void VisitUsages(Info& info, IR::Inst& inst) {
break;
case IR::Opcode::LoadStorage64:
case IR::Opcode::WriteStorage64:
case IR::Opcode::StorageAtomicIAdd32x2:
case IR::Opcode::StorageAtomicSMin32x2:
case IR::Opcode::StorageAtomicUMin32x2:
case IR::Opcode::StorageAtomicSMax32x2:
case IR::Opcode::StorageAtomicUMax32x2:
case IR::Opcode::StorageAtomicAnd32x2:
case IR::Opcode::StorageAtomicOr32x2:
case IR::Opcode::StorageAtomicXor32x2:
case IR::Opcode::StorageAtomicExchange32x2:
info.used_storage_buffer_types |= IR::Type::U32x2;
break;
case IR::Opcode::LoadStorage128:

View File

@@ -92,6 +92,15 @@ bool IsGlobalMemory(const IR::Inst& inst) {
case IR::Opcode::GlobalAtomicOr64:
case IR::Opcode::GlobalAtomicXor64:
case IR::Opcode::GlobalAtomicExchange64:
case IR::Opcode::GlobalAtomicIAdd32x2:
case IR::Opcode::GlobalAtomicSMin32x2:
case IR::Opcode::GlobalAtomicUMin32x2:
case IR::Opcode::GlobalAtomicSMax32x2:
case IR::Opcode::GlobalAtomicUMax32x2:
case IR::Opcode::GlobalAtomicAnd32x2:
case IR::Opcode::GlobalAtomicOr32x2:
case IR::Opcode::GlobalAtomicXor32x2:
case IR::Opcode::GlobalAtomicExchange32x2:
case IR::Opcode::GlobalAtomicAddF32:
case IR::Opcode::GlobalAtomicAddF16x2:
case IR::Opcode::GlobalAtomicAddF32x2:
@@ -135,6 +144,15 @@ bool IsGlobalMemoryWrite(const IR::Inst& inst) {
case IR::Opcode::GlobalAtomicOr64:
case IR::Opcode::GlobalAtomicXor64:
case IR::Opcode::GlobalAtomicExchange64:
case IR::Opcode::GlobalAtomicIAdd32x2:
case IR::Opcode::GlobalAtomicSMin32x2:
case IR::Opcode::GlobalAtomicUMin32x2:
case IR::Opcode::GlobalAtomicSMax32x2:
case IR::Opcode::GlobalAtomicUMax32x2:
case IR::Opcode::GlobalAtomicAnd32x2:
case IR::Opcode::GlobalAtomicOr32x2:
case IR::Opcode::GlobalAtomicXor32x2:
case IR::Opcode::GlobalAtomicExchange32x2:
case IR::Opcode::GlobalAtomicAddF32:
case IR::Opcode::GlobalAtomicAddF16x2:
case IR::Opcode::GlobalAtomicAddF32x2:
@@ -199,6 +217,8 @@ IR::Opcode GlobalToStorage(IR::Opcode opcode) {
return IR::Opcode::StorageAtomicOr32;
case IR::Opcode::GlobalAtomicXor32:
return IR::Opcode::StorageAtomicXor32;
case IR::Opcode::GlobalAtomicExchange32:
return IR::Opcode::StorageAtomicExchange32;
case IR::Opcode::GlobalAtomicIAdd64:
return IR::Opcode::StorageAtomicIAdd64;
case IR::Opcode::GlobalAtomicSMin64:
@@ -215,10 +235,26 @@ IR::Opcode GlobalToStorage(IR::Opcode opcode) {
return IR::Opcode::StorageAtomicOr64;
case IR::Opcode::GlobalAtomicXor64:
return IR::Opcode::StorageAtomicXor64;
case IR::Opcode::GlobalAtomicExchange32:
return IR::Opcode::StorageAtomicExchange32;
case IR::Opcode::GlobalAtomicExchange64:
return IR::Opcode::StorageAtomicExchange64;
case IR::Opcode::GlobalAtomicIAdd32x2:
return IR::Opcode::StorageAtomicIAdd32x2;
case IR::Opcode::GlobalAtomicSMin32x2:
return IR::Opcode::StorageAtomicSMin32x2;
case IR::Opcode::GlobalAtomicUMin32x2:
return IR::Opcode::StorageAtomicUMin32x2;
case IR::Opcode::GlobalAtomicSMax32x2:
return IR::Opcode::StorageAtomicSMax32x2;
case IR::Opcode::GlobalAtomicUMax32x2:
return IR::Opcode::StorageAtomicUMax32x2;
case IR::Opcode::GlobalAtomicAnd32x2:
return IR::Opcode::StorageAtomicAnd32x2;
case IR::Opcode::GlobalAtomicOr32x2:
return IR::Opcode::StorageAtomicOr32x2;
case IR::Opcode::GlobalAtomicXor32x2:
return IR::Opcode::StorageAtomicXor32x2;
case IR::Opcode::GlobalAtomicExchange32x2:
return IR::Opcode::StorageAtomicExchange32x2;
case IR::Opcode::GlobalAtomicAddF32:
return IR::Opcode::StorageAtomicAddF32;
case IR::Opcode::GlobalAtomicAddF16x2:
@@ -454,6 +490,15 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
case IR::Opcode::GlobalAtomicOr64:
case IR::Opcode::GlobalAtomicXor64:
case IR::Opcode::GlobalAtomicExchange64:
case IR::Opcode::GlobalAtomicIAdd32x2:
case IR::Opcode::GlobalAtomicSMin32x2:
case IR::Opcode::GlobalAtomicUMin32x2:
case IR::Opcode::GlobalAtomicSMax32x2:
case IR::Opcode::GlobalAtomicUMax32x2:
case IR::Opcode::GlobalAtomicAnd32x2:
case IR::Opcode::GlobalAtomicOr32x2:
case IR::Opcode::GlobalAtomicXor32x2:
case IR::Opcode::GlobalAtomicExchange32x2:
case IR::Opcode::GlobalAtomicAddF32:
case IR::Opcode::GlobalAtomicAddF16x2:
case IR::Opcode::GlobalAtomicAddF32x2:

View File

@@ -199,6 +199,26 @@ void Lower(IR::Block& block, IR::Inst& inst) {
return ShiftRightLogical64To32(block, inst);
case IR::Opcode::ShiftRightArithmetic64:
return ShiftRightArithmetic64To32(block, inst);
case IR::Opcode::SharedAtomicExchange64:
return inst.ReplaceOpcode(IR::Opcode::SharedAtomicExchange32x2);
case IR::Opcode::GlobalAtomicIAdd64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicIAdd32x2);
case IR::Opcode::GlobalAtomicSMin64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMin32x2);
case IR::Opcode::GlobalAtomicUMin64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMin32x2);
case IR::Opcode::GlobalAtomicSMax64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMax32x2);
case IR::Opcode::GlobalAtomicUMax64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMax32x2);
case IR::Opcode::GlobalAtomicAnd64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicAnd32x2);
case IR::Opcode::GlobalAtomicOr64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicOr32x2);
case IR::Opcode::GlobalAtomicXor64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicXor32x2);
case IR::Opcode::GlobalAtomicExchange64:
return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicExchange32x2);
default:
break;
}

View File

@@ -5,9 +5,10 @@
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <optional>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/frontend/emu_window.h"
#include "video_core/gpu.h"
@@ -28,8 +29,11 @@ struct RendererSettings {
Layout::FramebufferLayout screenshot_framebuffer_layout;
};
class RendererBase : NonCopyable {
class RendererBase {
public:
YUZU_NON_COPYABLE(RendererBase);
YUZU_NON_MOVEABLE(RendererBase);
explicit RendererBase(Core::Frontend::EmuWindow& window,
std::unique_ptr<Core::Frontend::GraphicsContext> context);
virtual ~RendererBase();

View File

@@ -7,12 +7,14 @@
#include <string_view>
#include <utility>
#include <glad/glad.h>
#include "common/common_types.h"
#include "common/common_funcs.h"
namespace OpenGL {
class OGLRenderbuffer : private NonCopyable {
class OGLRenderbuffer final {
public:
YUZU_NON_COPYABLE(OGLRenderbuffer);
OGLRenderbuffer() = default;
OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -36,8 +38,10 @@ public:
GLuint handle = 0;
};
class OGLTexture : private NonCopyable {
class OGLTexture final {
public:
YUZU_NON_COPYABLE(OGLTexture);
OGLTexture() = default;
OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -61,8 +65,10 @@ public:
GLuint handle = 0;
};
class OGLTextureView : private NonCopyable {
class OGLTextureView final {
public:
YUZU_NON_COPYABLE(OGLTextureView);
OGLTextureView() = default;
OGLTextureView(OGLTextureView&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -86,8 +92,10 @@ public:
GLuint handle = 0;
};
class OGLSampler : private NonCopyable {
class OGLSampler final {
public:
YUZU_NON_COPYABLE(OGLSampler);
OGLSampler() = default;
OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -111,8 +119,10 @@ public:
GLuint handle = 0;
};
class OGLShader : private NonCopyable {
class OGLShader final {
public:
YUZU_NON_COPYABLE(OGLShader);
OGLShader() = default;
OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -132,8 +142,10 @@ public:
GLuint handle = 0;
};
class OGLProgram : private NonCopyable {
class OGLProgram final {
public:
YUZU_NON_COPYABLE(OGLProgram);
OGLProgram() = default;
OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -154,8 +166,10 @@ public:
GLuint handle = 0;
};
class OGLAssemblyProgram : private NonCopyable {
class OGLAssemblyProgram final {
public:
YUZU_NON_COPYABLE(OGLAssemblyProgram);
OGLAssemblyProgram() = default;
OGLAssemblyProgram(OGLAssemblyProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -176,8 +190,10 @@ public:
GLuint handle = 0;
};
class OGLPipeline : private NonCopyable {
class OGLPipeline final {
public:
YUZU_NON_COPYABLE(OGLPipeline);
OGLPipeline() = default;
OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {}
@@ -198,8 +214,10 @@ public:
GLuint handle = 0;
};
class OGLBuffer : private NonCopyable {
class OGLBuffer final {
public:
YUZU_NON_COPYABLE(OGLBuffer);
OGLBuffer() = default;
OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -223,8 +241,10 @@ public:
GLuint handle = 0;
};
class OGLSync : private NonCopyable {
class OGLSync final {
public:
YUZU_NON_COPYABLE(OGLSync);
OGLSync() = default;
OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {}
@@ -247,8 +267,10 @@ public:
GLsync handle = 0;
};
class OGLFramebuffer : private NonCopyable {
class OGLFramebuffer final {
public:
YUZU_NON_COPYABLE(OGLFramebuffer);
OGLFramebuffer() = default;
OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -272,8 +294,10 @@ public:
GLuint handle = 0;
};
class OGLQuery : private NonCopyable {
class OGLQuery final {
public:
YUZU_NON_COPYABLE(OGLQuery);
OGLQuery() = default;
OGLQuery(OGLQuery&& o) noexcept : handle(std::exchange(o.handle, 0)) {}

View File

@@ -140,12 +140,12 @@ bool VKScheduler::UpdateRescaling(bool is_rescaling) {
void VKScheduler::WorkerThread(std::stop_token stop_token) {
Common::SetCurrentThreadName("yuzu:VulkanWorker");
do {
if (work_queue.empty()) {
wait_cv.notify_all();
}
std::unique_ptr<CommandChunk> work;
{
std::unique_lock lock{work_mutex};
if (work_queue.empty()) {
wait_cv.notify_all();
}
work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
if (stop_token.stop_requested()) {
continue;

View File

@@ -146,6 +146,7 @@ private:
using FuncType = TypedCommand<T>;
static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large");
recorded_counts++;
command_offset = Common::AlignUp(command_offset, alignof(FuncType));
if (command_offset > sizeof(data) - sizeof(FuncType)) {
return false;
@@ -167,7 +168,7 @@ private:
}
bool Empty() const {
return command_offset == 0;
return recorded_counts == 0;
}
bool HasSubmit() const {
@@ -178,6 +179,7 @@ private:
Command* first = nullptr;
Command* last = nullptr;
size_t recorded_counts = 0;
size_t command_offset = 0;
bool submit = false;
alignas(std::max_align_t) std::array<u8, 0x8000> data{};

View File

@@ -554,10 +554,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
};
}
[[nodiscard]] bool IsFormatFlipped(PixelFormat format) {
[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) {
switch (format) {
case PixelFormat::A1B5G5R5_UNORM:
return true;
case PixelFormat::B5G6R5_UNORM:
return emulate_bgr565;
default:
return false;
}
@@ -1488,7 +1490,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
};
if (!info.IsRenderTarget()) {
swizzle = info.Swizzle();
if (IsFormatFlipped(format)) {
if (IsFormatFlipped(format, device->MustEmulateBGR565())) {
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
}
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {

View File

@@ -39,6 +39,11 @@ constexpr std::array DEPTH16_UNORM_STENCIL8_UINT{
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_UNDEFINED,
};
constexpr std::array B5G6R5_UNORM_PACK16{
VK_FORMAT_R5G6B5_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
};
} // namespace Alternatives
enum class NvidiaArchitecture {
@@ -87,6 +92,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data();
case VK_FORMAT_D16_UNORM_S8_UINT:
return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data();
case VK_FORMAT_B5G6R5_UNORM_PACK16:
return Alternatives::B5G6R5_UNORM_PACK16.data();
default:
return nullptr;
}
@@ -224,9 +231,14 @@ std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) {
return supported_extensions;
}
bool IsExtensionSupported(std::span<const std::string> supported_extensions,
std::string_view extension) {
return std::ranges::find(supported_extensions, extension) != supported_extensions.end();
}
NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
std::span<const std::string> exts) {
if (std::ranges::find(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME) != exts.end()) {
if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) {
VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{};
shading_rate_props.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
@@ -239,7 +251,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
return NvidiaArchitecture::AmpereOrNewer;
}
}
if (std::ranges::find(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME) != exts.end()) {
if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) {
return NvidiaArchitecture::Turing;
}
return NvidiaArchitecture::VoltaOrOlder;
@@ -604,7 +616,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
break;
}
}
if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_MESA_RADV) {
const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
if (ext_extended_dynamic_state && is_radv) {
// Mask driver version variant
const u32 version = (properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
@@ -613,6 +626,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
ext_extended_dynamic_state = false;
}
}
if (ext_vertex_input_dynamic_state && is_radv) {
// TODO(ameerj): Blacklist only offending driver versions
// TODO(ameerj): Confirm if RDNA1 is affected
const bool is_rdna2 =
IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
if (is_rdna2) {
LOG_WARNING(Render_Vulkan,
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
ext_vertex_input_dynamic_state = false;
}
}
sets_per_pool = 64;
const bool is_amd =
@@ -628,7 +652,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
has_broken_cube_compatibility = true;
}
}
const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV;
const bool is_amd_or_radv = is_amd || is_radv;
if (ext_sampler_filter_minmax && is_amd_or_radv) {
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
if (!is_float16_supported) {
@@ -639,6 +663,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
if (ext_vertex_input_dynamic_state && is_intel_windows) {
LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
ext_vertex_input_dynamic_state = false;
@@ -652,6 +677,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
cant_blit_msaa = true;
}
if (is_intel_anv) {
LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format");
must_emulate_bgr565 = true;
}
supports_d24_depth =
IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,

View File

@@ -354,6 +354,10 @@ public:
return cant_blit_msaa;
}
bool MustEmulateBGR565() const {
return must_emulate_bgr565;
}
private:
/// Checks if the physical device is suitable.
void CheckSuitability(bool requires_swapchain) const;
@@ -448,6 +452,7 @@ private:
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
// Telemetry parameters
std::string vendor_name; ///< Device's driver name.

View File

@@ -257,7 +257,7 @@ void QtControllerSelectorDialog::LoadConfiguration() {
}
void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
ConfigureVibration dialog(this);
ConfigureVibration dialog(this, system.HIDCore());
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint);

View File

@@ -23,13 +23,13 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
return QtProfileSelectionDialog::tr(
"%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
"00112233-4455-6677-8899-AABBCCDDEEFF))")
.arg(username, QString::fromStdString(uuid.FormatSwitch()));
.arg(username, QString::fromStdString(uuid.FormattedString()));
}
QString GetImagePath(Common::UUID uuid) {
const auto path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch());
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
return QString::fromStdString(Common::FS::PathToUTF8String(path));
}

View File

@@ -65,23 +65,25 @@ const std::array<int, 2> Config::default_stick_mod = {
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 20> Config::default_hotkeys{{
const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
{QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
@@ -767,6 +769,7 @@ void Config::ReadUIValues() {
ReadBasicSetting(UISettings::values.callout_flags);
ReadBasicSetting(UISettings::values.show_console);
ReadBasicSetting(UISettings::values.pause_when_in_background);
ReadBasicSetting(UISettings::values.mute_when_in_background);
ReadBasicSetting(UISettings::values.hide_mouse);
qt_config->endGroup();
@@ -1295,6 +1298,7 @@ void Config::SaveUIValues() {
WriteBasicSetting(UISettings::values.callout_flags);
WriteBasicSetting(UISettings::values.show_console);
WriteBasicSetting(UISettings::values.pause_when_in_background);
WriteBasicSetting(UISettings::values.mute_when_in_background);
WriteBasicSetting(UISettings::values.hide_mouse);
qt_config->endGroup();

View File

@@ -46,7 +46,7 @@ public:
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
static const std::array<UISettings::Shortcut, 20> default_hotkeys;
static const std::array<UISettings::Shortcut, 22> default_hotkeys;
static constexpr UISettings::Theme default_theme{
#ifdef _WIN32

View File

@@ -46,6 +46,7 @@ void ConfigureGeneral::SetConfiguration() {
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue());
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue());
@@ -95,6 +96,7 @@ void ConfigureGeneral::ApplyConfiguration() {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked();
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
Settings::values.fps_cap.SetValue(ui->fps_cap->value());

View File

@@ -163,6 +163,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_background_mute">
<property name="text">
<string>Mute audio when in background</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_hide_mouse">
<property name="text">

View File

@@ -164,7 +164,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
});
connect(ui->vibrationButton, &QPushButton::clicked,
[this] { CallConfigureDialog<ConfigureVibration>(*this); });
[this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);

View File

@@ -488,6 +488,32 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
emulated_controller->SetStickParam(analog_id, {});
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
});
context_menu.addAction(tr("Center axis"), [&] {
const auto stick_value =
emulated_controller->GetSticksValues()[analog_id];
const float offset_x = stick_value.x.properties.offset;
const float offset_y = stick_value.y.properties.offset;
float raw_value_x = stick_value.x.raw_value;
float raw_value_y = stick_value.y.raw_value;
// See Core::HID::SanitizeStick() to obtain the original raw axis value
if (std::abs(offset_x) < 0.5f) {
if (raw_value_x > 0) {
raw_value_x *= 1 + offset_x;
} else {
raw_value_x *= 1 - offset_x;
}
}
if (std::abs(offset_x) < 0.5f) {
if (raw_value_y > 0) {
raw_value_y *= 1 + offset_y;
} else {
raw_value_y *= 1 - offset_y;
}
}
param.Set("offset_x", -raw_value_x + offset_x);
param.Set("offset_y", -raw_value_y + offset_y);
emulated_controller->SetStickParam(analog_id, param);
});
context_menu.addAction(tr("Invert axis"), [&] {
if (sub_button_id == 2 || sub_button_id == 3) {
const bool invert_value = param.Get("invert_x", "+") == "-";
@@ -1306,6 +1332,9 @@ void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::size_t button_id,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::InputType type) {
if (timeout_timer->isActive()) {
return;
}
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
button->setText(tr("Shake!"));
} else {

View File

@@ -70,7 +70,6 @@ void PlayerControlPreview::UpdateColors() {
colors.slider_arrow = QColor(14, 15, 18);
colors.font2 = QColor(255, 255, 255);
colors.indicator = QColor(170, 238, 255);
colors.indicator2 = QColor(100, 255, 100);
colors.deadzone = QColor(204, 136, 136);
colors.slider_button = colors.button;
}
@@ -88,7 +87,6 @@ void PlayerControlPreview::UpdateColors() {
colors.slider_arrow = QColor(65, 68, 73);
colors.font2 = QColor(0, 0, 0);
colors.indicator = QColor(0, 0, 200);
colors.indicator2 = QColor(0, 150, 0);
colors.deadzone = QColor(170, 0, 0);
colors.slider_button = QColor(153, 149, 149);
}
@@ -101,6 +99,8 @@ void PlayerControlPreview::UpdateColors() {
colors.font = QColor(255, 255, 255);
colors.led_on = QColor(255, 255, 0);
colors.led_off = QColor(170, 238, 255);
colors.indicator2 = QColor(59, 165, 93);
colors.charging = QColor(250, 168, 26);
colors.left = colors.primary;
colors.right = colors.primary;
@@ -357,7 +357,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
DrawCircle(p, center + QPoint(26, 71), 5);
// Draw battery
DrawBattery(p, center + QPoint(-170, -140),
DrawBattery(p, center + QPoint(-160, -140),
battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
}
@@ -484,7 +484,7 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5);
// Draw battery
DrawBattery(p, center + QPoint(110, -140),
DrawBattery(p, center + QPoint(120, -140),
battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
}
@@ -621,9 +621,9 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f);
// Draw battery
DrawBattery(p, center + QPoint(-100, -160),
DrawBattery(p, center + QPoint(-200, -10),
battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
DrawBattery(p, center + QPoint(40, -160),
DrawBattery(p, center + QPoint(160, -10),
battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
}
@@ -694,12 +694,12 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
// ZL and ZR buttons
p.setPen(colors.outline);
DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]);
DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]);
DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]);
DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]);
p.setPen(colors.transparent);
p.setBrush(colors.font);
DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f);
DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f);
DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f);
DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f);
// Minus and Plus button
p.setPen(colors.outline);
@@ -725,9 +725,9 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f);
// Draw battery
DrawBattery(p, center + QPoint(-200, 110),
DrawBattery(p, center + QPoint(-188, 95),
battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
DrawBattery(p, center + QPoint(130, 110),
DrawBattery(p, center + QPoint(150, 95),
battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
}
@@ -781,12 +781,12 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
// ZL and ZR buttons
p.setPen(colors.outline);
DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]);
DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]);
DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]);
DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]);
p.setPen(colors.transparent);
p.setBrush(colors.font);
DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f);
DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f);
DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f);
DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f);
// Minus and Plus buttons
p.setPen(colors.outline);
@@ -818,7 +818,7 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f);
// Draw battery
DrawBattery(p, center + QPoint(-30, -160),
DrawBattery(p, center + QPoint(-20, -160),
battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
}
@@ -875,7 +875,7 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8);
// Draw battery
DrawBattery(p, center + QPoint(-30, -165),
DrawBattery(p, center + QPoint(-20, 110),
battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
}
@@ -1030,6 +1030,10 @@ constexpr std::array<float, 30 * 2> symbol_c = {
-2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f,
};
constexpr std::array<float, 6 * 2> symbol_charging = {
6.5f, -1.0f, 1.0f, -1.0f, 1.0f, -3.0f, -6.5f, 1.0f, -1.0f, 1.0f, -1.0f, 3.0f,
};
constexpr std::array<float, 12 * 2> house = {
-1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f,
0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f,
@@ -2674,36 +2678,43 @@ void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
if (battery == Common::Input::BatteryLevel::None) {
return;
}
p.setPen(colors.outline);
// Draw outline
p.setPen(QPen(colors.button, 5));
p.setBrush(colors.transparent);
p.drawRect(center.x(), center.y(), 56, 20);
p.drawRect(center.x() + 56, center.y() + 6, 3, 8);
p.setBrush(colors.deadzone);
p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2);
p.setPen(QPen(colors.button, 3));
p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7);
// Draw Battery shape
p.setPen(QPen(colors.indicator2, 3));
p.setBrush(colors.transparent);
p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2);
p.setPen(QPen(colors.indicator2, 1));
p.setBrush(colors.indicator2);
p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7);
switch (battery) {
case Common::Input::BatteryLevel::Charging:
p.setBrush(colors.indicator2);
p.drawText(center + QPoint(2, 14), tr("Charging"));
p.drawRect(center.x(), center.y(), 34, 16);
p.setPen(colors.slider);
p.setBrush(colors.charging);
DrawSymbol(p, center + QPointF(17.0f, 8.0f), Symbol::Charging, 2.1f);
break;
case Common::Input::BatteryLevel::Full:
p.drawRect(center.x() + 42, center.y(), 14, 20);
p.drawRect(center.x() + 28, center.y(), 14, 20);
p.drawRect(center.x() + 14, center.y(), 14, 20);
p.drawRect(center.x(), center.y(), 14, 20);
p.drawRect(center.x(), center.y(), 34, 16);
break;
case Common::Input::BatteryLevel::Medium:
p.drawRect(center.x() + 28, center.y(), 14, 20);
p.drawRect(center.x() + 14, center.y(), 14, 20);
p.drawRect(center.x(), center.y(), 14, 20);
p.drawRect(center.x(), center.y(), 25, 16);
break;
case Common::Input::BatteryLevel::Low:
p.drawRect(center.x() + 14, center.y(), 14, 20);
p.drawRect(center.x(), center.y(), 14, 20);
p.drawRect(center.x(), center.y(), 17, 16);
break;
case Common::Input::BatteryLevel::Critical:
p.drawRect(center.x(), center.y(), 14, 20);
p.drawRect(center.x(), center.y(), 6, 16);
break;
case Common::Input::BatteryLevel::Empty:
p.drawRect(center.x(), center.y(), 5, 20);
p.drawRect(center.x(), center.y(), 3, 16);
break;
default:
break;
@@ -2724,6 +2735,7 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol
std::array<QPointF, symbol_sl.size() / 2> sl_icon;
std::array<QPointF, symbol_zr.size() / 2> zr_icon;
std::array<QPointF, symbol_sr.size() / 2> sr_icon;
std::array<QPointF, symbol_charging.size() / 2> charging_icon;
switch (symbol) {
case Symbol::House:
for (std::size_t point = 0; point < house.size() / 2; ++point) {
@@ -2809,6 +2821,13 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol
}
p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size()));
break;
case Symbol::Charging:
for (std::size_t point = 0; point < symbol_charging.size() / 2; ++point) {
charging_icon[point] = center + QPointF(symbol_charging[point * 2] * icon_size,
symbol_charging[point * 2 + 1] * icon_size);
}
p.drawPolygon(charging_icon.data(), static_cast<int>(charging_icon.size()));
break;
}
}

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