Compare commits

..

37 Commits

Author SHA1 Message Date
bunnei
7e54d9fc70 hle: kernel: KSynchronizationObject: Fix variable shadowing. 2021-11-21 02:30:16 -08:00
bunnei
fed016b7aa hle: kernel: Cleanup to match coding style. 2021-11-21 02:29:53 -08:00
bunnei
65124feaaa hle: kernel: KProcess: Improvements for thread pinning. 2021-11-14 02:08:28 -08:00
bunnei
d655450fd6 hle: kernel: KThreadQueue: Remove deprecated code. 2021-11-14 02:08:28 -08:00
bunnei
7231e689a7 hle: kernel: KConditionVariable: Various updates & simplifications. 2021-11-14 02:08:28 -08:00
bunnei
007b2301eb hle: kernel: KThread: Migrate to updated KThreadQueue (part 2). 2021-11-14 02:08:28 -08:00
bunnei
95cabd3f2e hle: kernel: KThread: Migrate to updated KThreadQueue (part 1). 2021-11-14 02:08:28 -08:00
bunnei
39090746c5 hle: kernel: KConditionVariable: Migrate to updated KThreadQueue. 2021-11-14 02:08:28 -08:00
bunnei
76d6e4a69f hle: kernel: KServerSession: Migrate to updated KThreadQueue. 2021-11-14 02:08:28 -08:00
bunnei
cbc79024cc hle: kernel: KLightConditionVariable: Migrate to updated KThreadQueue. 2021-11-14 02:08:28 -08:00
bunnei
f8b8ae7b45 hle: kernel: KLightLock: Migrate to updated KThreadQueue. 2021-11-14 02:08:28 -08:00
bunnei
30e4c6b858 hle: kernel: KAddressArbiter: Migrate to updated KThreadQueue. 2021-11-14 02:08:28 -08:00
bunnei
0c262044aa hle: kernel: KThread: Remove tracking of sync object from threads. 2021-11-14 02:08:28 -08:00
bunnei
e21040f5e2 hle: kernel: Update KThreadQueue and migrate KSynchronizationObject. 2021-11-14 02:08:28 -08:00
bunnei
30442d8a89 Merge pull request #7260 from vonchenplus/spirv_support_legacy_attribute_v2
shader: Spirv support legacy attribute v2
2021-11-14 02:07:45 -08:00
bunnei
7f256392a1 Merge pull request #7305 from Morph1984/ci
ci: Print traceback on patch merge failure
2021-11-14 02:07:04 -08:00
bunnei
0eacc362dd Merge pull request #7272 from behunin/the-courteous-logger
Logging: Impl refactor
2021-11-13 00:03:54 -08:00
Mai M
852858c2cb Merge pull request #7320 from OatmealDome/homebrew-capabilities
program_metadata: Add default ThreadInfo capability
2021-11-11 00:26:59 -05:00
OatmealDome
50acc0da20 program_metadata: Add default ThreadInfo kernel capability 2021-11-10 22:01:05 -05:00
Fernando S
bdabd17c76 Merge pull request #7303 from Morph1984/swkbd-confirm-skip-textcheck
applets/swkbd: Skip text checking if the text has been confirmed
2021-11-10 13:42:11 +01:00
Morph
3af2117c88 applets/swkbd: Fix text check message encoding
The text check message can be encoded in UTF-8.
2021-11-08 14:57:53 -05:00
Morph
84934693cf ci: Print traceback on patch merge failure 2021-11-08 13:36:05 -05:00
Morph
1af499c15b applets/swkbd: Skip text checking if the text has been confirmed
Confirm means that the text has already been checked by the application to be correct, but is asking the user for confirmation.
The confirmation text itself seems to be corrupted though, this needs to be investigated.

Fixes the software keyboard in Famicom Detective Club: The Missing Heir
2021-11-08 13:05:50 -05:00
Narr the Reg
ced1302975 service/pctl: Stub EndFreeCommunication
- Used by Just Dance 2022
2021-11-05 12:16:19 -04:00
Feng Chen
70d51f72ec vulkan_device: Add missing vulkan image format R5G6B5 in GetFormatProperties
- Used by Dragon Quest Builders
2021-11-05 10:31:40 -04:00
Morph
3a71ff44f8 Merge pull request #7279 from Morph1984/system-get-program-id
general: Get the current process program id directly from the system
2021-11-04 20:34:20 -04:00
Morph
2ceb514a39 Merge pull request #7289 from ameerj/perf-stat-shutdown
core: Reorder perf_stats destruction order on Shutdown
2021-11-04 20:34:09 -04:00
ameerj
0cf78a34ba core: Reorder perf_stats destruction order on Shutdown
Avoids the gpu_core using perf_stats after it's been freed.
2021-11-04 20:19:58 -04:00
Fernando S
3ceefc64f1 Merge pull request #7287 from Morph1984/stub-aoc
service: aoc: Stub more 13.x functions used by Animal Crossing
2021-11-05 00:26:10 +01:00
Morph
21c1316503 service: aoc: Stub NotifyUnmountAddOnContent
Used by Animal Crossing: New Horizons v2.0.0 DLC
2021-11-04 16:50:08 -04:00
Morph
ef427e4cb0 service: aoc: Stub NotifyMountAddOnContent and NotifyMountAddOnContent
Used by Animal Crossing: New Horizons v2.0.0 DLC
2021-11-04 16:48:47 -04:00
Feng Chen
f2a4204245 Simply legacy attribute implement 2021-11-04 09:26:16 +08:00
Levi Behunin
d6b5f64484 Refactor Logging Impl
Loop on stop_token and remove final_entry in Entry.
Move Backend thread out of Impl Constructor to its own function.
Add Start function for backend thread.
Use stop token in PopWait and check if entry filename is nullptr before logging.
2021-11-01 18:36:27 -06:00
vonchenplus
49f9a44235 Support gl_FogFragCoord attribute 2021-10-31 23:34:17 +08:00
vonchenplus
36c21ff6cb Support gl_BackSecondaryColor attribute 2021-10-26 23:14:40 +08:00
vonchenplus
92bebecf46 Support gl_FrontSecondaryColor attribute 2021-10-26 23:09:44 +08:00
vonchenplus
b04c7b6343 Support gl_BackColor attribute 2021-10-26 23:01:57 +08:00
41 changed files with 935 additions and 650 deletions

View File

@@ -1,7 +1,7 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, urllib3.request, shutil, subprocess, os
import requests, sys, json, urllib3.request, shutil, subprocess, os, traceback
tagline = sys.argv[2]
@@ -33,4 +33,5 @@ try:
for i in range(1,30):
do_page(i)
except:
traceback.print_exc(file=sys.stdout)
sys.exit(-1)

View File

