summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/kernel/condition_variable.cpp64
-rw-r--r--src/core/hle/kernel/condition_variable.h63
-rw-r--r--src/core/hle/kernel/errors.h1
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/mutex.cpp179
-rw-r--r--src/core/hle/kernel/mutex.h88
-rw-r--r--src/core/hle/kernel/svc.cpp162
-rw-r--r--src/core/hle/kernel/thread.cpp66
-rw-r--r--src/core/hle/kernel/thread.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp45
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp6
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h6
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/video_core/memory_manager.cpp53
-rw-r--r--src/video_core/memory_manager.h6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h14
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/video_core/textures/texture.h1
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp2
-rw-r--r--src/yuzu/debugger/wait_tree.cpp86
-rw-r--r--src/yuzu/debugger/wait_tree.h43
30 files changed, 394 insertions, 570 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c1a645460..b3807c204 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -42,8 +42,6 @@ add_library(core STATIC
hle/kernel/client_port.h
hle/kernel/client_session.cpp
hle/kernel/client_session.h
- hle/kernel/condition_variable.cpp
- hle/kernel/condition_variable.h
hle/kernel/errors.h
hle/kernel/event.cpp
hle/kernel/event.h
diff --git a/src/core/hle/kernel/condition_variable.cpp b/src/core/hle/kernel/condition_variable.cpp
deleted file mode 100644
index a786d7f74..000000000
--- a/src/core/hle/kernel/condition_variable.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/assert.h"
-#include "core/hle/kernel/condition_variable.h"
-#include "core/hle/kernel/errors.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object_address_table.h"
-#include "core/hle/kernel/thread.h"
-
-namespace Kernel {
-
-ConditionVariable::ConditionVariable() {}
-ConditionVariable::~ConditionVariable() {}
-
-ResultVal<SharedPtr<ConditionVariable>> ConditionVariable::Create(VAddr guest_addr,
- std::string name) {
- SharedPtr<ConditionVariable> condition_variable(new ConditionVariable);
-
- condition_variable->name = std::move(name);
- condition_variable->guest_addr = guest_addr;
- condition_variable->mutex_addr = 0;
-
- // Condition variables are referenced by guest address, so track this in the kernel
- g_object_address_table.Insert(guest_addr, condition_variable);
-
- return MakeResult<SharedPtr<ConditionVariable>>(std::move(condition_variable));
-}
-
-bool ConditionVariable::ShouldWait(Thread* thread) const {
- return GetAvailableCount() <= 0;
-}
-
-void ConditionVariable::Acquire(Thread* thread) {
- if (GetAvailableCount() <= 0)
- return;
-
- SetAvailableCount(GetAvailableCount() - 1);
-}
-
-ResultCode ConditionVariable::Release(s32 target) {
- if (target == -1) {
- // When -1, wake up all waiting threads
- SetAvailableCount(static_cast<s32>(GetWaitingThreads().size()));
- WakeupAllWaitingThreads();
- } else {
- // Otherwise, wake up just a single thread
- SetAvailableCount(target);
- WakeupWaitingThread(GetHighestPriorityReadyThread());
- }
-
- return RESULT_SUCCESS;
-}
-
-s32 ConditionVariable::GetAvailableCount() const {
- return Memory::Read32(guest_addr);
-}
-
-void ConditionVariable::SetAvailableCount(s32 value) const {
- Memory::Write32(guest_addr, value);
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/condition_variable.h b/src/core/hle/kernel/condition_variable.h
deleted file mode 100644
index 1c9f06769..000000000
--- a/src/core/hle/kernel/condition_variable.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <string>
-#include <queue>
-#include "common/common_types.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/wait_object.h"
-#include "core/hle/result.h"
-
-namespace Kernel {
-
-class ConditionVariable final : public WaitObject {
-public:
- /**
- * Creates a condition variable.
- * @param guest_addr Address of the object tracking the condition variable in guest memory. If
- * specified, this condition variable will update the guest object when its state changes.
- * @param name Optional name of condition variable.
- * @return The created condition variable.
- */
- static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr,
- std::string name = "Unknown");
-
- std::string GetTypeName() const override {
- return "ConditionVariable";
- }
- std::string GetName() const override {
- return name;
- }
-
- static const HandleType HANDLE_TYPE = HandleType::ConditionVariable;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
-
- s32 GetAvailableCount() const;
- void SetAvailableCount(s32 value) const;
-
- std::string name; ///< Name of condition variable (optional)
- VAddr guest_addr; ///< Address of the guest condition variable value
- VAddr mutex_addr; ///< (optional) Address of guest mutex value associated with this condition
- ///< variable, used for implementing events
-
- bool ShouldWait(Thread* thread) const override;
- void Acquire(Thread* thread) override;
-
- /**
- * Releases a slot from a condition variable.
- * @param target The number of threads to wakeup, -1 is all.
- * @return ResultCode indicating if the operation succeeded.
- */
- ResultCode Release(s32 target);
-
-private:
- ConditionVariable();
- ~ConditionVariable() override;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 29d8dfdaa..5be20c878 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -20,6 +20,7 @@ enum {
MaxConnectionsReached = 52,
// Confirmed Switch OS error codes
+ MisalignedAddress = 102,
InvalidHandle = 114,
Timeout = 117,
SynchronizationCanceled = 118,
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 053bf4e17..402ae900f 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -18,12 +18,10 @@ using Handle = u32;
enum class HandleType : u32 {
Unknown,
Event,
- Mutex,
SharedMemory,
Thread,
Process,
AddressArbiter,
- ConditionVariable,
Timer,
ResourceLimit,
CodeSet,
@@ -63,9 +61,7 @@ public:
bool IsWaitable() const {
switch (GetHandleType()) {
case HandleType::Event:
- case HandleType::Mutex:
case HandleType::Thread:
- case HandleType::ConditionVariable:
case HandleType::Timer:
case HandleType::ServerPort:
case HandleType::ServerSession:
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 0b9dc700c..63733ad79 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -7,6 +7,7 @@
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "core/core.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
@@ -15,124 +16,120 @@
namespace Kernel {
-void ReleaseThreadMutexes(Thread* thread) {
- for (auto& mtx : thread->held_mutexes) {
- mtx->SetHasWaiters(false);
- mtx->SetHoldingThread(nullptr);
- mtx->WakeupAllWaitingThreads();
- }
- thread->held_mutexes.clear();
-}
+/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
+/// those.
+static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
+ SharedPtr<Thread> current_thread, VAddr mutex_addr) {
-Mutex::Mutex() {}
-Mutex::~Mutex() {}
+ SharedPtr<Thread> highest_priority_thread;
+ u32 num_waiters = 0;
-SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr,
- std::string name) {
- SharedPtr<Mutex> mutex(new Mutex);
+ for (auto& thread : current_thread->wait_mutex_threads) {
+ if (thread->mutex_wait_address != mutex_addr)
+ continue;
- mutex->guest_addr = guest_addr;
- mutex->name = std::move(name);
+ ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
- // If mutex was initialized with a holding thread, acquire it by the holding thread
- if (holding_thread) {
- mutex->Acquire(holding_thread.get());
+ ++num_waiters;
+ if (highest_priority_thread == nullptr ||
+ thread->GetPriority() < highest_priority_thread->GetPriority()) {
+ highest_priority_thread = thread;
+ }
}
- // Mutexes are referenced by guest address, so track this in the kernel
- g_object_address_table.Insert(guest_addr, mutex);
-
- return mutex;
+ return {highest_priority_thread, num_waiters};
}
-bool Mutex::ShouldWait(Thread* thread) const {
- auto holding_thread = GetHoldingThread();
- return holding_thread != nullptr && thread != holding_thread;
+/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
+static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
+ SharedPtr<Thread> new_owner) {
+ auto threads = current_thread->wait_mutex_threads;
+ for (auto& thread : threads) {
+ if (thread->mutex_wait_address != mutex_addr)
+ continue;
+
+ ASSERT(thread->lock_owner == current_thread);
+ current_thread->RemoveMutexWaiter(thread);
+ if (new_owner != thread)
+ new_owner->AddMutexWaiter(thread);
+ }
}
-void Mutex::Acquire(Thread* thread) {
- ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
+ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
+ Handle requesting_thread_handle) {
+ // The mutex address must be 4-byte aligned
+ if ((address % sizeof(u32)) != 0) {
+ return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
+ }
- priority = thread->current_priority;
- thread->held_mutexes.insert(this);
- SetHoldingThread(thread);
- thread->UpdatePriority();
- Core::System::GetInstance().PrepareReschedule();
-}
+ SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
+ SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
-ResultCode Mutex::Release(Thread* thread) {
- auto holding_thread = GetHoldingThread();
- ASSERT(holding_thread);
+ // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
+ // thread.
+ ASSERT(requesting_thread == GetCurrentThread());
- // We can only release the mutex if it's held by the calling thread.
- ASSERT(thread == holding_thread);
+ u32 addr_value = Memory::Read32(address);
+
+ // If the mutex isn't being held, just return success.
+ if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
+ return RESULT_SUCCESS;
+ }
+
+ if (holding_thread == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ // Wait until the mutex is released
+ GetCurrentThread()->mutex_wait_address = address;
+ GetCurrentThread()->wait_handle = requesting_thread_handle;
+
+ GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX;
+ GetCurrentThread()->wakeup_callback = nullptr;
+
+ // Update the lock holder thread's priority to prevent priority inversion.
+ holding_thread->AddMutexWaiter(GetCurrentThread());
- holding_thread->held_mutexes.erase(this);
- holding_thread->UpdatePriority();
- SetHoldingThread(nullptr);
- SetHasWaiters(!GetWaitingThreads().empty());
- WakeupAllWaitingThreads();
Core::System::GetInstance().PrepareReschedule();
return RESULT_SUCCESS;
}
-void Mutex::AddWaitingThread(SharedPtr<Thread> thread) {
- WaitObject::AddWaitingThread(thread);
- thread->pending_mutexes.insert(this);
- SetHasWaiters(true);
- UpdatePriority();
-}
-
-void Mutex::RemoveWaitingThread(Thread* thread) {
- WaitObject::RemoveWaitingThread(thread);
- thread->pending_mutexes.erase(this);
- if (!GetHasWaiters())
- SetHasWaiters(!GetWaitingThreads().empty());
- UpdatePriority();
-}
+ResultCode Mutex::Release(VAddr address) {
+ // The mutex address must be 4-byte aligned
+ if ((address % sizeof(u32)) != 0) {
+ return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
+ }
-void Mutex::UpdatePriority() {
- if (!GetHoldingThread())
- return;
+ auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
- u32 best_priority = THREADPRIO_LOWEST;
- for (auto& waiter : GetWaitingThreads()) {
- if (waiter->current_priority < best_priority)
- best_priority = waiter->current_priority;
+ // There are no more threads waiting for the mutex, release it completely.
+ if (thread == nullptr) {
+ ASSERT(GetCurrentThread()->wait_mutex_threads.empty());
+ Memory::Write32(address, 0);
+ return RESULT_SUCCESS;
}
- if (best_priority != priority) {
- priority = best_priority;
- GetHoldingThread()->UpdatePriority();
- }
-}
+ // Transfer the ownership of the mutex from the previous owner to the new one.
+ TransferMutexOwnership(address, GetCurrentThread(), thread);
-Handle Mutex::GetOwnerHandle() const {
- GuestState guest_state{Memory::Read32(guest_addr)};
- return guest_state.holding_thread_handle;
-}
+ u32 mutex_value = thread->wait_handle;
-SharedPtr<Thread> Mutex::GetHoldingThread() const {
- GuestState guest_state{Memory::Read32(guest_addr)};
- return g_handle_table.Get<Thread>(guest_state.holding_thread_handle);
-}
+ if (num_waiters >= 2) {
+ // Notify the guest that there are still some threads waiting for the mutex
+ mutex_value |= Mutex::MutexHasWaitersFlag;
+ }
-void Mutex::SetHoldingThread(SharedPtr<Thread> thread) {
- GuestState guest_state{Memory::Read32(guest_addr)};
- guest_state.holding_thread_handle.Assign(thread ? thread->guest_handle : 0);
- Memory::Write32(guest_addr, guest_state.raw);
-}
+ // Grant the mutex to the next waiting thread and resume it.
+ Memory::Write32(address, mutex_value);
-bool Mutex::GetHasWaiters() const {
- GuestState guest_state{Memory::Read32(guest_addr)};
- return guest_state.has_waiters != 0;
-}
+ ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
+ thread->ResumeFromWait();
-void Mutex::SetHasWaiters(bool has_waiters) {
- GuestState guest_state{Memory::Read32(guest_addr)};
- guest_state.has_waiters.Assign(has_waiters ? 1 : 0);
- Memory::Write32(guest_addr, guest_state.raw);
-}
+ thread->lock_owner = nullptr;
+ thread->condvar_wait_address = 0;
+ thread->mutex_wait_address = 0;
+ thread->wait_handle = 0;
+ return RESULT_SUCCESS;
+}
} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 38db21005..3117e7c70 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -15,87 +15,23 @@ namespace Kernel {
class Thread;
-class Mutex final : public WaitObject {
+class Mutex final {
public:
- /**
- * Creates a mutex.
- * @param holding_thread Specifies a thread already holding the mutex. If not nullptr, this
- * thread will acquire the mutex.
- * @param guest_addr Address of the object tracking the mutex in guest memory. If specified,
- * this mutex will update the guest object when its state changes.
- * @param name Optional name of mutex
- * @return Pointer to new Mutex object
- */
- static SharedPtr<Mutex> Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr = 0,
- std::string name = "Unknown");
+ /// Flag that indicates that a mutex still has threads waiting for it.
+ static constexpr u32 MutexHasWaitersFlag = 0x40000000;
+ /// Mask of the bits in a mutex address value that contain the mutex owner.
+ static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
- std::string GetTypeName() const override {
- return "Mutex";
- }
- std::string GetName() const override {
- return name;
- }
+ /// Attempts to acquire a mutex at the specified address.
+ static ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
+ Handle requesting_thread_handle);
- static const HandleType HANDLE_TYPE = HandleType::Mutex;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
-
- u32 priority; ///< The priority of the mutex, used for priority inheritance.
- std::string name; ///< Name of mutex (optional)
- VAddr guest_addr; ///< Address of the guest mutex value
-
- /**
- * Elevate the mutex priority to the best priority
- * among the priorities of all its waiting threads.
- */
- void UpdatePriority();
-
- bool ShouldWait(Thread* thread) const override;
- void Acquire(Thread* thread) override;
-
- void AddWaitingThread(SharedPtr<Thread> thread) override;
- void RemoveWaitingThread(Thread* thread) override;
-
- /**
- * Attempts to release the mutex from the specified thread.
- * @param thread Thread that wants to release the mutex.
- * @returns The result code of the operation.
- */
- ResultCode Release(Thread* thread);
-
- /// Gets the handle to the holding process stored in the guest state.
- Handle GetOwnerHandle() const;
-
- /// Gets the Thread pointed to by the owner handle
- SharedPtr<Thread> GetHoldingThread() const;
- /// Sets the holding process handle in the guest state.
- void SetHoldingThread(SharedPtr<Thread> thread);
-
- /// Returns the has_waiters bit in the guest state.
- bool GetHasWaiters() const;
- /// Sets the has_waiters bit in the guest state.
- void SetHasWaiters(bool has_waiters);
+ /// Releases the mutex at the specified address.
+ static ResultCode Release(VAddr address);
private:
- Mutex();
- ~Mutex() override;
-
- /// Object in guest memory used to track the mutex state
- union GuestState {
- u32_le raw;
- /// Handle of the thread that currently holds the mutex, 0 if available
- BitField<0, 30, u32_le> holding_thread_handle;
- /// 1 when there are threads waiting for this mutex, otherwise 0
- BitField<30, 1, u32_le> has_waiters;
- };
- static_assert(sizeof(GuestState) == 4, "GuestState size is incorrect");
+ Mutex() = default;
+ ~Mutex() = default;
};
-/**
- * Releases all the mutexes held by the specified thread
- * @param thread Thread that is holding the mutexes
- */
-void ReleaseThreadMutexes(Thread* thread);
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 633740992..c22da6e47 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -13,7 +13,6 @@
#include "core/core_timing.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/condition_variable.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/mutex.h"
@@ -262,32 +261,14 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
"requesting_current_thread_handle=0x%08X",
holding_thread_handle, mutex_addr, requesting_thread_handle);
- SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
- SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
-
- ASSERT(requesting_thread);
- ASSERT(requesting_thread == GetCurrentThread());
-
- SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr);
- if (!mutex) {
- // Create a new mutex for the specified address if one does not already exist
- mutex = Mutex::Create(holding_thread, mutex_addr);
- mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
- }
-
- ASSERT(holding_thread == mutex->GetHoldingThread());
-
- return WaitSynchronization1(mutex, requesting_thread.get());
+ return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle);
}
/// Unlock a mutex
static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr);
- SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr);
- ASSERT(mutex);
-
- return mutex->Release(GetCurrentThread());
+ return Mutex::Release(mutex_addr);
}
/// Break program execution
@@ -412,11 +393,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
}
thread->SetPriority(priority);
- thread->UpdatePriority();
-
- // Update the mutexes that this thread is waiting for
- for (auto& mutex : thread->pending_mutexes)
- mutex->UpdatePriority();
Core::System::GetInstance().PrepareReschedule();
return RESULT_SUCCESS;
@@ -634,103 +610,75 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
- SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr);
- if (!mutex) {
- // Create a new mutex for the specified address if one does not already exist
- mutex = Mutex::Create(thread, mutex_addr);
- mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
- }
-
- SharedPtr<ConditionVariable> condition_variable =
- g_object_address_table.Get<ConditionVariable>(condition_variable_addr);
- if (!condition_variable) {
- // Create a new condition_variable for the specified address if one does not already exist
- condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap();
- condition_variable->name =
- Common::StringFromFormat("condition-variable-%llx", condition_variable_addr);
- }
-
- if (condition_variable->mutex_addr) {
- // Previously created the ConditionVariable using WaitProcessWideKeyAtomic, verify
- // everything is correct
- ASSERT(condition_variable->mutex_addr == mutex_addr);
- } else {
- // Previously created the ConditionVariable using SignalProcessWideKey, set the mutex
- // associated with it
- condition_variable->mutex_addr = mutex_addr;
- }
+ CASCADE_CODE(Mutex::Release(mutex_addr));
- if (mutex->GetOwnerHandle()) {
- // Release the mutex if the current thread is holding it
- mutex->Release(thread.get());
- }
+ SharedPtr<Thread> current_thread = GetCurrentThread();
+ current_thread->condvar_wait_address = condition_variable_addr;
+ current_thread->mutex_wait_address = mutex_addr;
+ current_thread->wait_handle = thread_handle;
+ current_thread->status = THREADSTATUS_WAIT_MUTEX;
+ current_thread->wakeup_callback = nullptr;
- auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason,
- SharedPtr<Thread> thread,
- SharedPtr<WaitObject> object, size_t index) {
- ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
+ current_thread->WakeAfterDelay(nano_seconds);
- if (reason == ThreadWakeupReason::Timeout) {
- thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
- return true;
- }
+ // Note: Deliberately don't attempt to inherit the lock owner's priority.
- ASSERT(reason == ThreadWakeupReason::Signal);
+ Core::System::GetInstance().PrepareReschedule();
+ return RESULT_SUCCESS;
+}
- // Now try to acquire the mutex and don't resume if it's not available.
- if (!mutex->ShouldWait(thread.get())) {
- mutex->Acquire(thread.get());
- thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
- return true;
- }
+/// Signal process wide key
+static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) {
+ LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x",
+ condition_variable_addr, target);
- if (nano_seconds == 0) {
- thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
- return true;
- }
+ u32 processed = 0;
+ auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
- thread->wait_objects = {mutex};
- mutex->AddWaitingThread(thread);
- thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
+ for (auto& thread : thread_list) {
+ if (thread->condvar_wait_address != condition_variable_addr)
+ continue;
- // Create an event to wake the thread up after the
- // specified nanosecond delay has passed
- thread->WakeAfterDelay(nano_seconds);
- thread->wakeup_callback = DefaultThreadWakeupCallback;
+ // Only process up to 'target' threads, unless 'target' is -1, in which case process
+ // them all.
+ if (target != -1 && processed >= target)
+ break;
- Core::System::GetInstance().PrepareReschedule();
+ // If the mutex is not yet acquired, acquire it.
+ u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
- return false;
- };
- CASCADE_CODE(
- WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback));
+ if (mutex_val == 0) {
+ // We were able to acquire the mutex, resume this thread.
+ Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
+ ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
+ thread->ResumeFromWait();
- return RESULT_SUCCESS;
-}
+ auto lock_owner = thread->lock_owner;
+ if (lock_owner)
+ lock_owner->RemoveMutexWaiter(thread);
-/// Signal process wide key
-static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) {
- LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x",
- condition_variable_addr, target);
+ thread->lock_owner = nullptr;
+ thread->mutex_wait_address = 0;
+ thread->condvar_wait_address = 0;
+ thread->wait_handle = 0;
+ } else {
+ // Couldn't acquire the mutex, block the thread.
+ Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
+ auto owner = g_handle_table.Get<Thread>(owner_handle);
+ ASSERT(owner);
+ ASSERT(thread->status != THREADSTATUS_RUNNING);
+ thread->status = THREADSTATUS_WAIT_MUTEX;
+ thread->wakeup_callback = nullptr;
- // Wakeup all or one thread - Any other value is unimplemented
- ASSERT(target == -1 || target == 1);
+ // Signal that the mutex now has a waiting thread.
+ Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
- SharedPtr<ConditionVariable> condition_variable =
- g_object_address_table.Get<ConditionVariable>(condition_variable_addr);
- if (!condition_variable) {
- // Create a new condition_variable for the specified address if one does not already exist
- condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap();
- condition_variable->name =
- Common::StringFromFormat("condition-variable-%llx", condition_variable_addr);
- }
+ owner->AddMutexWaiter(thread);
- CASCADE_CODE(condition_variable->Release(target));
+ Core::System::GetInstance().PrepareReschedule();
+ }
- if (condition_variable->mutex_addr) {
- // If a mutex was created for this condition_variable, wait the current thread on it
- SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(condition_variable->mutex_addr);
- return WaitSynchronization1(mutex, GetCurrentThread());
+ ++processed;
}
return RESULT_SUCCESS;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f3a8aa4aa..36222d45f 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -77,9 +77,6 @@ void Thread::Stop() {
}
wait_objects.clear();
- // Release all the mutexes that this thread holds
- ReleaseThreadMutexes(this);
-
// Mark the TLS slot in the thread's page as free.
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot =
@@ -126,6 +123,19 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
+ if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
+ thread->wait_handle) {
+ ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
+ thread->mutex_wait_address = 0;
+ thread->condvar_wait_address = 0;
+ thread->wait_handle = 0;
+
+ auto lock_owner = thread->lock_owner;
+ // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
+ // and don't have a lock owner.
+ ASSERT(lock_owner == nullptr);
+ }
+
if (resume)
thread->ResumeFromWait();
}
@@ -151,6 +161,7 @@ void Thread::ResumeFromWait() {
case THREADSTATUS_WAIT_HLE_EVENT:
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
+ case THREADSTATUS_WAIT_MUTEX:
break;
case THREADSTATUS_READY:
@@ -256,7 +267,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->last_running_ticks = CoreTiming::GetTicks();
thread->processor_id = processor_id;
thread->wait_objects.clear();
- thread->wait_address = 0;
+ thread->mutex_wait_address = 0;
+ thread->condvar_wait_address = 0;
+ thread->wait_handle = 0;
thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
thread->owner_process = owner_process;
@@ -317,17 +330,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
void Thread::SetPriority(u32 priority) {
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value.");
- Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
- nominal_priority = current_priority = priority;
-}
-
-void Thread::UpdatePriority() {
- u32 best_priority = nominal_priority;
- for (auto& mutex : held_mutexes) {
- if (mutex->priority < best_priority)
- best_priority = mutex->priority;
- }
- BoostPriority(best_priority);
+ nominal_priority = priority;
+ UpdatePriority();
}
void Thread::BoostPriority(u32 priority) {
@@ -377,6 +381,38 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + CommandHeaderOffset;
}
+void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
+ thread->lock_owner = this;
+ wait_mutex_threads.emplace_back(std::move(thread));
+ UpdatePriority();
+}
+
+void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
+ boost::remove_erase(wait_mutex_threads, thread);
+ thread->lock_owner = nullptr;
+ UpdatePriority();
+}
+
+void Thread::UpdatePriority() {
+ // Find the highest priority among all the threads that are waiting for this thread's lock
+ u32 new_priority = nominal_priority;
+ for (const auto& thread : wait_mutex_threads) {
+ if (thread->nominal_priority < new_priority)
+ new_priority = thread->nominal_priority;
+ }
+
+ if (new_priority == current_priority)
+ return;
+
+ Core::System::GetInstance().Scheduler().SetThreadPriority(this, new_priority);
+
+ current_priority = new_priority;
+
+ // Recursively update the priority of the thread that depends on the priority of this one.
+ if (lock_owner)
+ lock_owner->UpdatePriority();
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index dbf47e269..e0a3c0934 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -18,7 +18,7 @@
enum ThreadPriority : u32 {
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
- THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps
+ THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
THREADPRIO_LOWEST = 63, ///< Lowest thread priority
};
@@ -43,6 +43,7 @@ enum ThreadStatus {
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
+ THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
THREADSTATUS_DORMANT, ///< Created but not yet made ready
THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
};
@@ -54,7 +55,6 @@ enum class ThreadWakeupReason {
namespace Kernel {
-class Mutex;
class Process;
class Thread final : public WaitObject {
@@ -104,17 +104,20 @@ public:
void SetPriority(u32 priority);
/**
- * Boost's a thread's priority to the best priority among the thread's held mutexes.
- * This prevents priority inversion via priority inheritance.
- */
- void UpdatePriority();
-
- /**
* Temporarily boosts the thread's priority until the next time it is scheduled
* @param priority The new priority
*/
void BoostPriority(u32 priority);
+ /// Adds a thread to the list of threads that are waiting for a lock held by this thread.
+ void AddMutexWaiter(SharedPtr<Thread> thread);
+
+ /// Removes a thread from the list of threads that are waiting for a lock held by this thread.
+ void RemoveMutexWaiter(SharedPtr<Thread> thread);
+
+ /// Recalculates the current priority taking into account priority inheritance.
+ void UpdatePriority();
+
/**
* Gets the thread's thread ID
* @return The thread's ID
@@ -205,19 +208,22 @@ public:
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
- /// Mutexes currently held by this thread, which will be released when it exits.
- boost::container::flat_set<SharedPtr<Mutex>> held_mutexes;
-
- /// Mutexes that this thread is currently waiting for.
- boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes;
-
SharedPtr<Process> owner_process; ///< Process that owns this thread
/// Objects that the thread is waiting on, in the same order as they were
// passed to WaitSynchronization1/N.
std::vector<SharedPtr<WaitObject>> wait_objects;
- VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
+ /// List of threads that are waiting for a mutex that is held by this thread.
+ std::vector<SharedPtr<Thread>> wait_mutex_threads;
+
+ /// Thread that owns the lock that this thread is waiting for.
+ SharedPtr<Thread> lock_owner;
+
+ // If waiting on a ConditionVariable, this is the ConditionVariable address
+ VAddr condvar_wait_address;
+ VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
+ Handle wait_handle; ///< The handle used to wait for the mutex.
std::string name;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 61f22b1a5..aa6c7e8dc 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -13,7 +13,7 @@
namespace Service::Nvidia::Devices {
u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 71e844959..8e7ca6123 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -27,6 +27,11 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vecto
case IoctlCommand::IocGetVaRegionsCommand:
return GetVARegions(input, output);
}
+
+ if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
+ return Remap(input, output);
+
+ UNIMPLEMENTED_MSG("Unimplemented ioctl command");
return 0;
}
@@ -56,6 +61,36 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
return 0;
}
+u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
+ size_t num_entries = input.size() / sizeof(IoctlRemapEntry);
+
+ NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries);
+
+ std::vector<IoctlRemapEntry> entries(num_entries);
+ std::memcpy(entries.data(), input.data(), input.size());
+
+ auto& gpu = Core::System::GetInstance().GPU();
+
+ for (const auto& entry : entries) {
+ NGLOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
+ entry.offset, entry.nvmap_handle, entry.pages);
+ Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
+
+ auto object = nvmap_dev->GetObject(entry.nvmap_handle);
+ ASSERT(object);
+
+ ASSERT(object->status == nvmap::Object::Status::Allocated);
+
+ u64 size = static_cast<u64>(entry.pages) << 0x10;
+ ASSERT(size <= object->size);
+
+ Tegra::GPUVAddr returned = gpu.memory_manager->MapBufferEx(object->addr, offset, size);
+ ASSERT(returned == offset);
+ }
+ std::memcpy(output.data(), entries.data(), output.size());
+ return 0;
+}
+
u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), input.size());
@@ -73,6 +108,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
auto object = nvmap_dev->GetObject(params.nvmap_handle);
ASSERT(object);
+ // We can only map objects that have already been assigned a CPU address.
+ ASSERT(object->status == nvmap::Object::Status::Allocated);
+
+ ASSERT(params.buffer_offset == 0);
+
+ // The real nvservices doesn't make a distinction between handles and ids, and
+ // object can only have one handle and it will be the same as its id. Assert that this is the
+ // case to prevent unexpected behavior.
+ ASSERT(object->id == params.nvmap_handle);
+
auto& gpu = Core::System::GetInstance().GPU();
if (params.flags & 1) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index d86c3ebd9..f2dd0c3b3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -26,6 +26,7 @@ private:
enum class IoctlCommand : u32_le {
IocInitalizeExCommand = 0x40284109,
IocAllocateSpaceCommand = 0xC0184102,
+ IocRemapCommand = 0x00000014,
IocMapBufferExCommand = 0xC0284106,
IocBindChannelCommand = 0x40044101,
IocGetVaRegionsCommand = 0xC0404108,
@@ -54,6 +55,16 @@ private:
};
static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
+ struct IoctlRemapEntry {
+ u16_le flags;
+ u16_le kind;
+ u32_le nvmap_handle;
+ INSERT_PADDING_WORDS(1);
+ u32_le offset;
+ u32_le pages;
+ };
+ static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
+
struct IoctlMapBufferEx {
u32_le flags; // bit0: fixed_offset, bit2: cacheable
u32_le kind; // -1 is default
@@ -91,6 +102,7 @@ private:
u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
+ u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 660a0f665..6e1ba1ac7 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -18,7 +18,7 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
case IoctlCommand::IocCtrlEventWaitCommand:
return IocCtrlEventWait(input, output);
}
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 18ea12ef5..b715723d3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -25,7 +25,7 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
case IoctlCommand::IocZcullGetInfo:
return ZCullGetInfo(input, output);
}
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index a16e90457..dab6d0533 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -40,7 +40,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
}
}
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
};
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 4bb1f57f6..dcf079d91 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -32,7 +32,7 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
return IocParam(input, output);
}
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 03a4fed59..e4ff2e267 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,7 +9,8 @@
#include "core/core_timing.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
-namespace Service::NVFlinger {
+namespace Service {
+namespace NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
@@ -110,4 +111,5 @@ void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_eve
buffer_wait_event = std::move(wait_event);
}
-} // namespace Service::NVFlinger
+} // namespace NVFlinger
+} // namespace Service
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 95adc4706..1de5767cb 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -13,7 +13,8 @@ namespace CoreTiming {
struct EventType;
}
-namespace Service::NVFlinger {
+namespace Service {
+namespace NVFlinger {
struct IGBPBuffer {
u32_le magic;
@@ -97,4 +98,5 @@ private:
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
};
-} // namespace Service::NVFlinger
+} // namespace NVFlinger
+} // namespace Service
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index b5133e4d6..3853cfa1a 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -137,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(base_addr, 48, Memory::DEFAULT_STACK_SIZE);
+ process->Run(base_addr, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 3bc10ed0d..962bed2ab 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -165,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::DEFAULT_STACK_SIZE);
+ process->Run(Memory::PROCESS_IMAGE_VADDR, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 2789a4ca1..2e1edee03 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/memory_manager.h"
@@ -11,7 +12,8 @@ PAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
boost::optional<PAddr> paddr = FindFreeBlock(size, align);
ASSERT(paddr);
- for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) {
+ for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
+ ASSERT(PageSlot(*paddr + offset) == static_cast<u64>(PageStatus::Unmapped));
PageSlot(*paddr + offset) = static_cast<u64>(PageStatus::Allocated);
}
@@ -19,13 +21,8 @@ PAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
}
PAddr MemoryManager::AllocateSpace(PAddr paddr, u64 size, u64 align) {
- for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) {
- if (IsPageMapped(paddr + offset)) {
- return AllocateSpace(size, align);
- }
- }
-
- for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) {
+ for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
+ ASSERT(PageSlot(paddr + offset) == static_cast<u64>(PageStatus::Unmapped));
PageSlot(paddr + offset) = static_cast<u64>(PageStatus::Allocated);
}
@@ -33,12 +30,11 @@ PAddr MemoryManager::AllocateSpace(PAddr paddr, u64 size, u64 align) {
}
PAddr MemoryManager::MapBufferEx(VAddr vaddr, u64 size) {
- vaddr &= ~Memory::PAGE_MASK;
-
- boost::optional<PAddr> paddr = FindFreeBlock(size);
+ boost::optional<PAddr> paddr = FindFreeBlock(size, PAGE_SIZE);
ASSERT(paddr);
- for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) {
+ for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
+ ASSERT(PageSlot(*paddr + offset) == static_cast<u64>(PageStatus::Unmapped));
PageSlot(*paddr + offset) = vaddr + offset;
}
@@ -46,16 +42,10 @@ PAddr MemoryManager::MapBufferEx(VAddr vaddr, u64 size) {
}
PAddr MemoryManager::MapBufferEx(VAddr vaddr, PAddr paddr, u64 size) {
- vaddr &= ~Memory::PAGE_MASK;
- paddr &= ~Memory::PAGE_MASK;
+ ASSERT((paddr & PAGE_MASK) == 0);
- for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) {
- if (PageSlot(paddr + offset) != static_cast<u64>(PageStatus::Allocated)) {
- return MapBufferEx(vaddr, size);
- }
- }
-
- for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) {
+ for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
+ ASSERT(PageSlot(paddr + offset) == static_cast<u64>(PageStatus::Allocated));
PageSlot(paddr + offset) = vaddr + offset;
}
@@ -63,23 +53,20 @@ PAddr MemoryManager::MapBufferEx(VAddr vaddr, PAddr paddr, u64 size) {
}
boost::optional<PAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
- PAddr paddr{};
- u64 free_space{};
- align = (align + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
+ PAddr paddr = 0;
+ u64 free_space = 0;
+ align = (align + PAGE_MASK) & ~PAGE_MASK;
while (paddr + free_space < MAX_ADDRESS) {
if (!IsPageMapped(paddr + free_space)) {
- free_space += Memory::PAGE_SIZE;
+ free_space += PAGE_SIZE;
if (free_space >= size) {
return paddr;
}
} else {
- paddr += free_space + Memory::PAGE_SIZE;
+ paddr += free_space + PAGE_SIZE;
free_space = 0;
- const u64 remainder{paddr % align};
- if (!remainder) {
- paddr = (paddr - remainder) + align;
- }
+ paddr = Common::AlignUp(paddr, align);
}
}
@@ -89,7 +76,7 @@ boost::optional<PAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
VAddr MemoryManager::PhysicalToVirtualAddress(PAddr paddr) {
VAddr base_addr = PageSlot(paddr);
ASSERT(base_addr != static_cast<u64>(PageStatus::Unmapped));
- return base_addr + (paddr & Memory::PAGE_MASK);
+ return base_addr + (paddr & PAGE_MASK);
}
bool MemoryManager::IsPageMapped(PAddr paddr) {
@@ -97,14 +84,14 @@ bool MemoryManager::IsPageMapped(PAddr paddr) {
}
VAddr& MemoryManager::PageSlot(PAddr paddr) {
- auto& block = page_table[(paddr >> (Memory::PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK];
+ auto& block = page_table[(paddr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK];
if (!block) {
block = std::make_unique<PageBlock>();
for (unsigned index = 0; index < PAGE_BLOCK_SIZE; index++) {
(*block)[index] = static_cast<u64>(PageStatus::Unmapped);
}
}
- return (*block)[(paddr >> Memory::PAGE_BITS) & PAGE_BLOCK_MASK];
+ return (*block)[(paddr >> PAGE_BITS) & PAGE_BLOCK_MASK];
}
} // namespace Tegra
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 47da7acd6..b73e283f8 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -24,6 +24,10 @@ public:
PAddr MapBufferEx(VAddr vaddr, PAddr paddr, u64 size);
VAddr PhysicalToVirtualAddress(PAddr paddr);
+ static constexpr u64 PAGE_BITS = 16;
+ static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
+ static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
+
private:
boost::optional<PAddr> FindFreeBlock(u64 size, u64 align = 1);
bool IsPageMapped(PAddr paddr);
@@ -35,7 +39,7 @@ private:
};
static constexpr u64 MAX_ADDRESS{0x10000000000ULL};
- static constexpr u64 PAGE_TABLE_BITS{14};
+ static constexpr u64 PAGE_TABLE_BITS{10};
static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS};
static constexpr u64 PAGE_TABLE_MASK{PAGE_TABLE_SIZE - 1};
static constexpr u64 PAGE_BLOCK_BITS{14};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index ced2b8247..7410471cc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -49,6 +49,7 @@ struct FormatTuple {
static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false, 1}, // ABGR8
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false, 1}, // B5G6R5
+ {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false, 1}, // A2B10G10R10
{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT23
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT45
@@ -104,9 +105,9 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, VAddr b
static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr),
SurfaceParams::MaxPixelFormat>
morton_to_gl_fns = {
- MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
- MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
- MortonCopy<true, PixelFormat::DXT45>,
+ MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
+ MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::DXT1>,
+ MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>,
};
static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr),
@@ -114,6 +115,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr),
gl_to_morton_fns = {
MortonCopy<false, PixelFormat::ABGR8>,
MortonCopy<false, PixelFormat::B5G6R5>,
+ MortonCopy<false, PixelFormat::A2B10G10R10>,
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45 formats is not yet supported
nullptr,
nullptr,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 6861efe16..bf0fabb29 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -54,9 +54,10 @@ struct SurfaceParams {
enum class PixelFormat {
ABGR8 = 0,
B5G6R5 = 1,
- DXT1 = 2,
- DXT23 = 3,
- DXT45 = 4,
+ A2B10G10R10 = 2,
+ DXT1 = 3,
+ DXT23 = 4,
+ DXT45 = 5,
Max,
Invalid = 255,
@@ -88,6 +89,7 @@ struct SurfaceParams {
constexpr std::array<unsigned int, MaxPixelFormat> bpp_table = {
32, // ABGR8
16, // B5G6R5
+ 32, // A2B10G10R10
64, // DXT1
128, // DXT23
128, // DXT45
@@ -104,6 +106,8 @@ struct SurfaceParams {
switch (format) {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
return PixelFormat::ABGR8;
+ case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
+ return PixelFormat::A2B10G10R10;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
@@ -127,6 +131,8 @@ struct SurfaceParams {
return PixelFormat::ABGR8;
case Tegra::Texture::TextureFormat::B5G6R5:
return PixelFormat::B5G6R5;
+ case Tegra::Texture::TextureFormat::A2B10G10R10:
+ return PixelFormat::A2B10G10R10;
case Tegra::Texture::TextureFormat::DXT1:
return PixelFormat::DXT1;
case Tegra::Texture::TextureFormat::DXT23:
@@ -146,6 +152,8 @@ struct SurfaceParams {
return Tegra::Texture::TextureFormat::A8R8G8B8;
case PixelFormat::B5G6R5:
return Tegra::Texture::TextureFormat::B5G6R5;
+ case PixelFormat::A2B10G10R10:
+ return Tegra::Texture::TextureFormat::A2B10G10R10;
case PixelFormat::DXT1:
return Tegra::Texture::TextureFormat::DXT1;
case PixelFormat::DXT23:
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 4df687786..e0509f0ce 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -53,6 +53,7 @@ u32 BytesPerPixel(TextureFormat format) {
// In this case a 'pixel' actually refers to a 4x4 tile.
return 16;
case TextureFormat::A8R8G8B8:
+ case TextureFormat::A2B10G10R10:
return 4;
case TextureFormat::B5G6R5:
return 2;
@@ -78,6 +79,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
unswizzled_data.data(), true, block_height);
break;
case TextureFormat::A8R8G8B8:
+ case TextureFormat::A2B10G10R10:
case TextureFormat::B5G6R5:
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
unswizzled_data.data(), true, block_height);
@@ -100,6 +102,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::A8R8G8B8:
+ case TextureFormat::A2B10G10R10:
case TextureFormat::B5G6R5:
// TODO(Subv): For the time being just forward the same data without any decoding.
rgba_data = texture_data;
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 86e45aa88..dc004d361 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -15,6 +15,7 @@ namespace Texture {
enum class TextureFormat : u32 {
A8R8G8B8 = 0x8,
+ A2B10G10R10 = 0x9,
B5G6R5 = 0x15,
DXT1 = 0x24,
DXT23 = 0x25,
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 1e4844b57..5fada74be 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -25,6 +25,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
switch (render_target_format) {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
return Tegra::Texture::TextureFormat::A8R8G8B8;
+ case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
+ return Tegra::Texture::TextureFormat::A2B10G10R10;
default:
UNIMPLEMENTED_MSG("Unimplemented RT format");
}
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index cae2864e5..acc4c2e0b 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -6,8 +6,8 @@
#include "yuzu/util/util.h"
#include "core/core.h"
-#include "core/hle/kernel/condition_variable.h"
#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
@@ -67,6 +67,29 @@ QString WaitTreeText::GetText() const {
return text;
}
+WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) {
+ mutex_value = Memory::Read32(mutex_address);
+ owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
+ owner = Kernel::g_handle_table.Get<Kernel::Thread>(owner_handle);
+}
+
+QString WaitTreeMutexInfo::GetText() const {
+ return tr("waiting for mutex 0x%1").arg(mutex_address, 16, 16, QLatin1Char('0'));
+}
+
+std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const {
+ std::vector<std::unique_ptr<WaitTreeItem>> list;
+
+ bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0;
+
+ list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters)));
+ list.push_back(std::make_unique<WaitTreeText>(
+ tr("owner handle: 0x%1").arg(owner_handle, 8, 16, QLatin1Char('0'))));
+ if (owner != nullptr)
+ list.push_back(std::make_unique<WaitTreeThread>(*owner));
+ return list;
+}
+
WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {}
bool WaitTreeExpandableItem::IsExpandable() const {
@@ -84,11 +107,6 @@ std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitO
switch (object.GetHandleType()) {
case Kernel::HandleType::Event:
return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object));
- case Kernel::HandleType::Mutex:
- return std::make_unique<WaitTreeMutex>(static_cast<const Kernel::Mutex&>(object));
- case Kernel::HandleType::ConditionVariable:
- return std::make_unique<WaitTreeConditionVariable>(
- static_cast<const Kernel::ConditionVariable&>(object));
case Kernel::HandleType::Timer:
return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object));
case Kernel::HandleType::Thread:
@@ -160,6 +178,9 @@ QString WaitTreeThread::GetText() const {
case THREADSTATUS_WAIT_SYNCH_ANY:
status = tr("waiting for objects");
break;
+ case THREADSTATUS_WAIT_MUTEX:
+ status = tr("waiting for mutex");
+ break;
case THREADSTATUS_DORMANT:
status = tr("dormant");
break;
@@ -186,6 +207,7 @@ QColor WaitTreeThread::GetColor() const {
return QColor(Qt::GlobalColor::darkYellow);
case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY:
+ case THREADSTATUS_WAIT_MUTEX:
return QColor(Qt::GlobalColor::red);
case THREADSTATUS_DORMANT:
return QColor(Qt::GlobalColor::darkCyan);
@@ -225,11 +247,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>(
tr("last running ticks = %1").arg(thread.last_running_ticks)));
- if (thread.held_mutexes.empty()) {
- list.push_back(std::make_unique<WaitTreeText>(tr("not holding mutex")));
- } else {
- list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes));
- }
+ if (thread.mutex_wait_address != 0)
+ list.push_back(std::make_unique<WaitTreeMutexInfo>(thread.mutex_wait_address));
+ else
+ list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
+
if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread.status == THREADSTATUS_WAIT_SYNCH_ALL) {
list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
@@ -250,33 +272,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
return list;
}
-WaitTreeMutex::WaitTreeMutex(const Kernel::Mutex& object) : WaitTreeWaitObject(object) {}
-
-std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutex::GetChildren() const {
- std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
-
- const auto& mutex = static_cast<const Kernel::Mutex&>(object);
- if (mutex.GetHasWaiters()) {
- list.push_back(std::make_unique<WaitTreeText>(tr("locked by thread:")));
- list.push_back(std::make_unique<WaitTreeThread>(*mutex.GetHoldingThread()));
- } else {
- list.push_back(std::make_unique<WaitTreeText>(tr("free")));
- }
- return list;
-}
-
-WaitTreeConditionVariable::WaitTreeConditionVariable(const Kernel::ConditionVariable& object)
- : WaitTreeWaitObject(object) {}
-
-std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeConditionVariable::GetChildren() const {
- std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
-
- const auto& condition_variable = static_cast<const Kernel::ConditionVariable&>(object);
- list.push_back(std::make_unique<WaitTreeText>(
- tr("available count = %1").arg(condition_variable.GetAvailableCount())));
- return list;
-}
-
WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {}
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
@@ -293,21 +288,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
return list;
}
-WaitTreeMutexList::WaitTreeMutexList(
- const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list)
- : mutex_list(list) {}
-
-QString WaitTreeMutexList::GetText() const {
- return tr("holding mutexes");
-}
-
-std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexList::GetChildren() const {
- std::vector<std::unique_ptr<WaitTreeItem>> list(mutex_list.size());
- std::transform(mutex_list.begin(), mutex_list.end(), list.begin(),
- [](const auto& t) { return std::make_unique<WaitTreeMutex>(*t); });
- return list;
-}
-
WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list)
: thread_list(list) {}
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index e538174eb..300ba9ae4 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -16,8 +16,6 @@ class EmuThread;
namespace Kernel {
class WaitObject;
class Event;
-class Mutex;
-class ConditionVariable;
class Thread;
class Timer;
} // namespace Kernel
@@ -61,6 +59,20 @@ public:
bool IsExpandable() const override;
};
+class WaitTreeMutexInfo : public WaitTreeExpandableItem {
+ Q_OBJECT
+public:
+ explicit WaitTreeMutexInfo(VAddr mutex_address);
+ QString GetText() const override;
+ std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
+
+private:
+ VAddr mutex_address;
+ u32 mutex_value;
+ Kernel::Handle owner_handle;
+ Kernel::SharedPtr<Kernel::Thread> owner;
+};
+
class WaitTreeWaitObject : public WaitTreeExpandableItem {
Q_OBJECT
public:
@@ -104,20 +116,6 @@ public:
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
};
-class WaitTreeMutex : public WaitTreeWaitObject {
- Q_OBJECT
-public:
- explicit WaitTreeMutex(const Kernel::Mutex& object);
- std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
-};
-
-class WaitTreeConditionVariable : public WaitTreeWaitObject {
- Q_OBJECT
-public:
- explicit WaitTreeConditionVariable(const Kernel::ConditionVariable& object);
- std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
-};
-
class WaitTreeTimer : public WaitTreeWaitObject {
Q_OBJECT
public:
@@ -125,19 +123,6 @@ public:
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
};
-class WaitTreeMutexList : public WaitTreeExpandableItem {
- Q_OBJECT
-public:
- explicit WaitTreeMutexList(
- const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list);
-
- QString GetText() const override;
- std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
-
-private:
- const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& mutex_list;
-};
-
class WaitTreeThreadList : public WaitTreeExpandableItem {
Q_OBJECT
public: