summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/hid/emulated_controller.cpp4
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp14
-rw-r--r--src/core/hle/service/nfp/nfp.cpp187
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp224
-rw-r--r--src/core/hle/service/nfp/nfp_device.h12
-rw-r--r--src/core/hle/service/nfp/nfp_interface.cpp (renamed from src/core/hle/service/nfp/nfp_user.cpp)495
-rw-r--r--src/core/hle/service/nfp/nfp_interface.h (renamed from src/core/hle/service/nfp/nfp_user.h)36
-rw-r--r--src/core/hle/service/nfp/nfp_types.h54
-rw-r--r--src/dedicated_room/yuzu_room.cpp16
-rw-r--r--src/input_common/drivers/mouse.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp29
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp30
-rw-r--r--src/shader_recompiler/profile.h4
-rw-r--r--src/video_core/engines/fermi_2d.cpp16
-rw-r--r--src/video_core/engines/maxwell_3d.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_device.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp139
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h48
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp36
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h6
24 files changed, 1217 insertions, 161 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4e677f287..8817a99c9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -570,10 +570,10 @@ add_library(core STATIC
hle/service/nfp/nfp.h
hle/service/nfp/nfp_device.cpp
hle/service/nfp/nfp_device.h
+ hle/service/nfp/nfp_interface.cpp
+ hle/service/nfp/nfp_interface.h
hle/service/nfp/nfp_result.h
hle/service/nfp/nfp_types.h
- hle/service/nfp/nfp_user.cpp
- hle/service/nfp/nfp_user.h
hle/service/ngct/ngct.cpp
hle/service/ngct/ngct.h
hle/service/nifm/nifm.cpp
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index a29c9a6f8..a70f8807c 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -280,6 +280,10 @@ void EmulatedController::LoadVirtualGamepadParams() {
virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
+ virtual_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f);
+ virtual_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f);
+ virtual_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f);
+ virtual_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f);
}
void EmulatedController::ReloadInput() {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index a17c46121..e59de844c 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1807,7 +1807,7 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestCon
}
void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(AM::ResultNoDataInChannel);
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 23b8be993..3e62fa4fc 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -49,12 +49,6 @@ public:
};
// clang-format on
RegisterHandlers(functions);
-
- if (impl->GetSystem()
- .Initialize(device_name, in_params, handle, applet_resource_user_id)
- .IsError()) {
- LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
- }
}
~IAudioOut() override {
@@ -287,6 +281,14 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
in_params, handle, applet_resource_user_id);
+ result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle,
+ applet_resource_user_id);
+ if (result.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
impl->sessions[new_session_id] = audio_out->GetImpl();
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index e262dc2f2..2714f4bea 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -4,11 +4,138 @@
#include "common/logging/log.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfp/nfp.h"
-#include "core/hle/service/nfp/nfp_user.h"
+#include "core/hle/service/nfp/nfp_interface.h"
#include "core/hle/service/server_manager.h"
namespace Service::NFP {
+class IUser final : public Interface {
+public:
+ explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IUser::Initialize, "Initialize"},
+ {1, &IUser::Finalize, "Finalize"},
+ {2, &IUser::ListDevices, "ListDevices"},
+ {3, &IUser::StartDetection, "StartDetection"},
+ {4, &IUser::StopDetection, "StopDetection"},
+ {5, &IUser::Mount, "Mount"},
+ {6, &IUser::Unmount, "Unmount"},
+ {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
+ {8, &IUser::GetApplicationArea, "GetApplicationArea"},
+ {9, &IUser::SetApplicationArea, "SetApplicationArea"},
+ {10, &IUser::Flush, "Flush"},
+ {11, &IUser::Restore, "Restore"},
+ {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
+ {13, &IUser::GetTagInfo, "GetTagInfo"},
+ {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
+ {15, &IUser::GetCommonInfo, "GetCommonInfo"},
+ {16, &IUser::GetModelInfo, "GetModelInfo"},
+ {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
+ {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
+ {19, &IUser::GetState, "GetState"},
+ {20, &IUser::GetDeviceState, "GetDeviceState"},
+ {21, &IUser::GetNpadId, "GetNpadId"},
+ {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
+ {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
+ {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class ISystem final : public Interface {
+public:
+ explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &ISystem::InitializeSystem, "InitializeSystem"},
+ {1, &ISystem::FinalizeSystem, "FinalizeSystem"},
+ {2, &ISystem::ListDevices, "ListDevices"},
+ {3, &ISystem::StartDetection, "StartDetection"},
+ {4, &ISystem::StopDetection, "StopDetection"},
+ {5, &ISystem::Mount, "Mount"},
+ {6, &ISystem::Unmount, "Unmount"},
+ {10, &ISystem::Flush, "Flush"},
+ {11, &ISystem::Restore, "Restore"},
+ {12, &ISystem::CreateApplicationArea, "CreateApplicationArea"},
+ {13, &ISystem::GetTagInfo, "GetTagInfo"},
+ {14, &ISystem::GetRegisterInfo, "GetRegisterInfo"},
+ {15, &ISystem::GetCommonInfo, "GetCommonInfo"},
+ {16, &ISystem::GetModelInfo, "GetModelInfo"},
+ {17, &ISystem::AttachActivateEvent, "AttachActivateEvent"},
+ {18, &ISystem::AttachDeactivateEvent, "AttachDeactivateEvent"},
+ {19, &ISystem::GetState, "GetState"},
+ {20, &ISystem::GetDeviceState, "GetDeviceState"},
+ {21, &ISystem::GetNpadId, "GetNpadId"},
+ {23, &ISystem::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
+ {100, &ISystem::Format, "Format"},
+ {101, &ISystem::GetAdminInfo, "GetAdminInfo"},
+ {102, &ISystem::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
+ {103, &ISystem::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
+ {104, &ISystem::DeleteRegisterInfo, "DeleteRegisterInfo"},
+ {105, &ISystem::DeleteApplicationArea, "DeleteApplicationArea"},
+ {106, &ISystem::ExistsApplicationArea, "ExistsApplicationArea"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IDebug final : public Interface {
+public:
+ explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IDebug::InitializeDebug, "InitializeDebug"},
+ {1, &IDebug::FinalizeDebug, "FinalizeDebug"},
+ {2, &IDebug::ListDevices, "ListDevices"},
+ {3, &IDebug::StartDetection, "StartDetection"},
+ {4, &IDebug::StopDetection, "StopDetection"},
+ {5, &IDebug::Mount, "Mount"},
+ {6, &IDebug::Unmount, "Unmount"},
+ {7, &IDebug::OpenApplicationArea, "OpenApplicationArea"},
+ {8, &IDebug::GetApplicationArea, "GetApplicationArea"},
+ {9, &IDebug::SetApplicationArea, "SetApplicationArea"},
+ {10, &IDebug::Flush, "Flush"},
+ {11, &IDebug::Restore, "Restore"},
+ {12, &IDebug::CreateApplicationArea, "CreateApplicationArea"},
+ {13, &IDebug::GetTagInfo, "GetTagInfo"},
+ {14, &IDebug::GetRegisterInfo, "GetRegisterInfo"},
+ {15, &IDebug::GetCommonInfo, "GetCommonInfo"},
+ {16, &IDebug::GetModelInfo, "GetModelInfo"},
+ {17, &IDebug::AttachActivateEvent, "AttachActivateEvent"},
+ {18, &IDebug::AttachDeactivateEvent, "AttachDeactivateEvent"},
+ {19, &IDebug::GetState, "GetState"},
+ {20, &IDebug::GetDeviceState, "GetDeviceState"},
+ {21, &IDebug::GetNpadId, "GetNpadId"},
+ {22, &IDebug::GetApplicationAreaSize, "GetApplicationAreaSize"},
+ {23, &IDebug::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
+ {24, &IDebug::RecreateApplicationArea, "RecreateApplicationArea"},
+ {100, &IDebug::Format, "Format"},
+ {101, &IDebug::GetAdminInfo, "GetAdminInfo"},
+ {102, &IDebug::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
+ {103, &IDebug::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
+ {104, &IDebug::DeleteRegisterInfo, "DeleteRegisterInfo"},
+ {105, &IDebug::DeleteApplicationArea, "DeleteApplicationArea"},
+ {106, &IDebug::ExistsApplicationArea, "ExistsApplicationArea"},
+ {200, &IDebug::GetAll, "GetAll"},
+ {201, &IDebug::SetAll, "SetAll"},
+ {202, &IDebug::FlushDebug, "FlushDebug"},
+ {203, &IDebug::BreakTag, "BreakTag"},
+ {204, nullptr, "ReadBackupData"},
+ {205, nullptr, "WriteBackupData"},
+ {206, nullptr, "WriteNtf"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
class IUserManager final : public ServiceFramework<IUserManager> {
public:
explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} {
@@ -37,10 +164,68 @@ private:
std::shared_ptr<IUser> user_interface;
};
+class ISystemManager final : public ServiceFramework<ISystemManager> {
+public:
+ explicit ISystemManager(Core::System& system_) : ServiceFramework{system_, "nfp:sys"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &ISystemManager::CreateSystemInterface, "CreateSystemInterface"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void CreateSystemInterface(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ if (system_interface == nullptr) {
+ system_interface = std::make_shared<ISystem>(system);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<ISystem>(system_interface);
+ }
+
+ std::shared_ptr<ISystem> system_interface;
+};
+
+class IDebugManager final : public ServiceFramework<IDebugManager> {
+public:
+ explicit IDebugManager(Core::System& system_) : ServiceFramework{system_, "nfp:dbg"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IDebugManager::CreateDebugInterface, "CreateDebugInterface"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void CreateDebugInterface(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ if (system_interface == nullptr) {
+ system_interface = std::make_shared<IDebug>(system);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IDebug>(system_interface);
+ }
+
+ std::shared_ptr<IDebug> system_interface;
+};
+
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("nfp:user", std::make_shared<IUserManager>(system));
+ server_manager->RegisterNamedService("nfp:sys", std::make_shared<ISystemManager>(system));
+ server_manager->RegisterNamedService("nfp:dbg", std::make_shared<IDebugManager>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index 607e70968..3f9af53c8 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -29,7 +29,6 @@
#include "core/hle/service/nfp/amiibo_crypto.h"
#include "core/hle/service/nfp/nfp_device.h"
#include "core/hle/service/nfp/nfp_result.h"
-#include "core/hle/service/nfp/nfp_user.h"
#include "core/hle/service/time/time_manager.h"
#include "core/hle/service/time/time_zone_content_manager.h"
#include "core/hle/service/time/time_zone_types.h"
@@ -241,6 +240,42 @@ Result NfpDevice::Flush() {
tag_data.write_counter++;
+ FlushWithBreak(BreakType::Normal);
+
+ is_data_moddified = false;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::FlushDebug() {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ tag_data.write_counter++;
+
+ FlushWithBreak(BreakType::Normal);
+
+ is_data_moddified = false;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::FlushWithBreak(BreakType break_type) {
+ if (break_type != BreakType::Normal) {
+ LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type);
+ return WrongDeviceState;
+ }
+
std::vector<u8> data(sizeof(EncryptedNTAG215File));
if (is_plain_amiibo) {
memcpy(data.data(), &tag_data, sizeof(tag_data));
@@ -258,8 +293,6 @@ Result NfpDevice::Flush() {
return WriteAmiiboFailed;
}
- is_data_moddified = false;
-
return ResultSuccess;
}
@@ -417,6 +450,38 @@ Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
return ResultSuccess;
}
+Result NfpDevice::GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ if (tag_data.settings.settings.amiibo_initialized == 0) {
+ return RegistrationIsNotInitialized;
+ }
+
+ Service::Mii::MiiManager manager;
+ const auto& settings = tag_data.settings;
+
+ // TODO: Validate and complete this data
+ register_info = {
+ .mii_store_data = {},
+ .creation_date = settings.init_date.GetWriteDate(),
+ .amiibo_name = GetAmiiboName(settings),
+ .font_region = settings.settings.font_region,
+ };
+
+ return ResultSuccess;
+}
+
Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
@@ -807,6 +872,159 @@ Result NfpDevice::DeleteApplicationArea() {
return Flush();
}
+Result NfpDevice::ExistApplicationArea(bool& has_application_area) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::GetAll(NfpData& data) const {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ CommonInfo common_info{};
+ Service::Mii::MiiManager manager;
+ const u64 application_id = tag_data.application_id;
+
+ GetCommonInfo(common_info);
+
+ data = {
+ .magic = tag_data.constant_value,
+ .write_counter = tag_data.write_counter,
+ .settings_crc = tag_data.settings.crc,
+ .common_info = common_info,
+ .mii_char_info = tag_data.owner_mii,
+ .mii_store_data_extension = tag_data.mii_extension,
+ .creation_date = tag_data.settings.init_date.GetWriteDate(),
+ .amiibo_name = tag_data.settings.amiibo_name,
+ .amiibo_name_null_terminated = 0,
+ .settings = tag_data.settings.settings,
+ .unknown1 = tag_data.unknown,
+ .register_info_crc = tag_data.register_info_crc,
+ .unknown2 = tag_data.unknown2,
+ .application_id = application_id,
+ .access_id = tag_data.application_area_id,
+ .settings_crc_counter = tag_data.settings.crc_counter,
+ .font_region = tag_data.settings.settings.font_region,
+ .tag_type = PackedTagType::Type2,
+ .console_type =
+ static_cast<AppAreaVersion>(application_id >> application_id_version_offset & 0xf),
+ .application_id_byte = tag_data.application_id_byte,
+ .application_area = tag_data.application_area,
+ };
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::SetAll(const NfpData& data) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ tag_data.constant_value = data.magic;
+ tag_data.write_counter = data.write_counter;
+ tag_data.settings.crc = data.settings_crc;
+ tag_data.settings.write_date.SetWriteDate(data.common_info.last_write_date);
+ tag_data.write_counter = data.common_info.write_counter;
+ tag_data.amiibo_version = data.common_info.version;
+ tag_data.owner_mii = data.mii_char_info;
+ tag_data.mii_extension = data.mii_store_data_extension;
+ tag_data.settings.init_date.SetWriteDate(data.creation_date);
+ tag_data.settings.amiibo_name = data.amiibo_name;
+ tag_data.settings.settings = data.settings;
+ tag_data.unknown = data.unknown1;
+ tag_data.register_info_crc = data.register_info_crc;
+ tag_data.unknown2 = data.unknown2;
+ tag_data.application_id = data.application_id;
+ tag_data.application_area_id = data.access_id;
+ tag_data.settings.crc_counter = data.settings_crc_counter;
+ tag_data.settings.settings.font_region.Assign(data.font_region);
+ tag_data.application_id_byte = data.application_id_byte;
+ tag_data.application_area = data.application_area;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::BreakTag(BreakType break_type) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ // TODO: Complete this implementation
+
+ return FlushWithBreak(break_type);
+}
+
+Result NfpDevice::ReadBackupData() {
+ // Not implemented
+ return ResultSuccess;
+}
+
+Result NfpDevice::WriteBackupData() {
+ // Not implemented
+ return ResultSuccess;
+}
+
+Result NfpDevice::WriteNtf() {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ // Not implemented
+
+ return ResultSuccess;
+}
+
u64 NfpDevice::GetHandle() const {
// Generate a handle based of the npad id
return static_cast<u64>(npad_id);
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
index 7f963730d..bab05538a 100644
--- a/src/core/hle/service/nfp/nfp_device.h
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -41,12 +41,16 @@ public:
Result StopDetection();
Result Mount(MountTarget mount_target);
Result Unmount();
+
Result Flush();
+ Result FlushDebug();
+ Result FlushWithBreak(BreakType break_type);
Result GetTagInfo(TagInfo& tag_info) const;
Result GetCommonInfo(CommonInfo& common_info) const;
Result GetModelInfo(ModelInfo& model_info) const;
Result GetRegisterInfo(RegisterInfo& register_info) const;
+ Result GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const;
Result GetAdminInfo(AdminInfo& admin_info) const;
Result DeleteRegisterInfo();
@@ -61,6 +65,14 @@ public:
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
Result DeleteApplicationArea();
+ Result ExistApplicationArea(bool& has_application_area);
+
+ Result GetAll(NfpData& data) const;
+ Result SetAll(const NfpData& data);
+ Result BreakTag(BreakType break_type);
+ Result ReadBackupData();
+ Result WriteBackupData();
+ Result WriteNtf();
u64 GetHandle() const;
u32 GetApplicationAreaSize() const;
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_interface.cpp
index 4e8534113..2ed8bb1ba 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_interface.cpp
@@ -7,42 +7,13 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfp/nfp_device.h"
+#include "core/hle/service/nfp/nfp_interface.h"
#include "core/hle/service/nfp/nfp_result.h"
-#include "core/hle/service/nfp/nfp_user.h"
namespace Service::NFP {
-IUser::IUser(Core::System& system_)
- : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} {
- static const FunctionInfo functions[] = {
- {0, &IUser::Initialize, "Initialize"},
- {1, &IUser::Finalize, "Finalize"},
- {2, &IUser::ListDevices, "ListDevices"},
- {3, &IUser::StartDetection, "StartDetection"},
- {4, &IUser::StopDetection, "StopDetection"},
- {5, &IUser::Mount, "Mount"},
- {6, &IUser::Unmount, "Unmount"},
- {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
- {8, &IUser::GetApplicationArea, "GetApplicationArea"},
- {9, &IUser::SetApplicationArea, "SetApplicationArea"},
- {10, &IUser::Flush, "Flush"},
- {11, &IUser::Restore, "Restore"},
- {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
- {13, &IUser::GetTagInfo, "GetTagInfo"},
- {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
- {15, &IUser::GetCommonInfo, "GetCommonInfo"},
- {16, &IUser::GetModelInfo, "GetModelInfo"},
- {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
- {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
- {19, &IUser::GetState, "GetState"},
- {20, &IUser::GetDeviceState, "GetDeviceState"},
- {21, &IUser::GetNpadId, "GetNpadId"},
- {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
- {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
- {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
- };
- RegisterHandlers(functions);
-
+Interface::Interface(Core::System& system_, const char* name)
+ : ServiceFramework{system_, name}, service_context{system_, service_name} {
availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
@@ -52,11 +23,37 @@ IUser::IUser(Core::System& system_)
}
}
-IUser ::~IUser() {
+Interface::~Interface() {
availability_change_event->Close();
}
-void IUser::Initialize(HLERequestContext& ctx) {
+void Interface::Initialize(HLERequestContext& ctx) {
+ LOG_INFO(Service_NFP, "called");
+
+ state = State::Initialized;
+
+ for (auto& device : devices) {
+ device->Initialize();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Interface::InitializeSystem(HLERequestContext& ctx) {
+ LOG_INFO(Service_NFP, "called");
+
+ state = State::Initialized;
+
+ for (auto& device : devices) {
+ device->Initialize();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Interface::InitializeDebug(HLERequestContext& ctx) {
LOG_INFO(Service_NFP, "called");
state = State::Initialized;
@@ -69,7 +66,7 @@ void IUser::Initialize(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-void IUser::Finalize(HLERequestContext& ctx) {
+void Interface::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFP, "called");
state = State::NonInitialized;
@@ -82,7 +79,33 @@ void IUser::Finalize(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-void IUser::ListDevices(HLERequestContext& ctx) {
+void Interface::FinalizeSystem(HLERequestContext& ctx) {
+ LOG_INFO(Service_NFP, "called");
+
+ state = State::NonInitialized;
+
+ for (auto& device : devices) {
+ device->Finalize();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Interface::FinalizeDebug(HLERequestContext& ctx) {
+ LOG_INFO(Service_NFP, "called");
+
+ state = State::NonInitialized;
+
+ for (auto& device : devices) {
+ device->Finalize();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Interface::ListDevices(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (state == State::NonInitialized) {
@@ -128,7 +151,7 @@ void IUser::ListDevices(HLERequestContext& ctx) {
rb.Push(static_cast<s32>(nfp_devices.size()));
}
-void IUser::StartDetection(HLERequestContext& ctx) {
+void Interface::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
@@ -153,7 +176,7 @@ void IUser::StartDetection(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::StopDetection(HLERequestContext& ctx) {
+void Interface::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
@@ -177,7 +200,7 @@ void IUser::StopDetection(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::Mount(HLERequestContext& ctx) {
+void Interface::Mount(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto model_type{rp.PopEnum<ModelType>()};
@@ -204,7 +227,7 @@ void IUser::Mount(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::Unmount(HLERequestContext& ctx) {
+void Interface::Unmount(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
@@ -228,7 +251,7 @@ void IUser::Unmount(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::OpenApplicationArea(HLERequestContext& ctx) {
+void Interface::OpenApplicationArea(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto access_id{rp.Pop<u32>()};
@@ -253,7 +276,7 @@ void IUser::OpenApplicationArea(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::GetApplicationArea(HLERequestContext& ctx) {
+void Interface::GetApplicationArea(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto data_size = ctx.GetWriteBufferSize();
@@ -287,7 +310,7 @@ void IUser::GetApplicationArea(HLERequestContext& ctx) {
rb.Push(static_cast<u32>(data_size));
}
-void IUser::SetApplicationArea(HLERequestContext& ctx) {
+void Interface::SetApplicationArea(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto data{ctx.ReadBuffer()};
@@ -318,7 +341,7 @@ void IUser::SetApplicationArea(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::Flush(HLERequestContext& ctx) {
+void Interface::Flush(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
@@ -342,7 +365,7 @@ void IUser::Flush(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::Restore(HLERequestContext& ctx) {
+void Interface::Restore(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
@@ -366,7 +389,7 @@ void IUser::Restore(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::CreateApplicationArea(HLERequestContext& ctx) {
+void Interface::CreateApplicationArea(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto access_id{rp.Pop<u32>()};
@@ -399,7 +422,7 @@ void IUser::CreateApplicationArea(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::GetTagInfo(HLERequestContext& ctx) {
+void Interface::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
@@ -425,7 +448,7 @@ void IUser::GetTagInfo(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::GetRegisterInfo(HLERequestContext& ctx) {
+void Interface::GetRegisterInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
@@ -451,7 +474,7 @@ void IUser::GetRegisterInfo(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::GetCommonInfo(HLERequestContext& ctx) {
+void Interface::GetCommonInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
@@ -477,7 +500,7 @@ void IUser::GetCommonInfo(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::GetModelInfo(HLERequestContext& ctx) {
+void Interface::GetModelInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
@@ -503,7 +526,7 @@ void IUser::GetModelInfo(HLERequestContext& ctx) {
rb.Push(result);
}
-void IUser::AttachActivateEvent(HLERequestContext& ctx) {
+void Interface::AttachActivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
@@ -527,7 +550,7 @@ void IUser::AttachActivateEvent(HLERequestContext& ctx) {
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
-void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
+void Interface::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
@@ -551,7 +574,7 @@ void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
-void IUser::GetState(HLERequestContext& ctx) {
+void Interface::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
IPC::ResponseBuilder rb{ctx, 3};
@@ -559,7 +582,7 @@ void IUser::GetState(HLERequestContext& ctx) {
rb.PushEnum(state);
}
-void IUser::GetDeviceState(HLERequestContext& ctx) {
+void Interface::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
@@ -577,7 +600,7 @@ void IUser::GetDeviceState(HLERequestContext& ctx) {
rb.PushEnum(device.value()->GetCurrentState());
}
-void IUser::GetNpadId(HLERequestContext& ctx) {
+void Interface::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
@@ -601,7 +624,7 @@ void IUser::GetNpadId(HLERequestContext& ctx) {
rb.PushEnum(device.value()->GetNpadId());
}
-void IUser::GetApplicationAreaSize(HLERequestContext& ctx) {
+void Interface::GetApplicationAreaSize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
@@ -619,7 +642,7 @@ void IUser::GetApplicationAreaSize(HLERequestContext& ctx) {
rb.Push(device.value()->GetApplicationAreaSize());
}
-void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
+void Interface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
LOG_INFO(Service_NFP, "called");
if (state == State::NonInitialized) {
@@ -633,7 +656,7 @@ void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
-void IUser::RecreateApplicationArea(HLERequestContext& ctx) {
+void Interface::RecreateApplicationArea(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto access_id{rp.Pop<u32>()};
@@ -660,7 +683,361 @@ void IUser::RecreateApplicationArea(HLERequestContext& ctx) {
rb.Push(result);
}
-std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) {
+void Interface::Format(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->Format();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::GetAdminInfo(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ AdminInfo admin_info{};
+ const auto result = device.value()->GetAdminInfo(admin_info);
+ ctx.WriteBuffer(admin_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::GetRegisterInfoPrivate(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ RegisterInfoPrivate register_info{};
+ const auto result = device.value()->GetRegisterInfoPrivate(register_info);
+ ctx.WriteBuffer(register_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto buffer{ctx.ReadBuffer()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}, buffer_size={}", device_handle,
+ buffer.size());
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->SetRegisterInfoPrivate({});
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::DeleteRegisterInfo(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->DeleteRegisterInfo();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::DeleteApplicationArea(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->DeleteApplicationArea();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::ExistsApplicationArea(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ bool has_application_area = false;
+ const auto result = device.value()->ExistApplicationArea(has_application_area);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(has_application_area);
+}
+
+void Interface::GetAll(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ NfpData data{};
+ const auto result = device.value()->GetAll(data);
+
+ ctx.WriteBuffer(data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::SetAll(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto nfp_data{ctx.ReadBuffer()};
+
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ NfpData data{};
+ memcpy(&data, nfp_data.data(), sizeof(NfpData));
+
+ const auto result = device.value()->SetAll(data);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::FlushDebug(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->FlushDebug();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::BreakTag(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto break_type{rp.PopEnum<BreakType>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}, break_type={}", device_handle, break_type);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->BreakTag(break_type);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::ReadBackupData(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->ReadBackupData();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::WriteBackupData(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->WriteBackupData();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Interface::WriteNtf(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->WriteNtf();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+std::optional<std::shared_ptr<NfpDevice>> Interface::GetNfpDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_interface.h
index 1f3ff2ea8..616c94b06 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_interface.h
@@ -13,19 +13,17 @@
namespace Service::NFP {
class NfpDevice;
-class IUser final : public ServiceFramework<IUser> {
+class Interface : public ServiceFramework<Interface> {
public:
- explicit IUser(Core::System& system_);
- ~IUser();
-
-private:
- enum class State : u32 {
- NonInitialized,
- Initialized,
- };
+ explicit Interface(Core::System& system_, const char* name);
+ ~Interface() override;
void Initialize(HLERequestContext& ctx);
+ void InitializeSystem(HLERequestContext& ctx);
+ void InitializeDebug(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
+ void FinalizeSystem(HLERequestContext& ctx);
+ void FinalizeDebug(HLERequestContext& ctx);
void ListDevices(HLERequestContext& ctx);
void StartDetection(HLERequestContext& ctx);
void StopDetection(HLERequestContext& ctx);
@@ -49,6 +47,26 @@ private:
void GetApplicationAreaSize(HLERequestContext& ctx);
void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
void RecreateApplicationArea(HLERequestContext& ctx);
+ void Format(HLERequestContext& ctx);
+ void GetAdminInfo(HLERequestContext& ctx);
+ void GetRegisterInfoPrivate(HLERequestContext& ctx);
+ void SetRegisterInfoPrivate(HLERequestContext& ctx);
+ void DeleteRegisterInfo(HLERequestContext& ctx);
+ void DeleteApplicationArea(HLERequestContext& ctx);
+ void ExistsApplicationArea(HLERequestContext& ctx);
+ void GetAll(HLERequestContext& ctx);
+ void SetAll(HLERequestContext& ctx);
+ void FlushDebug(HLERequestContext& ctx);
+ void BreakTag(HLERequestContext& ctx);
+ void ReadBackupData(HLERequestContext& ctx);
+ void WriteBackupData(HLERequestContext& ctx);
+ void WriteNtf(HLERequestContext& ctx);
+
+private:
+ enum class State : u32 {
+ NonInitialized,
+ Initialized,
+ };
std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 70c878552..1ef047cee 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -109,6 +109,12 @@ enum class AppAreaVersion : u8 {
NotSet = 0xFF,
};
+enum class BreakType : u32 {
+ Normal,
+ Unknown1,
+ Unknown2,
+};
+
enum class CabinetMode : u8 {
StartNicknameAndOwnerSettings,
StartGameDataEraser,
@@ -181,6 +187,12 @@ struct AmiiboDate {
};
}
+ void SetWriteDate(const WriteDate& write_date) {
+ SetYear(write_date.year);
+ SetMonth(write_date.month);
+ SetDay(write_date.day);
+ }
+
void SetYear(u16 year) {
const u16 year_converted = static_cast<u16>((year - 2000) << 9);
raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
@@ -354,6 +366,15 @@ struct RegisterInfo {
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
+struct RegisterInfoPrivate {
+ Service::Mii::MiiStoreData mii_store_data;
+ WriteDate creation_date;
+ AmiiboName amiibo_name;
+ u8 font_region;
+ INSERT_PADDING_BYTES(0x8E);
+};
+static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");
+
struct AdminInfo {
u64 application_id;
u32 application_area_id;
@@ -366,6 +387,39 @@ struct AdminInfo {
};
static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size");
+#pragma pack(1)
+// This is nn::nfp::NfpData
+struct NfpData {
+ u8 magic;
+ INSERT_PADDING_BYTES(0x1);
+ u8 write_counter;
+ INSERT_PADDING_BYTES(0x1);
+ u32 settings_crc;
+ INSERT_PADDING_BYTES(0x38);
+ CommonInfo common_info;
+ Service::Mii::Ver3StoreData mii_char_info;
+ Service::Mii::NfpStoreDataExtension mii_store_data_extension;
+ WriteDate creation_date;
+ std::array<u16_be, amiibo_name_length> amiibo_name;
+ u16 amiibo_name_null_terminated;
+ Settings settings;
+ u8 unknown1;
+ u32 register_info_crc;
+ std::array<u32, 5> unknown2;
+ INSERT_PADDING_BYTES(0x64);
+ u64 application_id;
+ u32 access_id;
+ u16 settings_crc_counter;
+ u8 font_region;
+ PackedTagType tag_type;
+ AppAreaVersion console_type;
+ u8 application_id_byte;
+ INSERT_PADDING_BYTES(0x2E);
+ ApplicationArea application_area;
+};
+static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
+#pragma pack()
+
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp
index 359891883..d707dabe2 100644
--- a/src/dedicated_room/yuzu_room.cpp
+++ b/src/dedicated_room/yuzu_room.cpp
@@ -49,6 +49,7 @@ static void PrintHelp(const char* argv0) {
" [options] <filename>\n"
"--room-name The name of the room\n"
"--room-description The room description\n"
+ "--bind-address The bind address for the room\n"
"--port The port used for the room\n"
"--max_members The maximum number of players for this room\n"
"--password The password for the room\n"
@@ -195,6 +196,7 @@ int main(int argc, char** argv) {
std::string web_api_url;
std::string ban_list_file;
std::string log_file = "yuzu-room.log";
+ std::string bind_address;
u64 preferred_game_id = 0;
u32 port = Network::DefaultRoomPort;
u32 max_members = 16;
@@ -203,6 +205,7 @@ int main(int argc, char** argv) {
static struct option long_options[] = {
{"room-name", required_argument, 0, 'n'},
{"room-description", required_argument, 0, 'd'},
+ {"bind-address", required_argument, 0, 's'},
{"port", required_argument, 0, 'p'},
{"max_members", required_argument, 0, 'm'},
{"password", required_argument, 0, 'w'},
@@ -222,7 +225,8 @@ int main(int argc, char** argv) {
InitializeLogging(log_file);
while (optind < argc) {
- int arg = getopt_long(argc, argv, "n:d:p:m:w:g:u:t:a:i:l:hv", long_options, &option_index);
+ int arg =
+ getopt_long(argc, argv, "n:d:s:p:m:w:g:u:t:a:i:l:hv", long_options, &option_index);
if (arg != -1) {
switch (static_cast<char>(arg)) {
case 'n':
@@ -231,6 +235,9 @@ int main(int argc, char** argv) {
case 'd':
room_description.assign(optarg);
break;
+ case 's':
+ bind_address.assign(optarg);
+ break;
case 'p':
port = strtoul(optarg, &endarg, 0);
break;
@@ -295,6 +302,9 @@ int main(int argc, char** argv) {
PrintHelp(argv[0]);
return -1;
}
+ if (bind_address.empty()) {
+ LOG_INFO(Network, "Bind address is empty: defaulting to 0.0.0.0");
+ }
if (port > UINT16_MAX) {
LOG_ERROR(Network, "Port needs to be in the range 0 - 65535!");
PrintHelp(argv[0]);
@@ -358,8 +368,8 @@ int main(int argc, char** argv) {
if (auto room = network.GetRoom().lock()) {
AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game,
.id = preferred_game_id};
- if (!room->Create(room_name, room_description, "", port, password, max_members, username,
- preferred_game_info, std::move(verify_backend), ban_list,
+ if (!room->Create(room_name, room_description, bind_address, port, password, max_members,
+ username, preferred_game_info, std::move(verify_backend), ban_list,
enable_yuzu_mods)) {
LOG_INFO(Network, "Failed to create room: ");
return -1;
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index 4fb2a6cfa..0c9f642bb 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -135,7 +135,7 @@ void Mouse::Move(int x, int y, int center_x, int center_y) {
auto mouse_change =
(Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
- last_motion_change += {-mouse_change.y, -mouse_change.x, last_motion_change.z};
+ last_motion_change += {-mouse_change.y, -mouse_change.x, 0};
const auto move_distance = mouse_change.Length();
if (move_distance == 0) {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index f335c8af0..418505475 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -143,6 +143,21 @@ IR::Inst* PrepareSparse(IR::Inst& inst) {
}
return sparse_inst;
}
+
+std::string ImageGatherSubpixelOffset(const IR::TextureInstInfo& info, std::string_view texture,
+ std::string_view coords) {
+ switch (info.type) {
+ case TextureType::Color2D:
+ case TextureType::Color2DRect:
+ return fmt::format("{}+vec2(0.001953125)/vec2(textureSize({}, 0))", coords, texture);
+ case TextureType::ColorArray2D:
+ case TextureType::ColorCube:
+ return fmt::format("vec3({0}.xy+vec2(0.001953125)/vec2(textureSize({1}, 0)),{0}.z)", coords,
+ texture);
+ default:
+ return std::string{coords};
+ }
+}
} // Anonymous namespace
void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
@@ -340,6 +355,13 @@ void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
ctx.AddU1("{}=true;", *sparse_inst);
}
+ std::string coords_with_subpixel_offset;
+ if (ctx.profile.need_gather_subpixel_offset) {
+ // Apply a subpixel offset of 1/512 the texel size of the texture to ensure same rounding on
+ // AMD hardware as on Maxwell or other Nvidia architectures.
+ coords_with_subpixel_offset = ImageGatherSubpixelOffset(info, texture, coords);
+ coords = coords_with_subpixel_offset;
+ }
if (!sparse_inst || !supports_sparse) {
if (offset.IsEmpty()) {
ctx.Add("{}=textureGather({},{},int({}));", texel, texture, coords,
@@ -387,6 +409,13 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
ctx.AddU1("{}=true;", *sparse_inst);
}
+ std::string coords_with_subpixel_offset;
+ if (ctx.profile.need_gather_subpixel_offset) {
+ // Apply a subpixel offset of 1/512 the texel size of the texture to ensure same rounding on
+ // AMD hardware as on Maxwell or other Nvidia architectures.
+ coords_with_subpixel_offset = ImageGatherSubpixelOffset(info, texture, coords);
+ coords = coords_with_subpixel_offset;
+ }
if (!sparse_inst || !supports_sparse) {
if (offset.IsEmpty()) {
ctx.Add("{}=textureGather({},{},{});", texel, texture, coords, dref);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 02073c420..7d901c04b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -261,6 +261,30 @@ Id BitTest(EmitContext& ctx, Id mask, Id bit) {
const Id bit_value{ctx.OpBitwiseAnd(ctx.U32[1], shifted, ctx.Const(1u))};
return ctx.OpINotEqual(ctx.U1, bit_value, ctx.u32_zero_value);
}
+
+Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info, Id texture,
+ Id coords) {
+ // Apply a subpixel offset of 1/512 the texel size of the texture to ensure same rounding on
+ // AMD hardware as on Maxwell or other Nvidia architectures.
+ const auto calculate_coords{[&](size_t dim) {
+ const Id nudge{ctx.Const(0x1p-9f)};
+ const Id image_size{ctx.OpImageQuerySizeLod(ctx.U32[dim], texture, ctx.u32_zero_value)};
+ Id offset{dim == 2 ? ctx.ConstantComposite(ctx.F32[dim], nudge, nudge)
+ : ctx.ConstantComposite(ctx.F32[dim], nudge, nudge, ctx.f32_zero_value)};
+ offset = ctx.OpFDiv(ctx.F32[dim], offset, ctx.OpConvertUToF(ctx.F32[dim], image_size));
+ return ctx.OpFAdd(ctx.F32[dim], coords, offset);
+ }};
+ switch (info.type) {
+ case TextureType::Color2D:
+ case TextureType::Color2DRect:
+ return calculate_coords(2);
+ case TextureType::ColorArray2D:
+ case TextureType::ColorCube:
+ return calculate_coords(3);
+ default:
+ return coords;
+ }
+}
} // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -423,6 +447,9 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const ImageOperands operands(ctx, offset, offset2);
+ if (ctx.profile.need_gather_subpixel_offset) {
+ coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
+ }
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
operands.MaskOptional(), operands.Span());
@@ -432,6 +459,9 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
const IR::Value& offset, const IR::Value& offset2, Id dref) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const ImageOperands operands(ctx, offset, offset2);
+ if (ctx.profile.need_gather_subpixel_offset) {
+ coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
+ }
return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
operands.Span());
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 253e0d0bd..9f88fb440 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -52,6 +52,10 @@ struct Profile {
bool need_declared_frag_colors{};
/// Prevents fast math optimizations that may cause inaccuracies
bool need_fastmath_off{};
+ /// Some GPU vendors use a different rounding precision when calculating texture pixel
+ /// coordinates with the 16.8 format in the ImageGather instruction than the Maxwell
+ /// architecture. Applying an offset does fix this mismatching rounding behaviour.
+ bool need_gather_subpixel_offset{};
/// OpFClamp is broken and OpFMax + OpFMin should be used instead
bool has_broken_spirv_clamp{};
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index a126c359c..02e161270 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -77,6 +77,14 @@ void Fermi2D::Blit() {
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
src.format != regs.dst.format;
+
+ auto srcX = args.src_x0;
+ auto srcY = args.src_y0;
+ if (args.sample_mode.origin == Origin::Corner) {
+ srcX -= (args.du_dx >> 33) << 32;
+ srcY -= (args.dv_dy >> 33) << 32;
+ }
+
Config config{
.operation = regs.operation,
.filter = args.sample_mode.filter,
@@ -86,10 +94,10 @@ void Fermi2D::Blit() {
.dst_y0 = args.dst_y0,
.dst_x1 = args.dst_x0 + args.dst_width,
.dst_y1 = args.dst_y0 + args.dst_height,
- .src_x0 = static_cast<s32>(args.src_x0 >> 32),
- .src_y0 = static_cast<s32>(args.src_y0 >> 32),
- .src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32),
- .src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32),
+ .src_x0 = static_cast<s32>(srcX >> 32),
+ .src_y0 = static_cast<s32>(srcY >> 32),
+ .src_x1 = static_cast<s32>((srcX + args.du_dx * args.dst_width) >> 32),
+ .src_y1 = static_cast<s32>((srcY + args.dv_dy * args.dst_height) >> 32),
};
const auto need_align_to_pitch =
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 614d61db4..0932fadc2 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -4,6 +4,7 @@
#include <cstring>
#include <optional>
#include "common/assert.h"
+#include "common/bit_util.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
@@ -259,12 +260,13 @@ u32 Maxwell3D::GetMaxCurrentVertices() {
size_t Maxwell3D::EstimateIndexBufferSize() {
GPUVAddr start_address = regs.index_buffer.StartAddress();
GPUVAddr end_address = regs.index_buffer.EndAddress();
- static constexpr std::array<size_t, 4> max_sizes = {
- std::numeric_limits<u8>::max(), std::numeric_limits<u16>::max(),
- std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
+ static constexpr std::array<size_t, 3> max_sizes = {std::numeric_limits<u8>::max(),
+ std::numeric_limits<u16>::max(),
+ std::numeric_limits<u32>::max()};
const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
+ const size_t log2_byte_size = Common::Log2Ceil64(byte_size);
return std::min<size_t>(
- memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[byte_size]) /
+ memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) /
byte_size,
static_cast<size_t>(end_address - start_address));
}
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 3ff8cad83..cc0b95f1a 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -176,6 +176,10 @@ public:
return vendor_name == "ATI Technologies Inc.";
}
+ bool IsIntel() const {
+ return vendor_name == "Intel";
+ }
+
bool CanReportMemoryUsage() const {
return can_report_memory;
}
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 479bb8ba3..6ecda2984 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -218,6 +218,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.lower_left_origin_mode = true,
.need_declared_frag_colors = true,
.need_fastmath_off = device.NeedsFastmathOff(),
+ .need_gather_subpixel_offset = device.IsAmd() || device.IsIntel(),
.has_broken_spirv_clamp = true,
.has_broken_unsigned_image_offsets = true,
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index 8aa07ef9d..47c74e4d8 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -10,7 +10,14 @@
namespace Vulkan {
-MasterSemaphore::MasterSemaphore(const Device& device) {
+MasterSemaphore::MasterSemaphore(const Device& device_) : device(device_) {
+ if (!device.HasTimelineSemaphore()) {
+ static constexpr VkFenceCreateInfo fence_ci{
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0};
+ fence = device.GetLogical().CreateFence(fence_ci);
+ return;
+ }
+
static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = nullptr,
@@ -42,4 +49,134 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
MasterSemaphore::~MasterSemaphore() = default;
+void MasterSemaphore::Refresh() {
+ if (!semaphore) {
+ // If we don't support timeline semaphores, there's nothing to refresh
+ return;
+ }
+
+ u64 this_tick{};
+ u64 counter{};
+ do {
+ this_tick = gpu_tick.load(std::memory_order_acquire);
+ counter = semaphore.GetCounter();
+ if (counter < this_tick) {
+ return;
+ }
+ } while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
+ std::memory_order_relaxed));
+}
+
+void MasterSemaphore::Wait(u64 tick) {
+ if (!semaphore) {
+ // If we don't support timeline semaphores, use an atomic wait
+ while (true) {
+ u64 current_value = gpu_tick.load(std::memory_order_relaxed);
+ if (current_value >= tick) {
+ return;
+ }
+ gpu_tick.wait(current_value);
+ }
+
+ return;
+ }
+
+ // No need to wait if the GPU is ahead of the tick
+ if (IsFree(tick)) {
+ return;
+ }
+
+ // Update the GPU tick and try again
+ Refresh();
+
+ if (IsFree(tick)) {
+ return;
+ }
+
+ // If none of the above is hit, fallback to a regular wait
+ while (!semaphore.Wait(tick)) {
+ }
+
+ Refresh();
+}
+
+VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick) {
+ if (semaphore) {
+ return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
+ } else {
+ return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
+ }
+}
+
+static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+};
+
+VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
+ VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick) {
+ const VkSemaphore timeline_semaphore = *semaphore;
+
+ const u32 num_signal_semaphores = signal_semaphore ? 2 : 1;
+ const std::array signal_values{host_tick, u64(0)};
+ const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
+
+ const u32 num_wait_semaphores = wait_semaphore ? 2 : 1;
+ const std::array wait_values{host_tick - 1, u64(1)};
+ const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
+
+ const VkTimelineSemaphoreSubmitInfo timeline_si{
+ .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
+ .pNext = nullptr,
+ .waitSemaphoreValueCount = num_wait_semaphores,
+ .pWaitSemaphoreValues = wait_values.data(),
+ .signalSemaphoreValueCount = num_signal_semaphores,
+ .pSignalSemaphoreValues = signal_values.data(),
+ };
+ const VkSubmitInfo submit_info{
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = &timeline_si,
+ .waitSemaphoreCount = num_wait_semaphores,
+ .pWaitSemaphores = wait_semaphores.data(),
+ .pWaitDstStageMask = wait_stage_masks.data(),
+ .commandBufferCount = 1,
+ .pCommandBuffers = cmdbuf.address(),
+ .signalSemaphoreCount = num_signal_semaphores,
+ .pSignalSemaphores = signal_semaphores.data(),
+ };
+
+ return device.GetGraphicsQueue().Submit(submit_info);
+}
+
+VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick) {
+ const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
+ const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
+
+ const VkSubmitInfo submit_info{
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = nullptr,
+ .waitSemaphoreCount = num_wait_semaphores,
+ .pWaitSemaphores = &wait_semaphore,
+ .pWaitDstStageMask = wait_stage_masks.data(),
+ .commandBufferCount = 1,
+ .pCommandBuffers = cmdbuf.address(),
+ .signalSemaphoreCount = num_signal_semaphores,
+ .pSignalSemaphores = &signal_semaphore,
+ };
+
+ auto result = device.GetGraphicsQueue().Submit(submit_info, *fence);
+
+ if (result == VK_SUCCESS) {
+ fence.Wait();
+ fence.Reset();
+ gpu_tick.store(host_tick);
+ gpu_tick.notify_all();
+ }
+
+ return result;
+}
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 689f02ea5..f2f61f781 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -4,6 +4,8 @@
#pragma once
#include <atomic>
+#include <condition_variable>
+#include <mutex>
#include <thread>
#include "common/common_types.h"
@@ -29,11 +31,6 @@ public:
return gpu_tick.load(std::memory_order_acquire);
}
- /// Returns the timeline semaphore handle.
- [[nodiscard]] VkSemaphore Handle() const noexcept {
- return *semaphore;
- }
-
/// Returns true when a tick has been hit by the GPU.
[[nodiscard]] bool IsFree(u64 tick) const noexcept {
return KnownGpuTick() >= tick;
@@ -45,37 +42,24 @@ public:
}
/// Refresh the known GPU tick
- void Refresh() {
- u64 this_tick{};
- u64 counter{};
- do {
- this_tick = gpu_tick.load(std::memory_order_acquire);
- counter = semaphore.GetCounter();
- if (counter < this_tick) {
- return;
- }
- } while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
- std::memory_order_relaxed));
- }
+ void Refresh();
/// Waits for a tick to be hit on the GPU
- void Wait(u64 tick) {
- // No need to wait if the GPU is ahead of the tick
- if (IsFree(tick)) {
- return;
- }
- // Update the GPU tick and try again
- Refresh();
- if (IsFree(tick)) {
- return;
- }
- // If none of the above is hit, fallback to a regular wait
- while (!semaphore.Wait(tick)) {
- }
- Refresh();
- }
+ void Wait(u64 tick);
+
+ /// Submits the device graphics queue, updating the tick as necessary
+ VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick);
+
+private:
+ VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick);
+ VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
+ VkSemaphore wait_semaphore, u64 host_tick);
private:
+ const Device& device; ///< Device.
+ vk::Fence fence; ///< Fence.
vk::Semaphore semaphore; ///< Timeline semaphore.
std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
std::atomic<u64> current_tick{1}; ///< Current logical tick.
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 0684cceed..985cc3203 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -329,6 +329,11 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.lower_left_origin_mode = false,
.need_declared_frag_colors = false,
+ .need_gather_subpixel_offset = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY ||
+ driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
+ driver_id == VK_DRIVER_ID_MESA_RADV ||
+ driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS ||
+ driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA,
.has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS,
.has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index b264e6ada..057e16967 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -212,45 +212,13 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
const u64 signal_value = master_semaphore->NextTick();
Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
cmdbuf.End();
- const VkSemaphore timeline_semaphore = master_semaphore->Handle();
-
- const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U;
- const std::array signal_values{signal_value, u64(0)};
- const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
-
- const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U;
- const std::array wait_values{signal_value - 1, u64(1)};
- const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
- static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
- VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
- VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
- };
-
- const VkTimelineSemaphoreSubmitInfo timeline_si{
- .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
- .pNext = nullptr,
- .waitSemaphoreValueCount = num_wait_semaphores,
- .pWaitSemaphoreValues = wait_values.data(),
- .signalSemaphoreValueCount = num_signal_semaphores,
- .pSignalSemaphoreValues = signal_values.data(),
- };
- const VkSubmitInfo submit_info{
- .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
- .pNext = &timeline_si,
- .waitSemaphoreCount = num_wait_semaphores,
- .pWaitSemaphores = wait_semaphores.data(),
- .pWaitDstStageMask = wait_stage_masks.data(),
- .commandBufferCount = 1,
- .pCommandBuffers = cmdbuf.address(),
- .signalSemaphoreCount = num_signal_semaphores,
- .pSignalSemaphores = signal_semaphores.data(),
- };
if (on_submit) {
on_submit();
}
- switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
+ switch (const VkResult result = master_semaphore->SubmitQueue(
+ cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
case VK_SUCCESS:
break;
case VK_ERROR_DEVICE_LOST:
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 41b5da18a..7d5018151 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -145,7 +145,6 @@
FEATURE_NAME(robustness2, robustImageAccess2) \
FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \
FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \
- FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
FEATURE_NAME(variable_pointer, variablePointers) \
FEATURE_NAME(variable_pointer, variablePointersStorageBuffer)
@@ -158,6 +157,7 @@
FEATURE_NAME(provoking_vertex, provokingVertexLast) \
FEATURE_NAME(shader_float16_int8, shaderFloat16) \
FEATURE_NAME(shader_float16_int8, shaderInt8) \
+ FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
FEATURE_NAME(transform_feedback, transformFeedback) \
FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \
FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState)
@@ -493,6 +493,10 @@ public:
return extensions.shader_atomic_int64;
}
+ bool HasTimelineSemaphore() const {
+ return features.timeline_semaphore.timelineSemaphore;
+ }
+
/// Returns the minimum supported version of SPIR-V.
u32 SupportedSpirvVersion() const {
if (instance_version >= VK_API_VERSION_1_3) {