@@ -6,6 +6,7 @@
#include <chrono>
#include <climits>
#include <exception>
#include <stop_token>
#include <thread>
#include <vector>
@@ -186,6 +187,10 @@ public:
initialization_in_progress_suppress_logging = false;
}
static void Start() {
instance->StartBackendThread();
}
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
@@ -201,7 +206,7 @@ public:
}
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, std::string message) {
const char* function, std::string&& message) {
if (!filter.CheckMessage(log_class, log_level))
return;
const Entry& entry =
@@ -211,40 +216,41 @@ public:
private:
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
: filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] {
Common::SetCurrentThreadName("yuzu:Log");
Entry entry;
const auto write_logs = [this, &entry]() {
ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
};
while (true) {
entry = message_queue.PopWait();
if (entry.final_entry) {
break;
}
write_logs();
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
while (max_logs_to_write-- && message_queue.Pop(entry)) {
write_logs();
}
})} {}
: filter{filter_}, file_backend{file_backend_filename} {}
~Impl() {
StopBackendThread();
}
void StartBackendThread() {
backend_thread = std::thread([this] {
Common::SetCurrentThreadName("yuzu:Log");
Entry entry;
const auto write_logs = [this, &entry]() {
ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
};
while (!stop.stop_requested()) {
entry = message_queue.PopWait(stop.get_token());
if (entry.filename != nullptr) {
write_logs();
}
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
while (max_logs_to_write-- && message_queue.Pop(entry)) {
write_logs();
}
});
}
void StopBackendThread() {
Entry stop_entry{};
stop_entry.final_entry = true;
message_queue.Push(stop_entry);
stop.request_stop();
backend_thread.join();
}
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message) const {
const char* function, std::string&& message) const {
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::steady_clock;
@@ -257,7 +263,6 @@ private:
.line_num = line_nr,
.function = function,
.message = std::move(message),
.final_entry = false,
};
}
@@ -278,8 +283,9 @@ private:
ColorConsoleBackend color_console_backend{};
FileBackend file_backend;
std::stop_source stop;
std::thread backend_thread;
MPSCQueue<Entry> message_queue{};
MPSCQueue<Entry, true> message_queue{};
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
};
} // namespace
@@ -288,6 +294,10 @@ void Initialize() {
Impl::Initialize();
}
void Start() {
Impl::Start();
}
void DisableLoggingInTests() {
initialization_in_progress_suppress_logging = true;
}

View File

@@ -14,6 +14,8 @@ class Filter;
/// Initializes the logging system. This should be the first thing called in main.
void Initialize();
void Start();
void DisableLoggingInTests();
/**

View File

@@ -22,7 +22,6 @@ struct Entry {
unsigned int line_num = 0;
std::string function;
std::string message;
bool final_entry = false;
};
} // namespace Common::Log

View File

@@ -173,6 +173,7 @@ add_library(core STATIC
hle/kernel/k_event.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
hle/kernel/k_light_condition_variable.cpp
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
@@ -225,6 +226,7 @@ add_library(core STATIC
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
hle/kernel/k_trace.h
hle/kernel/k_transfer_memory.cpp

View File

@@ -324,8 +324,8 @@ struct System::Impl {
time_manager.Shutdown();
core_timing.Shutdown();
app_loader.reset();
perf_stats.reset();
gpu_core.reset();
perf_stats.reset();
kernel.Shutdown();
memory.Reset();
applet_manager.ClearAll();

View File

@@ -53,13 +53,16 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
}
/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
// Allow use of cores 0~3 and thread priorities 1~63.
constexpr u32 default_thread_info_capability = 0x30007F7;
ProgramMetadata result;
result.LoadManual(
true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/,
0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/,
0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/,
0x1FE00000 /*system_resource_size*/, {} /*capabilities*/);
0x1FE00000 /*system_resource_size*/, {default_thread_info_capability} /*capabilities*/);
return result;
}

View File

@@ -16,7 +16,8 @@ DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default;
void DefaultSoftwareKeyboardApplet::InitializeKeyboard(
bool is_inline, KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) {
if (is_inline) {
@@ -128,7 +129,7 @@ void DefaultSoftwareKeyboardApplet::ExitKeyboard() const {
}
void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const {
submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text);
submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text, true);
}
void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const {

View File

@@ -57,7 +57,7 @@ public:
virtual void InitializeKeyboard(
bool is_inline, KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) = 0;
@@ -82,7 +82,7 @@ public:
void InitializeKeyboard(
bool is_inline, KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) override;
@@ -106,7 +106,7 @@ private:
KeyboardInitializeParameters parameters;
mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback;
mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback;

View File

@@ -8,6 +8,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
@@ -85,6 +86,27 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
return true;
}
class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
public:
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// If the thread is waiting on an address arbiter, remove it from the tree.
if (waiting_thread->IsWaitingForAddressArbiter()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearAddressArbiter();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KAddressArbiter::ThreadTree* m_tree;
};
} // namespace
ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
@@ -96,14 +118,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -129,14 +151,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -197,14 +219,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -214,6 +236,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -224,9 +247,6 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
bool succeeded{};
@@ -256,31 +276,20 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
return cur_thread->GetWaitResult();
}
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -291,9 +300,6 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
@@ -316,26 +322,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
return cur_thread->GetWaitResult();
}
} // namespace Kernel

View File

@@ -11,6 +11,7 @@
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
@@ -57,6 +58,48 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
return true;
}
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
public:
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel)
: KThreadQueue(kernel) {}
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
private:
KConditionVariable::ThreadTree* m_tree;
public:
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
KernelCore& kernel, KConditionVariable::ThreadTree* t)
: KThreadQueue(kernel), m_tree(t) {}
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// If the thread is waiting on a condvar, remove it from the tree.
if (waiting_thread->IsWaitingForConditionVariable()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearConditionVariable();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
KConditionVariable::KConditionVariable(Core::System& system_)
@@ -78,84 +121,77 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Determine the next tag.
u32 next_value{};
if (next_owner_thread) {
if (next_owner_thread != nullptr) {
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
}
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
if (next_owner_thread) {
next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
// Write the value to userspace.
ResultCode result{ResultSuccess};
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
result = ResultInvalidCurrentMemory;
}
return ResultInvalidCurrentMemory;
// Signal the next owner thread.
next_owner_thread->EndWait(result);
return result;
} else {
// Just write the value to userspace.
R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
ResultInvalidCurrentMemory);
return ResultSuccess;
}
}
return ResultSuccess;
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
{
KScopedAutoObject<KThread> owner_thread;
ASSERT(owner_thread.IsNull());
{
KScopedSchedulerLock sl(kernel);
cur_thread->SetSyncedObject(nullptr, ResultSuccess);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
{
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
// Get the lock owner thread.
owner_thread =
kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
handle);
R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
}
ASSERT(owner_thread.IsNotNull());
}
// Remove the thread as a waiter from the lock owner.
KThread* owner_thread{};
{
KScopedSchedulerLock sl(kernel);
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()
->GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Close our reference to the owner thread, now that the wait is over.
owner_thread->Close();
// Get the wait result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
return cur_thread->GetWaitResult();
}
KThread* KConditionVariable::SignalImpl(KThread* thread) {
void KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -169,18 +205,16 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) {
if (can_access) [[likely]] {
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
KThread* thread_to_close = nullptr;
if (can_access) {
if (can_access) [[likely]] {
if (prev_tag == Svc::InvalidHandle) {
// If nobody held the lock previously, we're all good.
thread->SetSyncedObject(nullptr, ResultSuccess);
thread->Wakeup();
thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
KThread* owner_thread = kernel.CurrentProcess()
@@ -189,35 +223,24 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
if (owner_thread) {
if (owner_thread) [[likely]] {
// Add the thread as a waiter on the owner.
owner_thread->AddWaiter(thread);
thread_to_close = owner_thread;
owner_thread->Close();
} else {
// The lock was tagged with a thread that doesn't exist.
thread->SetSyncedObject(nullptr, ResultInvalidState);
thread->Wakeup();
thread->EndWait(ResultInvalidState);
}
}
} else {
// If the address wasn't accessible, note so.
thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
thread->Wakeup();
thread->EndWait(ResultInvalidCurrentMemory);
}
return thread_to_close;
}
void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Prepare for signaling.
constexpr int MaxThreads = 16;
KLinkedList<KThread> thread_list{kernel};
std::array<KThread*, MaxThreads> thread_array;
s32 num_to_close{};
// Perform signaling.
s32 num_waiters{};
int num_waiters = 0;
{
KScopedSchedulerLock sl(kernel);
@@ -226,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread;
} else {
thread_list.push_back(*thread);
}
}
this->SignalImpl(target_thread);
it = thread_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
@@ -241,31 +257,20 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
// If we have no waiters, clear the has waiter flag.
if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
const u32 has_waiter_flag{};
const u32 has_waiter_flag = 0;
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
}
}
// Close threads in the array.
for (auto i = 0; i < num_to_close; ++i) {
thread_array[i]->Close();
}
// Close threads in the list.
for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
(*it).Close();
}
}
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -290,8 +295,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
// Wake up the next owner.
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
next_owner_thread->EndWait(ResultSuccess);
}
// Write to the cv key.
@@ -308,40 +312,21 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
}
// If timeout is zero, time out.
R_UNLESS(timeout != 0, ResultTimedOut);
// Update condition variable tracking.
{
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
}
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
// If the timeout is non-zero, set the thread as waiting.
if (timeout != 0) {
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the condition variable.
{
KScopedSchedulerLock sl(kernel);
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(cur_thread);
}
if (cur_thread->IsWaitingForConditionVariable()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearConditionVariable();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
// Get the wait result.
return cur_thread->GetWaitResult();
}
} // namespace Kernel

