From f7cf0f6559f84dd3c3503e6ee6d5c2eda6914e86 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Fri, 10 Mar 2023 21:31:28 +0100 Subject: [PATCH] early-access version 3447 --- README.md | 2 +- .../hle/service/am/applets/applet_cabinet.cpp | 4 +- src/core/hle/service/nfp/nfp_device.cpp | 95 +++++++++++++----- src/core/hle/service/nfp/nfp_device.h | 8 +- .../renderer_vulkan/vk_scheduler.cpp | 97 ++++++++++++++----- src/video_core/renderer_vulkan/vk_scheduler.h | 9 +- 6 files changed, 157 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 8ee65b14d..e6717b71d 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3446. +This is the source code for early-access 3447. ## Legal Notice diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index d0969b0f1..162687b29 100755 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -119,7 +119,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: { Service::NFP::AmiiboName name{}; std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1)); - nfp_device->SetNicknameAndOwner(name); + nfp_device->SetRegisterInfoPrivate(name); break; } case Service::NFP::CabinetMode::StartGameDataEraser: @@ -129,7 +129,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) nfp_device->RestoreAmiibo(); break; case Service::NFP::CabinetMode::StartFormatter: - nfp_device->DeleteAllData(); + nfp_device->Format(); break; default: UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode); diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 063e33304..79f9d20ad 100755 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -404,8 +404,8 @@ Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const { // Restore application id to original value if (application_id >> 0x38 != 0) { const u8 application_byte = tag_data.application_id_byte & 0xf; - application_id &= ~(0xfULL << application_id_version_offset); - application_id |= static_cast(application_byte) << application_id_version_offset; + application_id = RemoveVersionByte(application_id) | + (static_cast(application_byte) << application_id_version_offset); } application_area_id = tag_data.application_area_id; @@ -424,7 +424,39 @@ Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const { return ResultSuccess; } -Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { +Result NfpDevice::DeleteRegisterInfo() { + 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; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return RegistrationIsNotInitialized; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); + rng.GenerateRandomBytes(&tag_data.settings.amiibo_name, sizeof(tag_data.settings.amiibo_name)); + rng.GenerateRandomBytes(&tag_data.unknown, sizeof(u8)); + rng.GenerateRandomBytes(&tag_data.unknown2[0], sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.unknown2[1], sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.application_area_crc, sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.settings.init_date, sizeof(u32)); + tag_data.settings.settings.font_region.Assign(0); + tag_data.settings.settings.amiibo_initialized.Assign(0); + + return Flush(); +} + +Result NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { @@ -441,14 +473,23 @@ Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { Service::Mii::MiiManager manager; auto& settings = tag_data.settings; - settings.init_date = GetAmiiboDate(current_posix_time); - settings.write_date = GetAmiiboDate(current_posix_time); - UpdateSettingsCrc(); + if (tag_data.settings.settings.amiibo_initialized == 0) { + settings.init_date = GetAmiiboDate(current_posix_time); + settings.write_date.raw_date = 0; + } SetAmiiboName(settings, amiibo_name); tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); + tag_data.unknown = 0; + tag_data.unknown2[6] = 0; + settings.country_code_id = 0; + settings.settings.font_region.Assign(0); settings.settings.amiibo_initialized.Assign(1); + // TODO: this is a mix of tag.file input + std::array unknown_input{}; + tag_data.application_area_crc = CalculateCrc(unknown_input); + return Flush(); } @@ -471,24 +512,18 @@ Result NfpDevice::RestoreAmiibo() { return ResultSuccess; } -Result NfpDevice::DeleteAllData() { - const auto result = DeleteApplicationArea(); - if (result.IsError()) { - return result; +Result NfpDevice::Format() { + auto result1 = DeleteApplicationArea(); + auto result2 = DeleteRegisterInfo(); + + if (result1.IsError()) { + return result1; } - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; + if (result2.IsError()) { + return result2; } - Common::TinyMT rng{}; - rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); - tag_data.settings.settings.amiibo_initialized.Assign(0); - return Flush(); } @@ -671,13 +706,11 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span dat } const u64 application_id = system.GetApplicationProcessProgramID(); - const u64 application_id_without_version_byte = - application_id & ~(0xfULL << application_id_version_offset); tag_data.application_id_byte = static_cast(application_id >> application_id_version_offset & 0xf); tag_data.application_id = - application_id_without_version_byte | + RemoveVersionByte(application_id) | (static_cast(AppAreaVersion::NintendoSwitch) << application_id_version_offset); tag_data.settings.settings.appdata_initialized.Assign(1); tag_data.application_area_id = access_id; @@ -704,12 +737,20 @@ Result NfpDevice::DeleteApplicationArea() { return WrongDeviceState; } + if (tag_data.settings.settings.appdata_initialized == 0) { + return ApplicationAreaIsNotInitialized; + } + + if (tag_data.application_write_counter != counter_limit) { + tag_data.application_write_counter++; + } + Common::TinyMT rng{}; rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); rng.GenerateRandomBytes(&tag_data.application_id, sizeof(u64)); rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.application_id_byte, sizeof(u8)); tag_data.settings.settings.appdata_initialized.Assign(0); - tag_data.application_write_counter++; tag_data.unknown = {}; return Flush(); @@ -781,6 +822,10 @@ AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { return amiibo_date; } +u64 NfpDevice::RemoveVersionByte(u64 application_id) const { + return application_id & ~(0xfULL << application_id_version_offset); +} + void NfpDevice::UpdateSettingsCrc() { auto& settings = tag_data.settings; @@ -793,7 +838,7 @@ void NfpDevice::UpdateSettingsCrc() { settings.crc = CalculateCrc(unknown_input); } -u32 NfpDevice::CalculateCrc(std::span data) { +u32 NfpDevice::CalculateCrc(std::span data) { constexpr u32 magic = 0xedb88320; u32 crc = 0xffffffff; diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index b27d20773..a46ed469d 100755 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h @@ -49,9 +49,10 @@ public: Result GetRegisterInfo(RegisterInfo& register_info) const; Result GetAdminInfo(AdminInfo& admin_info) const; - Result SetNicknameAndOwner(const AmiiboName& amiibo_name); + Result DeleteRegisterInfo(); + Result SetRegisterInfoPrivate(const AmiiboName& amiibo_name); Result RestoreAmiibo(); - Result DeleteAllData(); + Result Format(); Result OpenApplicationArea(u32 access_id); Result GetApplicationAreaId(u32& application_area_id) const; @@ -77,8 +78,9 @@ private: AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); AmiiboDate GetAmiiboDate(s64 posix_time) const; + u64 RemoveVersionByte(u64 application_id) const; void UpdateSettingsCrc(); - u32 CalculateCrc(std::span data); + u32 CalculateCrc(std::span); bool is_controller_set{}; int callback_key; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 00f4386d8..cbcdc7649 100755 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -47,24 +47,41 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_) Scheduler::~Scheduler() = default; void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { + // When flushing, we only send data to the worker thread; no waiting is necessary. SubmitExecution(signal_semaphore, wait_semaphore); AllocateNewContext(); } void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { + // When finishing, we need to wait for the submission to have executed on the device. const u64 presubmit_tick = CurrentTick(); SubmitExecution(signal_semaphore, wait_semaphore); - WaitWorker(); + DrainRequests(); Wait(presubmit_tick); AllocateNewContext(); } +void Scheduler::DrainRequests() { + MICROPROFILE_SCOPE(Vulkan_WaitForWorker); + DispatchWork(); + + // Wait until the queue is empty and the queue lock can be held. + // This drains the queue. + std::unique_lock ql{queue_mutex}; + event_cv.wait(ql, [this] { return work_queue.empty(); }); +} + void Scheduler::WaitWorker() { MICROPROFILE_SCOPE(Vulkan_WaitForWorker); DispatchWork(); - std::unique_lock lock{work_mutex}; - wait_cv.wait(lock, [this] { return work_queue.empty(); }); + // Wait until the queue is empty and the execution lock can be held. + // This ensures Vulkan is aware of everything we have done when we return. + std::unique_lock el{execution_mutex}; + event_cv.wait(el, [this] { + std::scoped_lock ql{queue_mutex}; + return work_queue.empty(); + }); } void Scheduler::DispatchWork() { @@ -72,10 +89,10 @@ void Scheduler::DispatchWork() { return; } { - std::scoped_lock lock{work_mutex}; + std::scoped_lock ql{queue_mutex}; work_queue.push(std::move(chunk)); } - work_cv.notify_one(); + event_cv.notify_all(); AcquireNewChunk(); } @@ -137,30 +154,59 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) { void Scheduler::WorkerThread(std::stop_token stop_token) { Common::SetCurrentThreadName("VulkanWorker"); - do { + + const auto TryPopQueue{[this](auto& work) -> bool { + std::scoped_lock ql{queue_mutex}; + if (work_queue.empty()) { + return false; + } + + work = std::move(work_queue.front()); + work_queue.pop(); + event_cv.notify_all(); + return true; + }}; + + while (!stop_token.stop_requested()) { std::unique_ptr work; - bool has_submit{false}; + { - std::unique_lock lock{work_mutex}; - if (work_queue.empty()) { - wait_cv.notify_all(); - } - Common::CondvarWait(work_cv, lock, stop_token, [&] { return !work_queue.empty(); }); + std::unique_lock el{execution_mutex}; + + // Wait for work. + Common::CondvarWait(event_cv, el, stop_token, [&] { + std::scoped_lock ql{queue_mutex}; + return !work_queue.empty(); + }); + + // If we've been asked to stop, we're done. if (stop_token.stop_requested()) { + return; + } + + // If we don't have any work, restart from the top. + if (!TryPopQueue(work)) { continue; } - work = std::move(work_queue.front()); - work_queue.pop(); - has_submit = work->HasSubmit(); + // Perform the work, tracking whether the chunk was a submission + // before executing. + const bool has_submit = work->HasSubmit(); work->ExecuteAll(current_cmdbuf); + + // If the chunk was a submission, reallocate the command buffer. + if (has_submit) { + AllocateWorkerCommandBuffer(); + } } - if (has_submit) { - AllocateWorkerCommandBuffer(); + + { + std::scoped_lock rl{reserve_mutex}; + + // Recycle the chunk back to the reserve. + chunk_reserve.emplace_back(std::move(work)); } - std::scoped_lock reserve_lock{reserve_mutex}; - chunk_reserve.push_back(std::move(work)); - } while (!stop_token.stop_requested()); + } } void Scheduler::AllocateWorkerCommandBuffer() { @@ -289,13 +335,16 @@ void Scheduler::EndRenderPass() { } void Scheduler::AcquireNewChunk() { - std::scoped_lock lock{reserve_mutex}; + std::scoped_lock rl{reserve_mutex}; + if (chunk_reserve.empty()) { + // If we don't have anything reserved, we need to make a new chunk. chunk = std::make_unique(); - return; + } else { + // Otherwise, we can just take from the reserve. + chunk = std::make_unique(); + chunk_reserve.pop_back(); } - chunk = std::move(chunk_reserve.back()); - chunk_reserve.pop_back(); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index e5ad75e65..faa75da5d 100755 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -39,6 +39,9 @@ public: /// Sends the current execution context to the GPU and waits for it to complete. void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); + /// Waits for the worker thread to begin executing everything. + void DrainRequests(); + /// Waits for the worker thread to finish executing everything. After this function returns it's /// safe to touch worker resources. void WaitWorker(); @@ -232,10 +235,10 @@ private: std::queue> work_queue; std::vector> chunk_reserve; + std::mutex execution_mutex; std::mutex reserve_mutex; - std::mutex work_mutex; - std::condition_variable_any work_cv; - std::condition_variable wait_cv; + std::mutex queue_mutex; + std::condition_variable_any event_cv; std::jthread worker_thread; };