View File

@@ -34,7 +34,7 @@ public:
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
[[nodiscard]] KThread* SignalImpl(KThread* thread);
void SignalImpl(KThread* thread);
ThreadTree thread_tree;

View File

@@ -0,0 +1,80 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
public:
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel, KThread::WaiterList* wl,
bool term)
: KThreadQueue(kernel), m_wait_list(wl), m_allow_terminating_thread(term) {}
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Only process waits if we're allowed to.
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
return;
}
// Remove the thread from the waiting thread from the light condition variable.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KThread::WaiterList* m_wait_list;
bool m_allow_terminating_thread;
};
} // namespace
void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
// Create thread queue.
KThread* owner = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
allow_terminating_thread);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Add the thread to the queue.
wait_list.push_back(*owner);
// Begin waiting.
owner->BeginWait(std::addressof(wait_queue));
}
// Re-acquire the lock.
lock->Lock();
}
void KLightConditionVariable::Broadcast() {
KScopedSchedulerLock lk(kernel);
// Signal all threads.
for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
it->EndWait(ResultSuccess);
}
}
} // namespace Kernel

View File

@@ -1,73 +1,25 @@
// Copyright 2020 yuzu Emulator Project
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
class KernelCore;
class KLightLock;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
WaitImpl(lock, timeout, allow_terminating_thread);
}
void Broadcast() {
KScopedSchedulerLock lk{kernel};
// Signal all threads.
for (auto& thread : wait_list) {
thread.SetState(ThreadState::Runnable);
}
}
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
void Broadcast();
private:
void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
KThread* owner = GetCurrentThreadPointer(kernel);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Set the thread as waiting.
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(GetCurrentThread(kernel));
}
// Remove the thread from the wait list.
{
KScopedSchedulerLock sl{kernel};
wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
}
// Cancel the task that the sleep setup.
kernel.TimeManager().UnscheduleTimeEvent(owner);
// Re-acquire the lock.
lock->Lock();
}
KernelCore& kernel;
KThread::WaiterList wait_list{};
};

View File

@@ -5,10 +5,26 @@
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightLock final : public KThreadQueue {
public:
explicit ThreadQueueImplForKLightLock(KernelCore& kernel) : KThreadQueue(kernel) {}
virtual void CancelWait([[maybe_unused]] KThread* waiting_thread,
[[maybe_unused]] ResultCode wait_result,
[[maybe_unused]] bool cancel_timer_task) override {
// Do nothing, waiting to acquire a light lock cannot be canceled.
}
};
} // namespace
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
const uintptr_t cur_thread_tag = (cur_thread | 1);
@@ -16,33 +32,28 @@ void KLightLock::Lock() {
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
std::memory_order_acquire)) {
if ((old_tag | 1) == cur_thread_tag) {
return;
}
}
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
break;
}
LockSlowPath(old_tag | 1, cur_thread);
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
uintptr_t expected = cur_thread;
do {
if (expected != cur_thread) {
return UnlockSlowPath(cur_thread);
}
} while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
this->UnlockSlowPath(cur_thread);
}
}
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
ThreadQueueImplForKLightLock wait_queue(kernel);
// Pend the current thread waiting on the owner thread.
{
@@ -50,30 +61,23 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
return;
return false;
}
// Add the current thread as a waiter on the owner.
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ul);
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// Set thread states.
cur_thread->SetState(ThreadState::Waiting);
// Begin waiting to hold the lock.
cur_thread->BeginWait(std::addressof(wait_queue));
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
// We're no longer waiting on the lock owner.
{
KScopedSchedulerLock sl{kernel};
if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
}
return true;
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
@@ -81,22 +85,20 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
// Unlock.
{
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Get the next owner.
s32 num_waiters = 0;
s32 num_waiters;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner != nullptr) {
next_tag = reinterpret_cast<uintptr_t>(next_owner);
if (num_waiters > 1) {
next_tag |= 0x1;
}
next_tag =
reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
next_owner->SetState(ThreadState::Runnable);
next_owner->EndWait(ResultSuccess);
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
@@ -110,7 +112,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
}
// Write the new tag value.
tag.store(next_tag);
tag.store(next_tag, std::memory_order_release);
}
}

View File

@@ -20,7 +20,7 @@ public:
void Unlock();
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);

View File

@@ -227,12 +227,15 @@ void KProcess::PinCurrentThread() {
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
// Pin it.
PinThread(core_id, cur_thread);
cur_thread->Pin();
// If the thread isn't terminated, pin it.
if (!cur_thread->IsTerminationRequested()) {
// Pin it.
PinThread(core_id, cur_thread);
cur_thread->Pin();
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
void KProcess::UnpinCurrentThread() {
@@ -250,6 +253,20 @@ void KProcess::UnpinCurrentThread() {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
void KProcess::UnpinThread(KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the thread's core id.
const auto core_id = thread->GetActiveCore();
// Unpin it.
UnpinThread(core_id, thread);
thread->Unpin();
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
[[maybe_unused]] size_t size) {
// Lock ourselves, to prevent concurrent access.

View File

@@ -259,7 +259,7 @@ public:
[[nodiscard]] KThread* GetPinnedThread(s32 core_id) const {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
return pinned_threads[core_id];
return pinned_threads.at(core_id);
}
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
@@ -347,6 +347,7 @@ public:
void PinCurrentThread();
void UnpinCurrentThread();
void UnpinThread(KThread* thread);
KLightLock& GetStateLock() {
return state_lock;
@@ -368,14 +369,14 @@ private:
void PinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
ASSERT(thread != nullptr);
ASSERT(pinned_threads[core_id] == nullptr);
ASSERT(pinned_threads.at(core_id) == nullptr);
pinned_threads[core_id] = thread;
}
void UnpinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
ASSERT(thread != nullptr);
ASSERT(pinned_threads[core_id] == thread);
ASSERT(pinned_threads.at(core_id) == thread);
pinned_threads[core_id] = nullptr;
}

View File

@@ -8,6 +8,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/time_manager.h"

View File

@@ -175,8 +175,7 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
{
KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) {
context.GetThread().Wakeup();
context.GetThread().SetSyncedObject(nullptr, result);
context.GetThread().EndWait(result);
}
}

View File

@@ -8,11 +8,66 @@
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
public:
ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
KSynchronizationObject::ThreadListNode* n, s32 c)
: KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
ResultCode wait_result) override {
// Determine the sync index, and unlink all nodes.
s32 sync_index = -1;
for (auto i = 0; i < m_count; ++i) {
// Check if this is the signaled object.
if (m_objects[i] == signaled_object && sync_index == -1) {
sync_index = i;
}
// Unlink the current node from the current object.
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread's sync index.
waiting_thread->SetSyncedIndex(sync_index);
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base end wait handler.
KThreadQueue::EndWait(waiting_thread, wait_result);
}
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove all nodes from our list.
for (auto i = 0; i < m_count; ++i) {
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KSynchronizationObject** m_objects;
KSynchronizationObject::ThreadListNode* m_nodes;
s32 m_count;
};
} // namespace
void KSynchronizationObject::Finalize() {
this->OnFinalizeSynchronizationObject();
KAutoObject::Finalize();
@@ -25,11 +80,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait.
KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread();
KThread* thread = GetCurrentThreadPointer(kernel_ctx);
ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
thread_nodes.data(), num_objects);
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout};
KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
// Check if the thread should terminate.
if (thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) {
@@ -48,12 +111,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
return ResultTimedOut;
}
// Check if the thread should terminate.
if (thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Check if waiting was canceled.
if (thread->IsWaitCancelled()) {
slp.CancelSleep();
@@ -66,73 +123,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
thread_nodes[i].thread = thread;
thread_nodes[i].next = nullptr;
if (objects[i]->thread_list_tail == nullptr) {
objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
} else {
objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
}
objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
objects[i]->LinkNode(std::addressof(thread_nodes[i]));
}
// For debugging only
thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
// Mark the thread as waiting.
// Mark the thread as cancellable.
thread->SetCancellable();
thread->SetSyncedObject(nullptr, ResultTimedOut);
thread->SetState(ThreadState::Waiting);
// Clear the thread's synced index.
thread->SetSyncedIndex(-1);
// Wait for an object to be signaled.
thread->BeginWait(std::addressof(wait_queue));
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
}
// The lock/sleep is done, so we should be able to get our result.
// Thread is no longer cancellable.
thread->ClearCancellable();
// For debugging only
thread->SetWaitObjectsForDebugging({});
// Cancel the timer as needed.
kernel_ctx.TimeManager().UnscheduleTimeEvent(thread);
// Set the output index.
*out_index = thread->GetSyncedIndex();
// Get the wait result.
ResultCode wait_result{ResultSuccess};
s32 sync_index = -1;
{
KScopedSchedulerLock lock(kernel_ctx);
KSynchronizationObject* synced_obj;
wait_result = thread->GetWaitResult(std::addressof(synced_obj));
for (auto i = 0; i < num_objects; ++i) {
// Unlink the object from the list.
ThreadListNode* prev_ptr =
reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
ThreadListNode* prev_val = nullptr;
ThreadListNode *prev, *tail_prev;
do {
prev = prev_ptr;
prev_ptr = prev_ptr->next;
tail_prev = prev_val;
prev_val = prev_ptr;
} while (prev_ptr != std::addressof(thread_nodes[i]));
if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
objects[i]->thread_list_tail = tail_prev;
}
prev->next = thread_nodes[i].next;
if (objects[i] == synced_obj) {
sync_index = i;
}
}
}
// Set output.
*out_index = sync_index;
return wait_result;
return thread->GetWaitResult();
}
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
@@ -141,7 +150,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(ResultCode result) {
KScopedSchedulerLock lock(kernel);
KScopedSchedulerLock sl(kernel);
// If we're not signaled, we've nothing to notify.
if (!this->IsSignaled()) {
@@ -150,11 +159,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
// Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
KThread* thread = cur_node->thread;
if (thread->GetState() == ThreadState::Waiting) {
thread->SetSyncedObject(this, result);
thread->SetState(ThreadState::Runnable);
}
cur_node->thread->NotifyAvailable(this, result);
}
}

View File

@@ -35,6 +35,38 @@ public:
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
void LinkNode(ThreadListNode* node_) {
// Link the node to the list.
if (thread_list_tail == nullptr) {
thread_list_head = node_;
} else {
thread_list_tail->next = node_;
}
thread_list_tail = node_;
}
void UnlinkNode(ThreadListNode* node_) {
// Unlink the node from the list.
ThreadListNode* prev_ptr =
reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
ThreadListNode* prev_val = nullptr;
ThreadListNode *prev, *tail_prev;
do {
prev = prev_ptr;
prev_ptr = prev_ptr->next;
tail_prev = prev_val;
prev_val = prev_ptr;
} while (prev_ptr != node_);
if (thread_list_tail == node_) {
thread_list_tail = tail_prev;
}
prev->next = node_->next;
}
protected:
explicit KSynchronizationObject(KernelCore& kernel);
~KSynchronizationObject() override;

View File

@@ -56,6 +56,34 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
namespace Kernel {
namespace {
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
public:
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
: KThreadQueueWithoutEndWait(kernel_) {}
};
class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue {
public:
explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
: KThreadQueue(kernel_), m_wait_list(wl) {}
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread from the wait list.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KThread::WaiterList* m_wait_list;
};
} // namespace
KThread::KThread(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
KThread::~KThread() = default;
@@ -127,9 +155,6 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
priority = prio;
base_priority = prio;
// Set sync object and waiting lock to null.
synced_object = nullptr;
// Initialize sleeping queue.
sleeping_queue = nullptr;
@@ -273,11 +298,14 @@ void KThread::Finalize() {
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
// The thread shouldn't be a kernel waiter.
// Clear the lock owner
it->SetLockOwner(nullptr);
it->SetSyncedObject(nullptr, ResultInvalidState);
it->Wakeup();
// Erase the waiter from our list.
it = waiter_list.erase(it);
// Cancel the thread's wait.
it->CancelWait(ResultInvalidState, true);
}
}
@@ -294,15 +322,12 @@ bool KThread::IsSignaled() const {
return signaled;
}
void KThread::Wakeup() {
KScopedSchedulerLock sl{kernel};
void KThread::OnTimer() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If we're waiting, cancel the wait.
if (GetState() == ThreadState::Waiting) {
if (sleeping_queue != nullptr) {
sleeping_queue->WakeupThread(this);
} else {
SetState(ThreadState::Runnable);
}
sleeping_queue->CancelWait(this, ResultTimedOut, false);
}
}
@@ -327,7 +352,7 @@ void KThread::StartTermination() {
// Signal.
signaled = true;
NotifyAvailable();
KSynchronizationObject::NotifyAvailable();
// Clear previous thread in KScheduler.
KScheduler::ClearPreviousThread(kernel, this);
@@ -475,30 +500,32 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
return ResultSuccess;
}
ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
ASSERT(parent != nullptr);
ASSERT(v_affinity_mask != 0);
KScopedLightLock lk{activity_pause_lock};
KScopedLightLock lk(activity_pause_lock);
// Set the core mask.
u64 p_affinity_mask = 0;
{
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
ASSERT(num_core_migration_disables >= 0);
// If the core id is no-update magic, preserve the ideal core id.
if (cpu_core_id == Svc::IdealCoreNoUpdate) {
cpu_core_id = virtual_ideal_core_id;
R_UNLESS(((1ULL << cpu_core_id) & v_affinity_mask) != 0, ResultInvalidCombination);
// If we're updating, set our ideal virtual core.
if (core_id_ != Svc::IdealCoreNoUpdate) {
virtual_ideal_core_id = core_id_;
} else {
// Preserve our ideal core id.
core_id_ = virtual_ideal_core_id;
R_UNLESS(((1ULL << core_id_) & v_affinity_mask) != 0, ResultInvalidCombination);
}
// Set the virtual core/affinity mask.
virtual_ideal_core_id = cpu_core_id;
// Set our affinity mask.
virtual_affinity_mask = v_affinity_mask;
// Translate the virtual core to a physical core.
if (cpu_core_id >= 0) {
cpu_core_id = Core::Hardware::VirtualToPhysicalCoreMap[cpu_core_id];
if (core_id_ >= 0) {
core_id_ = Core::Hardware::VirtualToPhysicalCoreMap[core_id_];
}
// Translate the virtual affinity mask to a physical one.
@@ -513,7 +540,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
const KAffinityMask old_mask = physical_affinity_mask;
// Set our new ideals.
physical_ideal_core_id = cpu_core_id;
physical_ideal_core_id = core_id_;
physical_affinity_mask.SetAffinityMask(p_affinity_mask);
if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
@@ -531,18 +558,18 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
}
} else {
// Otherwise, we edit the original affinity for restoration later.
original_physical_ideal_core_id = cpu_core_id;
original_physical_ideal_core_id = core_id_;
original_physical_affinity_mask.SetAffinityMask(p_affinity_mask);
}
}
// Update the pinned waiter list.
ThreadQueueImplForKThreadSetProperty wait_queue(kernel, std::addressof(pinned_waiter_list));
{
bool retry_update{};
bool thread_is_pinned{};
do {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Don't do any further management if our termination has been requested.
R_SUCCEED_IF(IsTerminationRequested());
@@ -570,12 +597,9 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
ResultTerminationRequested);
// Note that the thread was pinned.
thread_is_pinned = true;
// Wait until the thread isn't pinned any more.
pinned_waiter_list.push_back(GetCurrentThread(kernel));
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
} else {
// If the thread isn't pinned, release the scheduler lock and retry until it's
// not current.
@@ -583,16 +607,6 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
}
}
} while (retry_update);
// If the thread was pinned, it no longer is, and we should remove the current thread from
// our waiter list.
if (thread_is_pinned) {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
// Remove from the list.
pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
}
}
return ResultSuccess;
@@ -641,15 +655,9 @@ void KThread::WaitCancel() {
KScopedSchedulerLock sl{kernel};
// Check if we're waiting and cancellable.
if (GetState() == ThreadState::Waiting && cancellable) {
if (sleeping_queue != nullptr) {
sleeping_queue->WakeupThread(this);
wait_cancelled = true;
} else {
SetSyncedObject(nullptr, ResultCancelled);
SetState(ThreadState::Runnable);
wait_cancelled = false;
}
if (this->GetState() == ThreadState::Waiting && cancellable) {
wait_cancelled = false;
sleeping_queue->CancelWait(this, ResultCancelled, true);
} else {
// Otherwise, note that we cancelled a wait.
wait_cancelled = true;
@@ -700,60 +708,59 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Set the activity.
{
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Verify our state.
const auto cur_state = GetState();
const auto cur_state = this->GetState();
R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
ResultInvalidState);
// Either pause or resume.
if (activity == Svc::ThreadActivity::Paused) {
// Verify that we're not suspended.
R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Suspend.
RequestSuspend(SuspendType::Thread);
this->RequestSuspend(SuspendType::Thread);
} else {
ASSERT(activity == Svc::ThreadActivity::Runnable);
// Verify that we're suspended.
R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Resume.
Resume(SuspendType::Thread);
this->Resume(SuspendType::Thread);
}
}
// If the thread is now paused, update the pinned waiter list.
if (activity == Svc::ThreadActivity::Paused) {
bool thread_is_pinned{};
bool thread_is_current{};
ThreadQueueImplForKThreadSetProperty wait_queue_(kernel,
std::addressof(pinned_waiter_list));
bool thread_is_current;
do {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Don't do any further management if our termination has been requested.
R_SUCCEED_IF(IsTerminationRequested());
R_SUCCEED_IF(this->IsTerminationRequested());
// By default, treat the thread as not current.
thread_is_current = false;
// Check whether the thread is pinned.
if (GetStackParameters().is_pinned) {
if (this->GetStackParameters().is_pinned) {
// Verify that the current thread isn't terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
ResultTerminationRequested);
// Note that the thread was pinned and not current.
thread_is_pinned = true;
thread_is_current = false;
// Wait until the thread isn't pinned any more.
pinned_waiter_list.push_back(GetCurrentThread(kernel));
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
} else {
// Check if the thread is currently running.
// If it is, we'll need to retry.
thread_is_current = false;
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
if (kernel.Scheduler(i).GetCurrentThread() == this) {
thread_is_current = true;
@@ -762,16 +769,6 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
}
}
} while (thread_is_current);
// If the thread was pinned, it no longer is, and we should remove the current thread from
// our waiter list.
if (thread_is_pinned) {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
// Remove from the list.
pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
}
}
return ResultSuccess;
@@ -996,29 +993,63 @@ ResultCode KThread::Sleep(s64 timeout) {
ASSERT(this == GetCurrentThreadPointer(kernel));
ASSERT(timeout > 0);
ThreadQueueImplForKThreadSleep wait_queue(kernel);
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp{kernel, this, timeout};
KScopedSchedulerLockAndSleep slp(kernel, this, timeout);
// Check if the thread should terminate.
if (IsTerminationRequested()) {
if (this->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Mark the thread as waiting.
SetState(ThreadState::Waiting);
// Wait for the sleep to end.
this->BeginWait(std::addressof(wait_queue));
SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
}
// The lock/sleep is done.
// Cancel the timer.
kernel.TimeManager().UnscheduleTimeEvent(this);
return ResultSuccess;
}
void KThread::BeginWait(KThreadQueue* queue) {
// Set our state as waiting.
SetState(ThreadState::Waiting);
// Set our wait queue.
sleeping_queue = queue;
}
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
// If we're waiting, notify our queue that we're available.
if (GetState() == ThreadState::Waiting) {
sleeping_queue->NotifyAvailable(this, signaled_object, wait_result_);
}
}
void KThread::EndWait(ResultCode wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
// If we're waiting, notify our queue that we're available.
if (GetState() == ThreadState::Waiting) {
sleeping_queue->EndWait(this, wait_result_);
}
}
void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
// If we're waiting, notify our queue that we're available.
if (GetState() == ThreadState::Waiting) {
sleeping_queue->CancelWait(this, wait_result_, cancel_timer_task);
}
}
void KThread::SetState(ThreadState state) {
KScopedSchedulerLock sl{kernel};

View File

@@ -161,8 +161,6 @@ public:
}
}
void Wakeup();
void SetBasePriority(s32 value);
[[nodiscard]] ResultCode Run();
@@ -197,13 +195,19 @@ public:
void Suspend();
void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
synced_object = obj;
constexpr void SetSyncedIndex(s32 index) {
synced_index = index;
}
[[nodiscard]] constexpr s32 GetSyncedIndex() const {
return synced_index;
}
constexpr void SetWaitResult(ResultCode wait_res) {
wait_result = wait_res;
}
[[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
*out = synced_object;
[[nodiscard]] constexpr ResultCode GetWaitResult() const {
return wait_result;
}
@@ -374,6 +378,8 @@ public:
[[nodiscard]] bool IsSignaled() const override;
void OnTimer();
static void PostDestroy(uintptr_t arg);
[[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
@@ -573,6 +579,15 @@ public:
address_key_value = val;
}
void ClearWaitQueue() {
sleeping_queue = nullptr;
}
void BeginWait(KThreadQueue* queue);
void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
void EndWait(ResultCode wait_result_);
void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
@@ -667,7 +682,6 @@ private:
KAffinityMask physical_affinity_mask{};
u64 thread_id{};
std::atomic<s64> cpu_time{};
KSynchronizationObject* synced_object{};
VAddr address_key{};
KProcess* parent{};
VAddr kernel_stack_top{};
@@ -684,6 +698,7 @@ private:
u32 address_key_value{};
u32 suspend_request_flags{};
u32 suspend_allowed_flags{};
s32 synced_index{};
ResultCode wait_result{ResultSuccess};
s32 base_priority{};
s32 physical_ideal_core_id{};

View File

@@ -0,0 +1,51 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
[[maybe_unused]] KSynchronizationObject* signaled_object,
[[maybe_unused]] ResultCode wait_result) {}
void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
// Set the thread as runnable.
waiting_thread->SetState(ThreadState::Runnable);
// Clear the thread's wait queue.
waiting_thread->ClearWaitQueue();
// Cancel the thread task.
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
}
void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
// Set the thread as runnable.
waiting_thread->SetState(ThreadState::Runnable);
// Clear the thread's wait queue.
waiting_thread->ClearWaitQueue();
// Cancel the thread task.
if (cancel_timer_task) {
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
}
}
void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
[[maybe_unused]] ResultCode wait_result) {}
} // namespace Kernel

View File

@@ -4,6 +4,7 @@
#pragma once
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
@@ -11,71 +12,24 @@ namespace Kernel {
class KThreadQueue {
public:
explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {}
virtual ~KThreadQueue() = default;
bool IsEmpty() const {
return wait_list.empty();
}
KThread::WaiterList::iterator begin() {
return wait_list.begin();
}
KThread::WaiterList::iterator end() {
return wait_list.end();
}
bool SleepThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// If the thread needs terminating, don't enqueue it.
if (t->IsTerminationRequested()) {
return false;
}
// Set the thread's queue and mark it as waiting.
t->SetSleepingQueue(this);
t->SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(*t);
return true;
}
void WakeupThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// Remove the thread from the queue.
wait_list.erase(wait_list.iterator_to(*t));
// Mark the thread as no longer sleeping.
t->SetState(ThreadState::Runnable);
t->SetSleepingQueue(nullptr);
}
KThread* WakeupFrontThread() {
KScopedSchedulerLock sl{kernel};
if (wait_list.empty()) {
return nullptr;
} else {
// Remove the thread from the queue.
auto it = wait_list.begin();
KThread* thread = std::addressof(*it);
wait_list.erase(it);
ASSERT(thread->GetState() == ThreadState::Waiting);
// Mark the thread as no longer sleeping.
thread->SetState(ThreadState::Runnable);
thread->SetSleepingQueue(nullptr);
return thread;
}
}
virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
ResultCode wait_result);
virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task);
private:
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
class KThreadQueueWithoutEndWait : public KThreadQueue {
public:
explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
virtual void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
};
} // namespace Kernel

View File

@@ -31,6 +31,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
@@ -310,23 +311,28 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
auto& kernel = system.Kernel();
// Create the wait queue.
KThreadQueue wait_queue(kernel);
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
thread->SetState(ThreadState::Waiting);
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
// This is a synchronous request, so we should wait for our request to complete.
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
{
KScopedAutoObject session =
kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(),
system.CoreTiming());
}
}
KSynchronizationObject* dummy{};
return thread->GetWaitResult(std::addressof(dummy));
return thread->GetWaitResult();
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {

View File

@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/time_manager.h"
@@ -15,7 +16,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
thread->Wakeup();
{
KScopedSchedulerLock sl(system.Kernel());
thread->OnTimer();
}
});
}

View File

@@ -109,13 +109,18 @@ void SoftwareKeyboard::Execute() {
ShowNormalKeyboard();
}
void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) {
void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text,
bool confirmed) {
if (complete) {
return;
}
if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) {
SubmitForTextCheck(submitted_text);
if (confirmed) {
SubmitNormalOutputAndExit(result, submitted_text);
} else {
SubmitForTextCheck(submitted_text);
}
} else {
SubmitNormalOutputAndExit(result, submitted_text);
}
@@ -273,13 +278,21 @@ void SoftwareKeyboard::ProcessTextCheck() {
std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck));
std::u16string text_check_message =
swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure ||
swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm
? Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_text_check.text_check_message.data(),
swkbd_text_check.text_check_message.size())
: u"";
std::u16string text_check_message = [this, &swkbd_text_check]() -> std::u16string {
if (swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure ||
swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm) {
return swkbd_config_common.use_utf8
? Common::UTF8ToUTF16(Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(
swkbd_text_check.text_check_message.data()),
swkbd_text_check.text_check_message.size() * sizeof(char16_t)))
: Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_text_check.text_check_message.data(),
swkbd_text_check.text_check_message.size());
} else {
return u"";
}
}();
LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}",
GetTextCheckResultName(swkbd_text_check.text_check_result),
@@ -583,11 +596,12 @@ void SoftwareKeyboard::InitializeFrontendKeyboard() {
.disable_cancel_button{disable_cancel_button},
};
frontend.InitializeKeyboard(false, std::move(initialize_parameters),
[this](SwkbdResult result, std::u16string submitted_text) {
SubmitTextNormal(result, submitted_text);
},
{});
frontend.InitializeKeyboard(
false, std::move(initialize_parameters),
[this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
SubmitTextNormal(result, submitted_text, confirmed);
},
{});
}
}

View File

@@ -36,8 +36,9 @@ public:
*
* @param result SwkbdResult enum
* @param submitted_text UTF-16 encoded string
* @param confirmed Whether the text has been confirmed after TextCheckResult::Confirm
*/
void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text);
void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text, bool confirmed);
/**
* Submits the input text to the application.

View File

@@ -122,9 +122,14 @@ AOC_U::AOC_U(Core::System& system_)
{8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
{9, nullptr, "GetAddOnContentLostErrorCode"},
{10, &AOC_U::GetAddOnContentListChangedEventWithProcessId, "GetAddOnContentListChangedEventWithProcessId"},
{11, &AOC_U::NotifyMountAddOnContent, "NotifyMountAddOnContent"},
{12, &AOC_U::NotifyUnmountAddOnContent, "NotifyUnmountAddOnContent"},
{13, nullptr, "IsAddOnContentMountedForDebug"},
{50, &AOC_U::CheckAddOnContentMountStatus, "CheckAddOnContentMountStatus"},
{100, &AOC_U::CreateEcPurchasedEventManager, "CreateEcPurchasedEventManager"},
{101, &AOC_U::CreatePermanentEcPurchasedEventManager, "CreatePermanentEcPurchasedEventManager"},
{110, nullptr, "CreateContentsServiceManager"},
{200, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
};
// clang-format on
@@ -270,6 +275,27 @@ void AOC_U::GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestConte
rb.PushCopyObjects(aoc_change_event->GetReadableEvent());
}
void AOC_U::NotifyMountAddOnContent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AOC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void AOC_U::NotifyUnmountAddOnContent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AOC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void AOC_U::CheckAddOnContentMountStatus(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AOC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void AOC_U::CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AOC, "(STUBBED) called");

View File

@@ -29,6 +29,9 @@ private:
void PrepareAddOnContent(Kernel::HLERequestContext& ctx);
void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
void GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestContext& ctx);
void NotifyMountAddOnContent(Kernel::HLERequestContext& ctx);
void NotifyUnmountAddOnContent(Kernel::HLERequestContext& ctx);
void CheckAddOnContentMountStatus(Kernel::HLERequestContext& ctx);
void CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx);
void CreatePermanentEcPurchasedEventManager(Kernel::HLERequestContext& ctx);

View File

@@ -44,7 +44,7 @@ public:
{1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
{1015, nullptr, "ConfirmPlayableApplicationVideo"},
{1016, nullptr, "ConfirmShowNewsPermission"},
{1017, nullptr, "EndFreeCommunication"},
{1017, &IParentalControlService::EndFreeCommunication, "EndFreeCommunication"},
{1018, &IParentalControlService::IsFreeCommunicationAvailable, "IsFreeCommunicationAvailable"},
{1031, &IParentalControlService::IsRestrictionEnabled, "IsRestrictionEnabled"},
{1032, nullptr, "GetSafetyLevel"},
@@ -236,6 +236,13 @@ private:
rb.Push(ResultSuccess);
}
void EndFreeCommunication(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IsFreeCommunicationAvailable(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");

View File

@@ -430,15 +430,33 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
}
}
size_t FindNextUnusedLocation(const std::bitset<IR::NUM_GENERICS>& used_locations,
size_t start_offset) {
size_t FindAndSetNextUnusedLocation(std::bitset<IR::NUM_GENERICS>& used_locations,
size_t& start_offset) {
for (size_t location = start_offset; location < used_locations.size(); ++location) {
if (!used_locations.test(location)) {
start_offset = location;
used_locations.set(location);
return location;
}
}
throw RuntimeError("Unable to get an unused location for legacy attribute");
}
Id DefineLegacyInput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
size_t& start_offset) {
const Id id{DefineInput(ctx, ctx.F32[4], true)};
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
ctx.Decorate(id, spv::Decoration::Location, location);
return id;
}
Id DefineLegacyOutput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
size_t& start_offset, std::optional<u32> invocations) {
const Id id{DefineOutput(ctx, ctx.F32[4], invocations)};
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
ctx.Decorate(id, spv::Decoration::Location, location);
return id;
}
} // Anonymous namespace
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@@ -520,6 +538,64 @@ Id EmitContext::BitOffset16(const IR::Value& offset) {
return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
}
Id EmitContext::InputLegacyAttribute(IR::Attribute attribute) {
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorFrontDiffuseA) {
return input_front_color;
}
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
attribute <= IR::Attribute::ColorFrontSpecularA) {
return input_front_secondary_color;
}
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
attribute <= IR::Attribute::ColorBackDiffuseA) {
return input_back_color;
}
if (attribute >= IR::Attribute::ColorBackSpecularR &&
attribute <= IR::Attribute::ColorBackSpecularA) {
return input_back_secondary_color;
}
if (attribute == IR::Attribute::FogCoordinate) {
return input_fog_frag_coord;
}
if (attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q) {
u32 index =
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
return input_fixed_fnc_textures[index];
}
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
}
Id EmitContext::OutputLegacyAttribute(IR::Attribute attribute) {
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorFrontDiffuseA) {
return output_front_color;
}
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
attribute <= IR::Attribute::ColorFrontSpecularA) {
return output_front_secondary_color;
}
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
attribute <= IR::Attribute::ColorBackDiffuseA) {
return output_back_color;
}
if (attribute >= IR::Attribute::ColorBackSpecularR &&
attribute <= IR::Attribute::ColorBackSpecularA) {
return output_back_secondary_color;
}
if (attribute == IR::Attribute::FogCoordinate) {
return output_fog_frag_coord;
}
if (attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q) {
u32 index =
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
return output_fixed_fnc_textures[index];
}
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
}
void EmitContext::DefineCommonTypes(const Info& info) {
void_id = TypeVoid();
@@ -1279,22 +1355,26 @@ void EmitContext::DefineInputs(const IR::Program& program) {
}
size_t previous_unused_location = 0;
if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
previous_unused_location = location;
used_locations.set(location);
const Id id{DefineInput(*this, F32[4], true)};
Decorate(id, spv::Decoration::Location, location);
input_front_color = id;
input_front_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
input_front_secondary_color =
DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
input_back_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
input_back_secondary_color =
DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::FogCoordinate)) {
input_fog_frag_coord = DefineLegacyInput(*this, used_locations, previous_unused_location);
}
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
const size_t location =
FindNextUnusedLocation(used_locations, previous_unused_location);
previous_unused_location = location;
used_locations.set(location);
const Id id{DefineInput(*this, F32[4], true)};
Decorate(id, spv::Decoration::Location, location);
input_fixed_fnc_textures[index] = id;
input_fixed_fnc_textures[index] =
DefineLegacyInput(*this, used_locations, previous_unused_location);
}
}
if (stage == Stage::TessellationEval) {
@@ -1356,22 +1436,29 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
}
size_t previous_unused_location = 0;
if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
previous_unused_location = location;
used_locations.set(location);
const Id id{DefineOutput(*this, F32[4], invocations)};
Decorate(id, spv::Decoration::Location, static_cast<u32>(location));
output_front_color = id;
output_front_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
output_front_secondary_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
output_back_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
output_back_secondary_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::FogCoordinate)) {
output_fog_frag_coord =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
const size_t location =
FindNextUnusedLocation(used_locations, previous_unused_location);
previous_unused_location = location;
used_locations.set(location);
const Id id{DefineOutput(*this, F32[4], invocations)};
Decorate(id, spv::Decoration::Location, location);
output_fixed_fnc_textures[index] = id;
output_fixed_fnc_textures[index] =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
}
switch (stage) {

View File

@@ -113,6 +113,9 @@ public:
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
Id InputLegacyAttribute(IR::Attribute attribute);
Id OutputLegacyAttribute(IR::Attribute attribute);
Id Const(u32 value) {
return Constant(U32[1], value);
}
@@ -269,12 +272,20 @@ public:
Id input_position{};
Id input_front_color{};
Id input_front_secondary_color{};
Id input_back_color{};
Id input_back_secondary_color{};
Id input_fog_frag_coord{};
std::array<Id, 10> input_fixed_fnc_textures{};
std::array<Id, 32> input_generics{};
Id output_point_size{};
Id output_position{};
Id output_front_color{};
Id output_front_secondary_color{};
Id output_back_color{};
Id output_back_secondary_color{};
Id output_fog_frag_coord{};
std::array<Id, 10> output_fixed_fnc_textures{};
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};

View File

@@ -43,23 +43,12 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
}
}
bool IsFixedFncTexture(IR::Attribute attribute) {
return attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q;
}
u32 FixedFncTextureAttributeIndex(IR::Attribute attribute) {
if (!IsFixedFncTexture(attribute)) {
throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
}
return (static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4u;
}
u32 FixedFncTextureAttributeElement(IR::Attribute attribute) {
if (!IsFixedFncTexture(attribute)) {
throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
}
return static_cast<u32>(attribute) % 4u;
bool IsLegacyAttribute(IR::Attribute attribute) {
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorBackSpecularA) ||
attribute == IR::Attribute::FogCoordinate ||
(attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q);
}
template <typename... Args>
@@ -93,12 +82,16 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
}
}
if (IsFixedFncTexture(attr)) {
const u32 index{FixedFncTextureAttributeIndex(attr)};
const u32 element{FixedFncTextureAttributeElement(attr)};
const Id element_id{ctx.Const(element)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_fixed_fnc_textures[index],
element_id);
if (IsLegacyAttribute(attr)) {
if (attr == IR::Attribute::FogCoordinate) {
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
ctx.Const(0u));
} else {
const u32 element{static_cast<u32>(attr) % 4};
const Id element_id{ctx.Const(element)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
element_id);
}
}
switch (attr) {
case IR::Attribute::PointSize:
@@ -111,14 +104,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
const Id element_id{ctx.Const(element)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
}
case IR::Attribute::ColorFrontDiffuseR:
case IR::Attribute::ColorFrontDiffuseG:
case IR::Attribute::ColorFrontDiffuseB:
case IR::Attribute::ColorFrontDiffuseA: {
const u32 element{static_cast<u32>(attr) % 4};
const Id element_id{ctx.Const(element)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_front_color, element_id);
}
case IR::Attribute::ClipDistance0:
case IR::Attribute::ClipDistance1:
case IR::Attribute::ClipDistance2:
@@ -341,11 +326,17 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
const Id value{ctx.OpLoad(type->id, pointer)};
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
}
if (IsFixedFncTexture(attr)) {
const u32 index{FixedFncTextureAttributeIndex(attr)};
const Id attr_id{ctx.input_fixed_fnc_textures[index]};
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, attr_id, ctx.Const(element))};
return ctx.OpLoad(ctx.F32[1], attr_ptr);
if (IsLegacyAttribute(attr)) {
if (attr == IR::Attribute::FogCoordinate) {
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
ctx.InputLegacyAttribute(attr), ctx.Const(0u))};
return ctx.OpLoad(ctx.F32[1], attr_ptr);
} else {
const Id element_id{ctx.Const(element)};
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
ctx.InputLegacyAttribute(attr), element_id)};
return ctx.OpLoad(ctx.F32[1], attr_ptr);
}
}
switch (attr) {
case IR::Attribute::PrimitiveId:
@@ -356,13 +347,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
case IR::Attribute::PositionW:
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position,
ctx.Const(element)));
case IR::Attribute::ColorFrontDiffuseR:
case IR::Attribute::ColorFrontDiffuseG:
case IR::Attribute::ColorFrontDiffuseB:
case IR::Attribute::ColorFrontDiffuseA: {
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_front_color,
ctx.Const(element)));
}
case IR::Attribute::InstanceId:
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));

View File

@@ -103,6 +103,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
VK_FORMAT_A8B8G8R8_SNORM_PACK32,
VK_FORMAT_A8B8G8R8_SINT_PACK32,
VK_FORMAT_A8B8G8R8_SRGB_PACK32,
VK_FORMAT_R5G6B5_UNORM_PACK16,
VK_FORMAT_B5G6R5_UNORM_PACK16,
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_FORMAT_A2B10G10R10_UINT_PACK32,

View File

@@ -413,7 +413,7 @@ void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
? ui->text_edit_osk->toPlainText().toStdU16String()
: ui->line_edit_osk->text().toStdU16String();
emit SubmitNormalText(SwkbdResult::Ok, std::move(text));
emit SubmitNormalText(SwkbdResult::Ok, std::move(text), true);
break;
}
}
@@ -1510,7 +1510,8 @@ QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
void QtSoftwareKeyboard::InitializeKeyboard(
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) {
if (is_inline) {
@@ -1609,8 +1610,8 @@ void QtSoftwareKeyboard::ExitKeyboard() const {
}
void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result,
std::u16string submitted_text) const {
submit_normal_callback(result, submitted_text);
std::u16string submitted_text, bool confirmed) const {
submit_normal_callback(result, submitted_text, confirmed);
}
void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,

View File

@@ -51,8 +51,8 @@ public:
void ExitKeyboard();
signals:
void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
std::u16string submitted_text) const;
void SubmitNormalText(Service::AM::Applets::SwkbdResult result, std::u16string submitted_text,
bool confirmed = false) const;
void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
std::u16string submitted_text, s32 cursor_position) const;
@@ -234,7 +234,7 @@ public:
void InitializeKeyboard(
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) override;
@@ -272,13 +272,13 @@ signals:
void MainWindowExitKeyboard() const;
private:
void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
std::u16string submitted_text) const;
void SubmitNormalText(Service::AM::Applets::SwkbdResult result, std::u16string submitted_text,
bool confirmed) const;
void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
std::u16string submitted_text, s32 cursor_position) const;
mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback;
mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback;

View File

@@ -299,6 +299,8 @@ GMainWindow::GMainWindow()
SDL_EnableScreenSaver();
#endif
Common::Log::Start();
QStringList args = QApplication::arguments();
if (args.size() < 2) {
@@ -483,8 +485,9 @@ void GMainWindow::SoftwareKeyboardInitialize(
} else {
connect(
software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this,
[this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text) {
emit SoftwareKeyboardSubmitNormalText(result, submitted_text);
[this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text,
bool confirmed) {
emit SoftwareKeyboardSubmitNormalText(result, submitted_text, confirmed);
},
Qt::QueuedConnection);
}

View File

@@ -150,7 +150,7 @@ signals:
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
void SoftwareKeyboardSubmitNormalText(Service::AM::Applets::SwkbdResult result,
std::u16string submitted_text);
std::u16string submitted_text, bool confirmed);
void SoftwareKeyboardSubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
std::u16string submitted_text, s32 cursor_position);