Compare commits

..

7 Commits

Author SHA1 Message Date
Subv
46572d027d Kernel: Implemented mutex priority inheritance.
Verified with a hwtest and implemented based on reverse engineering.

Thread A's priority will get bumped to the highest priority among all the threads that are waiting for a mutex that A holds.
Once A releases the mutex and ownership is transferred to B, A's priority will return to normal and B's priority will be bumped.
2018-04-23 11:23:44 -05:00
Subv
a70ed9c8ae Kernel: Use 0x2C as default main thread priority for homebrew and lone NRO/NSOs 2018-04-20 21:04:35 -05:00
Subv
013778aa21 Qt: Update the WaitTree widget to show info about the current mutex of each thread. 2018-04-20 21:04:34 -05:00
Subv
be155f4d9d Kernel: Remove unused ConditionVariable class. 2018-04-20 21:04:33 -05:00
Subv
5fdfbfe25a Kernel: Remove old and unused Mutex code. 2018-04-20 21:04:32 -05:00
Subv
b18ccf9399 Kernel: Properly implemented svcWaitProcessWideKey and svcSignalProcessWideKey
They work in tandem with guest code to provide synchronization primitives along with svcArbitrateLock/Unlock
2018-04-20 21:04:27 -05:00
Subv
e81a2080eb Kernel: Corrected the implementation of svcArbitrateLock and svcArbitrateUnlock.
Switch mutexes are no longer kernel objects, they are managed in userland and only use the kernel to handle the contention case.
Mutex addresses store a special flag value (0x40000000) to notify the guest code that there are still some threads waiting for the mutex to be released. This flag is updated when a thread calls ArbitrateUnlock.

TODO:
* Fix svcWaitProcessWideKey
* Fix svcSignalProcessWideKey
* Remove the Mutex class.
2018-04-20 21:04:25 -05:00
37 changed files with 614 additions and 1196 deletions

View File

@@ -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

View File

@@ -12,13 +12,10 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/hw/hw.h"
#include "core/loader/loader.h"
#include "core/memory_setup.h"
@@ -29,8 +26,6 @@ namespace Core {
/*static*/ System System::s_instance;
System::~System() = default;
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
if (!cpu_core) {
@@ -172,12 +167,10 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
HW::Init();
Kernel::Init(system_mode);
scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
Service::Init(service_manager);
Service::Init();
GDBStub::Init();
if (!VideoCore::Init(emu_window)) {
@@ -207,26 +200,17 @@ void System::Shutdown() {
VideoCore::Shutdown();
GDBStub::Shutdown();
Service::Shutdown();
scheduler.reset();
scheduler = nullptr;
Kernel::Shutdown();
HW::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
cpu_core.reset();
telemetry_session = nullptr;
gpu_core = nullptr;
cpu_core = nullptr;
CoreTiming::Shutdown();
app_loader.reset();
app_loader = nullptr;
LOG_DEBUG(Core, "Shutdown OK");
}
Service::SM::ServiceManager& System::ServiceManager() {
return *service_manager;
}
const Service::SM::ServiceManager& System::ServiceManager() const {
return *service_manager;
}
} // namespace Core

View File

@@ -19,16 +19,10 @@
class EmuWindow;
class ARM_Interface;
namespace Service::SM {
class ServiceManager;
}
namespace Core {
class System {
public:
~System();
/**
* Gets the instance of the System singleton class.
* @returns Reference to the instance of the System singleton class.
@@ -143,9 +137,6 @@ public:
return *app_loader;
}
Service::SM::ServiceManager& ServiceManager();
const Service::SM::ServiceManager& ServiceManager() const;
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
debug_context = std::move(context);
}
@@ -180,9 +171,6 @@ private:
/// When true, signals that a reschedule should happen
bool reschedule_pending{};
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;

View File

@@ -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

View File

@@ -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

View File

@@ -20,6 +20,7 @@ enum {
MaxConnectionsReached = 52,
// Confirmed Switch OS error codes
MisalignedAddress = 102,
InvalidHandle = 114,
Timeout = 117,
SynchronizationCanceled = 118,

View File

@@ -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:

View File

@@ -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();
}
void Mutex::UpdatePriority() {
if (!GetHoldingThread())
return;
u32 best_priority = THREADPRIO_LOWEST;
for (auto& waiter : GetWaitingThreads()) {
if (waiter->current_priority < best_priority)
best_priority = waiter->current_priority;
ResultCode Mutex::Release(VAddr address) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
}
if (best_priority != priority) {
priority = best_priority;
GetHoldingThread()->UpdatePriority();
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
// 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;
}
}
Handle Mutex::GetOwnerHandle() const {
GuestState guest_state{Memory::Read32(guest_addr)};
return guest_state.holding_thread_handle;
}
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, GetCurrentThread(), thread);
SharedPtr<Thread> Mutex::GetHoldingThread() const {
GuestState guest_state{Memory::Read32(guest_addr)};
return g_handle_table.Get<Thread>(guest_state.holding_thread_handle);
}
u32 mutex_value = thread->wait_handle;
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);
}
if (num_waiters >= 2) {
// Notify the guest that there are still some threads waiting for the mutex
mutex_value |= Mutex::MutexHasWaitersFlag;
}
bool Mutex::GetHasWaiters() const {
GuestState guest_state{Memory::Read32(guest_addr)};
return guest_state.has_waiters != 0;
}
// Grant the mutex to the next waiting thread and resume it.
Memory::Write32(address, mutex_value);
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);
}
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
thread->ResumeFromWait();
thread->lock_owner = nullptr;
thread->condvar_wait_address = 0;
thread->mutex_wait_address = 0;
thread->wait_handle = 0;
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -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

View File

@@ -34,57 +34,57 @@ SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory cat
}
}
s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const {
switch (resource) {
case ResourceType::Commit:
case COMMIT:
return current_commit;
case ResourceType::Thread:
case THREAD:
return current_threads;
case ResourceType::Event:
case EVENT:
return current_events;
case ResourceType::Mutex:
case MUTEX:
return current_mutexes;
case ResourceType::Semaphore:
case SEMAPHORE:
return current_semaphores;
case ResourceType::Timer:
case TIMER:
return current_timers;
case ResourceType::SharedMemory:
case SHARED_MEMORY:
return current_shared_mems;
case ResourceType::AddressArbiter:
case ADDRESS_ARBITER:
return current_address_arbiters;
case ResourceType::CPUTime:
case CPU_TIME:
return current_cpu_time;
default:
LOG_ERROR(Kernel, "Unknown resource type=%08X", static_cast<u32>(resource));
LOG_ERROR(Kernel, "Unknown resource type=%08X", resource);
UNIMPLEMENTED();
return 0;
}
}
u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
u32 ResourceLimit::GetMaxResourceValue(u32 resource) const {
switch (resource) {
case ResourceType::Priority:
case PRIORITY:
return max_priority;
case ResourceType::Commit:
case COMMIT:
return max_commit;
case ResourceType::Thread:
case THREAD:
return max_threads;
case ResourceType::Event:
case EVENT:
return max_events;
case ResourceType::Mutex:
case MUTEX:
return max_mutexes;
case ResourceType::Semaphore:
case SEMAPHORE:
return max_semaphores;
case ResourceType::Timer:
case TIMER:
return max_timers;
case ResourceType::SharedMemory:
case SHARED_MEMORY:
return max_shared_mems;
case ResourceType::AddressArbiter:
case ADDRESS_ARBITER:
return max_address_arbiters;
case ResourceType::CPUTime:
case CPU_TIME:
return max_cpu_time;
default:
LOG_ERROR(Kernel, "Unknown resource type=%08X", static_cast<u32>(resource));
LOG_ERROR(Kernel, "Unknown resource type=%08X", resource);
UNIMPLEMENTED();
return 0;
}

View File

@@ -16,17 +16,17 @@ enum class ResourceLimitCategory : u8 {
OTHER = 3
};
enum class ResourceType {
Priority = 0,
Commit = 1,
Thread = 2,
Event = 3,
Mutex = 4,
Semaphore = 5,
Timer = 6,
SharedMemory = 7,
AddressArbiter = 8,
CPUTime = 9,
enum ResourceTypes {
PRIORITY = 0,
COMMIT = 1,
THREAD = 2,
EVENT = 3,
MUTEX = 4,
SEMAPHORE = 5,
TIMER = 6,
SHARED_MEMORY = 7,
ADDRESS_ARBITER = 8,
CPU_TIME = 9,
};
class ResourceLimit final : public Object {
@@ -60,14 +60,14 @@ public:
* @param resource Requested resource type
* @returns The current value of the resource type
*/
s32 GetCurrentResourceValue(ResourceType resource) const;
s32 GetCurrentResourceValue(u32 resource) const;
/**
* Gets the max value for the specified resource.
* @param resource Requested resource type
* @returns The max value of the resource type
*/
u32 GetMaxResourceValue(ResourceType resource) const;
u32 GetMaxResourceValue(u32 resource) const;
/// Name of resource limit object.
std::string name;

View File

@@ -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
@@ -407,16 +388,11 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) {
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
return ERR_NOT_AUTHORIZED;
}
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;
@@ -541,7 +517,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
}
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) {
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
return ERR_NOT_AUTHORIZED;
}
@@ -634,77 +610,20 @@ 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);
}
CASCADE_CODE(Mutex::Release(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);
}
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;
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;
}
current_thread->WakeAfterDelay(nano_seconds);
if (mutex->GetOwnerHandle()) {
// Release the mutex if the current thread is holding it
mutex->Release(thread.get());
}
auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason,
SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, size_t index) {
ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return true;
}
ASSERT(reason == ThreadWakeupReason::Signal);
// 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;
}
if (nano_seconds == 0) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return true;
}
thread->wait_objects = {mutex};
mutex->AddWaitingThread(thread);
thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
// Create an event to wake the thread up after the
// specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->wakeup_callback = DefaultThreadWakeupCallback;
Core::System::GetInstance().PrepareReschedule();
return false;
};
CASCADE_CODE(
WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback));
// Note: Deliberately don't attempt to inherit the lock owner's priority.
Core::System::GetInstance().PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -713,24 +632,53 @@ 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);
// Wakeup all or one thread - Any other value is unimplemented
ASSERT(target == -1 || target == 1);
u32 processed = 0;
auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
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);
}
for (auto& thread : thread_list) {
if (thread->condvar_wait_address != condition_variable_addr)
continue;
CASCADE_CODE(condition_variable->Release(target));
// Only process up to 'target' threads, unless 'target' is -1, in which case process
// them all.
if (target != -1 && processed >= target)
break;
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());
// If the mutex is not yet acquired, acquire it.
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
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();
auto lock_owner = thread->lock_owner;
if (lock_owner)
lock_owner->RemoveMutexWaiter(thread);
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;
// Signal that the mutex now has a waiting thread.
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
owner->AddMutexWaiter(thread);
Core::System::GetInstance().PrepareReschedule();
}
++processed;
}
return RESULT_SUCCESS;

View File

@@ -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();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**

View File

@@ -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 {
@@ -103,18 +103,21 @@ 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;

View File

@@ -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

View File

@@ -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

View File

@@ -145,7 +145,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead);
}
case IPC::CommandType::Control: {
Core::System::GetInstance().ServiceManager().InvokeControlRequest(context);
SM::g_service_manager->InvokeControlRequest(context);
break;
}
case IPC::CommandType::Request: {
@@ -170,40 +170,42 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
}
/// Initialize ServiceManager
void Init(std::shared_ptr<SM::ServiceManager>& sm) {
void Init() {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
// here and pass it into the respective InstallInterfaces functions.
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
SM::ServiceManager::InstallInterfaces(sm);
SM::g_service_manager = std::make_shared<SM::ServiceManager>();
SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
Account::InstallInterfaces(*sm);
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
Fatal::InstallInterfaces(*sm);
FileSystem::InstallInterfaces(*sm);
Friend::InstallInterfaces(*sm);
HID::InstallInterfaces(*sm);
LM::InstallInterfaces(*sm);
NFP::InstallInterfaces(*sm);
NIFM::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm);
Nvidia::InstallInterfaces(*sm);
PCTL::InstallInterfaces(*sm);
Sockets::InstallInterfaces(*sm);
SPL::InstallInterfaces(*sm);
SSL::InstallInterfaces(*sm);
Time::InstallInterfaces(*sm);
VI::InstallInterfaces(*sm, nv_flinger);
Set::InstallInterfaces(*sm);
Account::InstallInterfaces(*SM::g_service_manager);
AM::InstallInterfaces(*SM::g_service_manager, nv_flinger);
AOC::InstallInterfaces(*SM::g_service_manager);
APM::InstallInterfaces(*SM::g_service_manager);
Audio::InstallInterfaces(*SM::g_service_manager);
Fatal::InstallInterfaces(*SM::g_service_manager);
FileSystem::InstallInterfaces(*SM::g_service_manager);
Friend::InstallInterfaces(*SM::g_service_manager);
HID::InstallInterfaces(*SM::g_service_manager);
LM::InstallInterfaces(*SM::g_service_manager);
NFP::InstallInterfaces(*SM::g_service_manager);
NIFM::InstallInterfaces(*SM::g_service_manager);
NS::InstallInterfaces(*SM::g_service_manager);
Nvidia::InstallInterfaces(*SM::g_service_manager);
PCTL::InstallInterfaces(*SM::g_service_manager);
Sockets::InstallInterfaces(*SM::g_service_manager);
SPL::InstallInterfaces(*SM::g_service_manager);
SSL::InstallInterfaces(*SM::g_service_manager);
Time::InstallInterfaces(*SM::g_service_manager);
VI::InstallInterfaces(*SM::g_service_manager, nv_flinger);
Set::InstallInterfaces(*SM::g_service_manager);
LOG_DEBUG(Service, "initialized OK");
}
/// Shutdown ServiceManager
void Shutdown() {
SM::g_service_manager = nullptr;
g_kernel_named_ports.clear();
LOG_DEBUG(Service, "shutdown OK");
}

View File

@@ -178,7 +178,7 @@ private:
};
/// Initialize ServiceManager
void Init(std::shared_ptr<SM::ServiceManager>& sm);
void Init();
/// Shutdown ServiceManager
void Shutdown();

View File

@@ -14,8 +14,6 @@
namespace Service::SM {
ServiceManager::~ServiceManager() = default;
void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
controller_interface->InvokeRequest(context);
}
@@ -74,7 +72,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToSer
return client_port->Connect();
}
SM::~SM() = default;
std::shared_ptr<ServiceManager> g_service_manager;
/**
* SM::Initialize service function

View File

@@ -23,7 +23,7 @@ namespace Service::SM {
class SM final : public ServiceFramework<SM> {
public:
SM(std::shared_ptr<ServiceManager> service_manager);
~SM() override;
~SM() = default;
private:
void Initialize(Kernel::HLERequestContext& ctx);
@@ -44,8 +44,6 @@ class ServiceManager {
public:
static void InstallInterfaces(std::shared_ptr<ServiceManager> self);
~ServiceManager();
ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name,
unsigned int max_sessions);
ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name);
@@ -61,4 +59,6 @@ private:
std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services;
};
extern std::shared_ptr<ServiceManager> g_service_manager;
} // namespace Service::SM

View File

@@ -582,7 +582,7 @@ public:
{2203, nullptr, "SetLayerSize"},
{2204, nullptr, "GetLayerZ"},
{2205, &ISystemDisplayService::SetLayerZ, "SetLayerZ"},
{2207, &ISystemDisplayService::SetLayerVisibility, "SetLayerVisibility"},
{2207, nullptr, "SetLayerVisibility"},
{2209, nullptr, "SetLayerAlpha"},
{2312, nullptr, "CreateStrayLayer"},
{2400, nullptr, "OpenIndirectLayer"},
@@ -632,16 +632,6 @@ private:
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
bool visibility = rp.Pop<bool>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x%x, visibility=%u", layer_id,
visibility);
}
};
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
@@ -673,7 +663,7 @@ public:
{4206, nullptr, "SetDefaultDisplay"},
{6000, &IManagerDisplayService::AddToLayerStack, "AddToLayerStack"},
{6001, nullptr, "RemoveFromLayerStack"},
{6002, &IManagerDisplayService::SetLayerVisibility, "SetLayerVisibility"},
{6002, nullptr, "SetLayerVisibility"},
{6003, nullptr, "SetLayerConfig"},
{6004, nullptr, "AttachLayerPresentationTracer"},
{6005, nullptr, "DetachLayerPresentationTracer"},
@@ -755,16 +745,6 @@ private:
rb.Push(RESULT_SUCCESS);
}
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
bool visibility = rp.Pop<bool>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x%x, visibility=%u", layer_id,
visibility);
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
@@ -835,15 +815,15 @@ private:
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
rb.Push(static_cast<u64>(DisplayResolution::DockedWidth));
rb.Push(static_cast<u64>(DisplayResolution::DockedHeight));
rb.Push(static_cast<u32>(DisplayResolution::DockedWidth));
rb.Push(static_cast<u32>(DisplayResolution::DockedHeight));
} else {
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth));
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight));
rb.Push(static_cast<u32>(DisplayResolution::UndockedWidth));
rb.Push(static_cast<u32>(DisplayResolution::UndockedHeight));
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -147,36 +147,11 @@ void Maxwell3D::ProcessQueryGet() {
// VAddr before writing.
VAddr address = memory_manager.PhysicalToVirtualAddress(sequence_address);
// TODO(Subv): Support the other query units.
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
"Units other than CROP are unimplemented");
ASSERT_MSG(regs.query.query_get.short_query,
"Writing the entire query result structure is unimplemented");
u32 value = Memory::Read32(address);
u32 result = 0;
// TODO(Subv): Support the other query variables
switch (regs.query.query_get.select) {
case Regs::QuerySelect::Zero:
result = 0;
break;
default:
UNIMPLEMENTED_MSG("Unimplemented query select type %u",
static_cast<u32>(regs.query.query_get.select.Value()));
}
// TODO(Subv): Research and implement how query sync conditions work.
switch (regs.query.query_get.mode) {
case Regs::QueryMode::Write:
case Regs::QueryMode::Write2: {
case Regs::QueryMode::Write: {
// Write the current query sequence to the sequence address.
u32 sequence = regs.query.query_sequence;
Memory::Write32(address, sequence);
// TODO(Subv): Write the proper query response structure to the address when not using short
// mode.
break;
}
default:

View File

@@ -46,29 +46,6 @@ public:
enum class QueryMode : u32 {
Write = 0,
Sync = 1,
// TODO(Subv): It is currently unknown what the difference between method 2 and method 0
// is.
Write2 = 2,
};
enum class QueryUnit : u32 {
VFetch = 1,
VP = 2,
Rast = 4,
StrmOut = 5,
GP = 6,
ZCull = 7,
Prop = 10,
Crop = 15,
};
enum class QuerySelect : u32 {
Zero = 0,
};
enum class QuerySyncCondition : u32 {
NotEqual = 0,
GreaterThan = 1,
};
enum class ShaderProgram : u32 {
@@ -499,10 +476,7 @@ public:
u32 raw;
BitField<0, 2, QueryMode> mode;
BitField<4, 1, u32> fence;
BitField<12, 4, QueryUnit> unit;
BitField<16, 1, QuerySyncCondition> sync_cond;
BitField<23, 5, QuerySelect> select;
BitField<28, 1, u32> short_query;
BitField<12, 4, u32> unit;
} query_get;
GPUVAddr QueryAddress() const {

View File

@@ -4,24 +4,15 @@
#pragma once
#include <bitset>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Tegra {
namespace Shader {
struct Register {
// Register 255 is special cased to always be 0
static constexpr size_t ZeroIndex = 255;
constexpr Register() = default;
constexpr Register(u64 value) : value(value) {}
@@ -95,12 +86,181 @@ union Uniform {
BitField<34, 5, u64> index;
};
union OpCode {
enum class Id : u64 {
TEXS = 0x6C,
IPA = 0xE0,
FMUL32_IMM = 0x1E,
FFMA_IMM = 0x65,
FFMA_CR = 0x93,
FFMA_RC = 0xA3,
FFMA_RR = 0xB3,
FADD_C = 0x98B,
FMUL_C = 0x98D,
MUFU = 0xA10,
FADD_R = 0xB8B,
FMUL_R = 0xB8D,
LD_A = 0x1DFB,
ST_A = 0x1DFE,
FSETP_R = 0x5BB,
FSETP_C = 0x4BB,
EXIT = 0xE30,
KIL = 0xE33,
FMUL_IMM = 0x70D,
FMUL_IMM_x = 0x72D,
FADD_IMM = 0x70B,
FADD_IMM_x = 0x72B,
};
enum class Type {
Trivial,
Arithmetic,
Ffma,
Flow,
Memory,
Unknown,
};
struct Info {
Type type;
std::string name;
};
OpCode() = default;
constexpr OpCode(Id value) : value(static_cast<u64>(value)) {}
constexpr OpCode(u64 value) : value{value} {}
constexpr Id EffectiveOpCode() const {
switch (op1) {
case Id::TEXS:
return op1;
}
switch (op2) {
case Id::IPA:
case Id::FMUL32_IMM:
return op2;
}
switch (op3) {
case Id::FFMA_IMM:
case Id::FFMA_CR:
case Id::FFMA_RC:
case Id::FFMA_RR:
return op3;
}
switch (op4) {
case Id::EXIT:
case Id::FSETP_R:
case Id::FSETP_C:
case Id::KIL:
return op4;
}
switch (op5) {
case Id::MUFU:
case Id::LD_A:
case Id::ST_A:
case Id::FADD_R:
case Id::FADD_C:
case Id::FMUL_R:
case Id::FMUL_C:
return op5;
case Id::FMUL_IMM:
case Id::FMUL_IMM_x:
return Id::FMUL_IMM;
case Id::FADD_IMM:
case Id::FADD_IMM_x:
return Id::FADD_IMM;
}
return static_cast<Id>(value);
}
static const Info& GetInfo(const OpCode& opcode) {
static const std::map<Id, Info> info_table{BuildInfoTable()};
const auto& search{info_table.find(opcode.EffectiveOpCode())};
if (search != info_table.end()) {
return search->second;
}
static const Info unknown{Type::Unknown, "UNK"};
return unknown;
}
constexpr operator Id() const {
return static_cast<Id>(value);
}
constexpr OpCode operator<<(size_t bits) const {
return value << bits;
}
constexpr OpCode operator>>(size_t bits) const {
return value >> bits;
}
template <typename T>
constexpr u64 operator-(const T& oth) const {
return value - oth;
}
constexpr u64 operator&(const OpCode& oth) const {
return value & oth.value;
}
constexpr u64 operator~() const {
return ~value;
}
static std::map<Id, Info> BuildInfoTable() {
std::map<Id, Info> info_table;
info_table[Id::TEXS] = {Type::Memory, "texs"};
info_table[Id::LD_A] = {Type::Memory, "ld_a"};
info_table[Id::ST_A] = {Type::Memory, "st_a"};
info_table[Id::MUFU] = {Type::Arithmetic, "mufu"};
info_table[Id::FFMA_IMM] = {Type::Ffma, "ffma_imm"};
info_table[Id::FFMA_CR] = {Type::Ffma, "ffma_cr"};
info_table[Id::FFMA_RC] = {Type::Ffma, "ffma_rc"};
info_table[Id::FFMA_RR] = {Type::Ffma, "ffma_rr"};
info_table[Id::FADD_R] = {Type::Arithmetic, "fadd_r"};
info_table[Id::FADD_C] = {Type::Arithmetic, "fadd_c"};
info_table[Id::FADD_IMM] = {Type::Arithmetic, "fadd_imm"};
info_table[Id::FMUL_R] = {Type::Arithmetic, "fmul_r"};
info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"};
info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"};
info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"};
info_table[Id::FSETP_C] = {Type::Arithmetic, "fsetp_c"};
info_table[Id::FSETP_R] = {Type::Arithmetic, "fsetp_r"};
info_table[Id::EXIT] = {Type::Trivial, "exit"};
info_table[Id::IPA] = {Type::Trivial, "ipa"};
info_table[Id::KIL] = {Type::Flow, "kil"};
return info_table;
}
BitField<57, 7, Id> op1;
BitField<56, 8, Id> op2;
BitField<55, 9, Id> op3;
BitField<52, 12, Id> op4;
BitField<51, 13, Id> op5;
u64 value{};
};
static_assert(sizeof(OpCode) == 0x8, "Incorrect structure size");
} // namespace Shader
} // namespace Tegra
namespace std {
// TODO(bunnei): The below is forbidden by the C++ standard, but works fine. See #330.
// TODO(bunne): The below is forbidden by the C++ standard, but works fine. See #330.
template <>
struct make_unsigned<Tegra::Shader::Attribute> {
using type = Tegra::Shader::Attribute;
@@ -111,6 +271,11 @@ struct make_unsigned<Tegra::Shader::Register> {
using type = Tegra::Shader::Register;
};
template <>
struct make_unsigned<Tegra::Shader::OpCode> {
using type = Tegra::Shader::OpCode;
};
} // namespace std
namespace Tegra {
@@ -118,23 +283,7 @@ namespace Shader {
enum class Pred : u64 {
UnusedIndex = 0x7,
NeverExecute = 0xF,
};
enum class PredCondition : u64 {
LessThan = 1,
Equal = 2,
LessEqual = 3,
GreaterThan = 4,
NotEqual = 5,
GreaterEqual = 6,
// TODO(Subv): Other condition types
};
enum class PredOperation : u64 {
And = 0,
Or = 1,
Xor = 2,
NeverExecute = 0xf,
};
enum class SubOp : u64 {
@@ -149,24 +298,18 @@ enum class SubOp : u64 {
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
hex = instr.hex;
return *this;
}
constexpr Instruction(u64 value) : value{value} {}
OpCode opcode;
BitField<0, 8, Register> gpr0;
BitField<8, 8, Register> gpr8;
union {
BitField<16, 4, Pred> full_pred;
BitField<16, 3, u64> pred_index;
} pred;
BitField<19, 1, u64> negate_pred;
BitField<16, 4, Pred> pred;
BitField<20, 8, Register> gpr20;
BitField<20, 7, SubOp> sub_op;
BitField<28, 8, Register> gpr28;
BitField<39, 8, Register> gpr39;
BitField<48, 16, u64> opcode;
union {
BitField<20, 19, u64> imm20_19;
@@ -200,20 +343,6 @@ union Instruction {
BitField<49, 1, u64> negate_c;
} ffma;
union {
BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3;
BitField<7, 1, u64> abs_a;
BitField<39, 3, u64> pred39;
BitField<42, 1, u64> neg_pred;
BitField<43, 1, u64> neg_a;
BitField<44, 1, u64> abs_b;
BitField<45, 2, PredOperation> op;
BitField<47, 1, u64> ftz;
BitField<48, 4, PredCondition> cond;
BitField<56, 1, u64> neg_b;
} fsetp;
BitField<61, 1, u64> is_b_imm;
BitField<60, 1, u64> is_b_gpr;
BitField<59, 1, u64> is_c_gpr;
@@ -222,218 +351,11 @@ union Instruction {
Uniform uniform;
Sampler sampler;
u64 value;
u64 hex;
};
static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size");
static_assert(std::is_standard_layout<Instruction>::value,
"Structure does not have standard layout");
class OpCode {
public:
enum class Id {
KIL,
LD_A,
ST_A,
TEXQ, // Texture Query
TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
TLDS, // Texture Load with scalar/non-vec4 source/destinations
EXIT,
IPA,
FFMA_IMM, // Fused Multiply and Add
FFMA_CR,
FFMA_RC,
FFMA_RR,
FADD_C,
FADD_R,
FADD_IMM,
FMUL_C,
FMUL_R,
FMUL_IMM,
FMUL32_IMM,
MUFU, // Multi-Function Operator
RRO, // Range Reduction Operator
F2F_C,
F2F_R,
F2F_IMM,
F2I_C,
F2I_R,
F2I_IMM,
I2F_C,
I2F_R,
I2F_IMM,
LOP32I,
MOV_C,
MOV_R,
MOV_IMM,
MOV32I,
SHR_C,
SHR_R,
SHR_IMM,
FSETP_C, // Set Predicate
FSETP_R,
FSETP_IMM,
ISETP_C,
ISETP_IMM,
ISETP_R,
};
enum class Type {
Trivial,
Arithmetic,
Ffma,
Flow,
Memory,
FloatPredicate,
IntegerPredicate,
Unknown,
};
class Matcher {
public:
Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type)
: name{name}, mask{mask}, expected{expected}, id{id}, type{type} {}
const char* GetName() const {
return name;
}
u16 GetMask() const {
return mask;
}
Id GetId() const {
return id;
}
Type GetType() const {
return type;
}
/**
* Tests to see if the given instruction is the instruction this matcher represents.
* @param instruction The instruction to test
* @returns true if the given instruction matches.
*/
bool Matches(u16 instruction) const {
return (instruction & mask) == expected;
}
private:
const char* name;
u16 mask;
u16 expected;
Id id;
Type type;
};
static boost::optional<const Matcher&> Decode(Instruction instr) {
static const auto table{GetDecodeTable()};
const auto matches_instruction = [instr](const auto& matcher) {
return matcher.Matches(static_cast<u16>(instr.opcode));
};
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none;
}
private:
struct Detail {
private:
static constexpr size_t opcode_bitsize = 16;
/**
* Generates the mask and the expected value after masking from a given bitstring.
* A '0' in a bitstring indicates that a zero must be present at that bit position.
* A '1' in a bitstring indicates that a one must be present at that bit position.
*/
static auto GetMaskAndExpect(const char* const bitstring) {
u16 mask = 0, expect = 0;
for (size_t i = 0; i < opcode_bitsize; i++) {
const size_t bit_position = opcode_bitsize - i - 1;
switch (bitstring[i]) {
case '0':
mask |= 1 << bit_position;
break;
case '1':
expect |= 1 << bit_position;
mask |= 1 << bit_position;
break;
default:
// Ignore
break;
}
}
return std::make_tuple(mask, expect);
}
public:
/// Creates a matcher that can match and parse instructions based on bitstring.
static auto GetMatcher(const char* const bitstring, OpCode::Id op, OpCode::Type type,
const char* const name) {
const auto mask_expect = GetMaskAndExpect(bitstring);
return Matcher(name, std::get<0>(mask_expect), std::get<1>(mask_expect), op, type);
}
};
static std::vector<Matcher> GetDecodeTable() {
std::vector<Matcher> table = {
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"),
INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"),
INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"),
INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"),
INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"),
INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),
INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"),
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
INST("0101110010010---", Id::RRO, Type::Arithmetic, "RRO"),
INST("0100110010101---", Id::F2F_C, Type::Arithmetic, "F2F_C"),
INST("0101110010101---", Id::F2F_R, Type::Arithmetic, "F2F_R"),
INST("0011100-10101---", Id::F2F_IMM, Type::Arithmetic, "F2F_IMM"),
INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"),
INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"),
INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"),
INST("0100110010111---", Id::I2F_C, Type::Arithmetic, "I2F_C"),
INST("0101110010111---", Id::I2F_R, Type::Arithmetic, "I2F_R"),
INST("0011100-10111---", Id::I2F_IMM, Type::Arithmetic, "I2F_IMM"),
INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"),
INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
INST("000000010000----", Id::MOV32I, Type::Arithmetic, "MOV32I"),
INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"),
INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"),
INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"),
INST("010010111011----", Id::FSETP_C, Type::FloatPredicate, "FSETP_C"),
INST("010110111011----", Id::FSETP_R, Type::FloatPredicate, "FSETP_R"),
INST("0011011-1011----", Id::FSETP_IMM, Type::FloatPredicate, "FSETP_IMM"),
INST("010010110110----", Id::ISETP_C, Type::IntegerPredicate, "ISETP_C"),
INST("010110110110----", Id::ISETP_R, Type::IntegerPredicate, "ISETP_R"),
INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerPredicate, "ISETP_IMM"),
};
#undef INST
std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) {
// If a matcher has more bits in its mask it is more specific, so it
// should come first.
return std::bitset<16>(a.GetMask()).count() > std::bitset<16>(b.GetMask()).count();
});
return table;
}
};
} // namespace Shader
} // namespace Tegra

View File

@@ -14,6 +14,7 @@
#include "common/math_util.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/vector_math.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/settings.h"

View File

@@ -6,10 +6,15 @@
#include <array>
#include <cstddef>
#include <cstring>
#include <memory>
#include <unordered_map>
#include <vector>
#include <glad/glad.h>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/hash.h"
#include "common/vector_math.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"

View File

@@ -7,6 +7,7 @@
#include <cstring>
#include <iterator>
#include <memory>
#include <unordered_set>
#include <utility>
#include <vector>
#include <boost/optional.hpp>
@@ -19,6 +20,7 @@
#include "common/math_util.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/vector_math.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/hle/kernel/process.h"
@@ -670,8 +672,7 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
}
enum class MatchFlags {
None = 0,
enum MatchFlags {
Invalid = 1, // Flag that can be applied to other match types, invalid matches require
// validation before they can be used
Exact = 1 << 1, // Surfaces perfectly match
@@ -685,10 +686,6 @@ constexpr MatchFlags operator|(MatchFlags lhs, MatchFlags rhs) {
return static_cast<MatchFlags>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
constexpr MatchFlags operator&(MatchFlags lhs, MatchFlags rhs) {
return static_cast<MatchFlags>(static_cast<int>(lhs) & static_cast<int>(rhs));
}
/// Get the best surface match (and its match type) for the given flags
template <MatchFlags find_flags>
Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params,
@@ -706,15 +703,15 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params
: (params.res_scale <= surface->res_scale);
// validity will be checked in GetCopyableInterval
bool is_valid =
(find_flags & MatchFlags::Copy) != MatchFlags::None
find_flags & MatchFlags::Copy
? true
: surface->IsRegionValid(validate_interval.value_or(params.GetInterval()));
if ((find_flags & MatchFlags::Invalid) == MatchFlags::None && !is_valid)
if (!(find_flags & MatchFlags::Invalid) && !is_valid)
continue;
auto IsMatch_Helper = [&](auto check_type, auto match_fn) {
if ((find_flags & check_type) == MatchFlags::None)
if (!(find_flags & check_type))
return;
bool matched;

View File

@@ -14,13 +14,13 @@ class OGLTexture : private NonCopyable {
public:
OGLTexture() = default;
OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
OGLTexture(OGLTexture&& o) : handle(std::exchange(o.handle, 0)) {}
~OGLTexture() {
Release();
}
OGLTexture& operator=(OGLTexture&& o) noexcept {
OGLTexture& operator=(OGLTexture&& o) {
Release();
handle = std::exchange(o.handle, 0);
return *this;
@@ -49,13 +49,13 @@ class OGLSampler : private NonCopyable {
public:
OGLSampler() = default;
OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
OGLSampler(OGLSampler&& o) : handle(std::exchange(o.handle, 0)) {}
~OGLSampler() {
Release();
}
OGLSampler& operator=(OGLSampler&& o) noexcept {
OGLSampler& operator=(OGLSampler&& o) {
Release();
handle = std::exchange(o.handle, 0);
return *this;
@@ -84,13 +84,13 @@ class OGLShader : private NonCopyable {
public:
OGLShader() = default;
OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
OGLShader(OGLShader&& o) : handle(std::exchange(o.handle, 0)) {}
~OGLShader() {
Release();
}
OGLShader& operator=(OGLShader&& o) noexcept {
OGLShader& operator=(OGLShader&& o) {
Release();
handle = std::exchange(o.handle, 0);
return *this;
@@ -118,13 +118,13 @@ class OGLProgram : private NonCopyable {
public:
OGLProgram() = default;
OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
OGLProgram(OGLProgram&& o) : handle(std::exchange(o.handle, 0)) {}
~OGLProgram() {
Release();
}
OGLProgram& operator=(OGLProgram&& o) noexcept {
OGLProgram& operator=(OGLProgram&& o) {
Release();
handle = std::exchange(o.handle, 0);
return *this;
@@ -165,12 +165,13 @@ public:
class OGLPipeline : private NonCopyable {
public:
OGLPipeline() = default;
OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {}
OGLPipeline(OGLPipeline&& o) {
handle = std::exchange<GLuint>(o.handle, 0);
}
~OGLPipeline() {
Release();
}
OGLPipeline& operator=(OGLPipeline&& o) noexcept {
OGLPipeline& operator=(OGLPipeline&& o) {
handle = std::exchange<GLuint>(o.handle, 0);
return *this;
}
@@ -198,13 +199,13 @@ class OGLBuffer : private NonCopyable {
public:
OGLBuffer() = default;
OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
OGLBuffer(OGLBuffer&& o) : handle(std::exchange(o.handle, 0)) {}
~OGLBuffer() {
Release();
}
OGLBuffer& operator=(OGLBuffer&& o) noexcept {
OGLBuffer& operator=(OGLBuffer&& o) {
Release();
handle = std::exchange(o.handle, 0);
return *this;
@@ -233,12 +234,12 @@ class OGLSync : private NonCopyable {
public:
OGLSync() = default;
OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {}
OGLSync(OGLSync&& o) : handle(std::exchange(o.handle, nullptr)) {}
~OGLSync() {
Release();
}
OGLSync& operator=(OGLSync&& o) noexcept {
OGLSync& operator=(OGLSync&& o) {
Release();
handle = std::exchange(o.handle, nullptr);
return *this;
@@ -266,13 +267,13 @@ class OGLVertexArray : private NonCopyable {
public:
OGLVertexArray() = default;
OGLVertexArray(OGLVertexArray&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
OGLVertexArray(OGLVertexArray&& o) : handle(std::exchange(o.handle, 0)) {}
~OGLVertexArray() {
Release();
}
OGLVertexArray& operator=(OGLVertexArray&& o) noexcept {
OGLVertexArray& operator=(OGLVertexArray&& o) {
Release();
handle = std::exchange(o.handle, 0);
return *this;
@@ -301,13 +302,13 @@ class OGLFramebuffer : private NonCopyable {
public:
OGLFramebuffer() = default;
OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
OGLFramebuffer(OGLFramebuffer&& o) : handle(std::exchange(o.handle, 0)) {}
~OGLFramebuffer() {
Release();
}
OGLFramebuffer& operator=(OGLFramebuffer&& o) noexcept {
OGLFramebuffer& operator=(OGLFramebuffer&& o) {
Release();
handle = std::exchange(o.handle, 0);
return *this;

View File

@@ -97,12 +97,11 @@ private:
return exit_method;
for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
if (const auto opcode = OpCode::Decode({program_code[offset]})) {
switch (opcode->GetId()) {
case OpCode::Id::EXIT: {
return exit_method = ExitMethod::AlwaysEnd;
}
}
const Instruction instr = {program_code[offset]};
switch (instr.opcode.EffectiveOpCode()) {
case OpCode::Id::EXIT: {
return exit_method = ExitMethod::AlwaysEnd;
}
}
}
return exit_method = ExitMethod::AlwaysReturn;
@@ -221,8 +220,6 @@ private:
/// Generates code representing a temporary (GPR) register.
std::string GetRegister(const Register& reg, unsigned elem = 0) {
if (reg == Register::ZeroIndex)
return "0";
if (stage == Maxwell3D::Regs::ShaderStage::Fragment && reg < 4) {
// GPRs 0-3 are output color for the fragment shader
return std::string{"color."} + "rgba"[(reg + elem) & 3];
@@ -279,52 +276,6 @@ private:
shader.AddLine(dest + " = " + src + ";");
}
/*
* Writes code that assigns a predicate boolean variable.
* @param pred The id of the predicate to write to.
* @param value The expression value to assign to the predicate.
*/
void SetPredicate(u64 pred, const std::string& value) {
using Tegra::Shader::Pred;
// Can't assign to the constant predicate.
ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
std::string variable = 'p' + std::to_string(pred);
shader.AddLine(variable + " = " + value + ';');
declr_predicates.insert(std::move(variable));
}
/*
* Returns the condition to use in the 'if' for a predicated instruction.
* @param instr Instruction to generate the if condition for.
* @returns string containing the predicate condition.
*/
std::string GetPredicateCondition(Instruction instr) const {
using Tegra::Shader::Pred;
ASSERT(instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex));
std::string variable =
'p' + std::to_string(static_cast<u64>(instr.pred.pred_index.Value()));
if (instr.negate_pred) {
return "!(" + variable + ')';
}
return variable;
}
/*
* Returns whether the instruction at the specified offset is a 'sched' instruction.
* Sched instructions always appear before a sequence of 3 instructions.
*/
bool IsSchedInstruction(u32 offset) const {
// sched instructions appear once every 4 instructions.
static constexpr size_t SchedPeriod = 4;
u32 absolute_offset = offset - main_offset;
return (absolute_offset % SchedPeriod) == 0;
}
/**
* Compiles a single instruction from Tegra to GLSL.
* @param offset the offset of the Tegra shader instruction.
@@ -332,33 +283,11 @@ private:
* + 1. If the current instruction always terminates the program, returns PROGRAM_END.
*/
u32 CompileInstr(u32 offset) {
// Ignore sched instructions when generating code.
if (IsSchedInstruction(offset)) {
return offset + 1;
}
const Instruction instr = {program_code[offset]};
const auto opcode = OpCode::Decode(instr);
// Decoding failure
if (!opcode) {
NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value);
UNREACHABLE();
}
shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name);
shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName());
using Tegra::Shader::Pred;
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
"NeverExecute predicate not implemented");
if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
shader.AddLine("if (" + GetPredicateCondition(instr) + ')');
shader.AddLine('{');
++shader.scope;
}
switch (opcode->GetType()) {
switch (OpCode::GetInfo(instr.opcode).type) {
case OpCode::Type::Arithmetic: {
std::string dest = GetRegister(instr.gpr0);
std::string op_a = instr.alu.negate_a ? "-" : "";
@@ -383,7 +312,7 @@ private:
op_b = "abs(" + op_b + ")";
}
switch (opcode->GetId()) {
switch (instr.opcode.EffectiveOpCode()) {
case OpCode::Id::FMUL_C:
case OpCode::Id::FMUL_R:
case OpCode::Id::FMUL_IMM: {
@@ -425,18 +354,16 @@ private:
SetDest(0, dest, "min(" + op_a + "," + op_b + ")", 1, 1, instr.alu.abs_d);
break;
default:
NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}",
NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {}",
static_cast<unsigned>(instr.sub_op.Value()));
UNREACHABLE();
}
break;
}
case OpCode::Id::RRO: {
NGLOG_DEBUG(HW_GPU, "Skipping RRO instruction");
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName());
NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {} ({}): {}",
static_cast<unsigned>(instr.opcode.EffectiveOpCode()),
OpCode::GetInfo(instr.opcode).name, instr.hex);
UNREACHABLE();
}
}
@@ -448,7 +375,7 @@ private:
std::string op_b = instr.ffma.negate_b ? "-" : "";
std::string op_c = instr.ffma.negate_c ? "-" : "";
switch (opcode->GetId()) {
switch (instr.opcode.EffectiveOpCode()) {
case OpCode::Id::FFMA_CR: {
op_b += GetUniform(instr.uniform);
op_c += GetRegister(instr.gpr39);
@@ -470,7 +397,9 @@ private:
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName());
NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {} ({}): {}",
static_cast<unsigned>(instr.opcode.EffectiveOpCode()),
OpCode::GetInfo(instr.opcode).name, instr.hex);
UNREACHABLE();
}
}
@@ -482,7 +411,7 @@ private:
std::string gpr0 = GetRegister(instr.gpr0);
const Attribute::Index attribute = instr.attribute.fmt20.index;
switch (opcode->GetId()) {
switch (instr.opcode.EffectiveOpCode()) {
case OpCode::Id::LD_A: {
ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested");
SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4);
@@ -513,76 +442,22 @@ private:
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName());
NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {} ({}): {}",
static_cast<unsigned>(instr.opcode.EffectiveOpCode()),
OpCode::GetInfo(instr.opcode).name, instr.hex);
UNREACHABLE();
}
}
break;
}
case OpCode::Type::FloatPredicate: {
std::string op_a = instr.fsetp.neg_a ? "-" : "";
op_a += GetRegister(instr.gpr8);
if (instr.fsetp.abs_a) {
op_a = "abs(" + op_a + ')';
}
std::string op_b{};
if (instr.is_b_imm) {
if (instr.fsetp.neg_b) {
// Only the immediate version of fsetp has a neg_b bit.
op_b += '-';
}
op_b += '(' + GetImmediate19(instr) + ')';
} else {
if (instr.is_b_gpr) {
op_b += GetRegister(instr.gpr20);
} else {
op_b += GetUniform(instr.uniform);
}
}
if (instr.fsetp.abs_b) {
op_b = "abs(" + op_b + ')';
}
using Tegra::Shader::Pred;
ASSERT_MSG(instr.fsetp.pred0 == static_cast<u64>(Pred::UnusedIndex) &&
instr.fsetp.pred39 == static_cast<u64>(Pred::UnusedIndex),
"Compound predicates are not implemented");
// We can't use the constant predicate as destination.
ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
using Tegra::Shader::PredCondition;
switch (instr.fsetp.cond) {
case PredCondition::LessThan:
SetPredicate(instr.fsetp.pred3, '(' + op_a + ") < (" + op_b + ')');
break;
case PredCondition::Equal:
SetPredicate(instr.fsetp.pred3, '(' + op_a + ") == (" + op_b + ')');
break;
default:
NGLOG_CRITICAL(HW_GPU, "Unhandled predicate condition: {} (a: {}, b: {})",
static_cast<unsigned>(instr.fsetp.cond.Value()), op_a, op_b);
UNREACHABLE();
}
break;
}
default: {
switch (opcode->GetId()) {
switch (instr.opcode.EffectiveOpCode()) {
case OpCode::Id::EXIT: {
ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex),
"Predicated exits not implemented");
shader.AddLine("return true;");
offset = PROGRAM_END - 1;
break;
}
case OpCode::Id::KIL: {
shader.AddLine("discard;");
break;
}
case OpCode::Id::IPA: {
const auto& attribute = instr.attribute.fmt28;
std::string dest = GetRegister(instr.gpr0);
@@ -590,7 +465,9 @@ private:
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {} ({}): {}",
static_cast<unsigned>(instr.opcode.EffectiveOpCode()),
OpCode::GetInfo(instr.opcode).name, instr.hex);
UNREACHABLE();
}
}
@@ -599,12 +476,6 @@ private:
}
}
// Close the predicate condition scope.
if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
--shader.scope;
shader.AddLine('}');
}
return offset + 1;
}
@@ -734,12 +605,6 @@ private:
declarations.AddNewLine();
++const_buffer_layout;
}
declarations.AddNewLine();
for (const auto& pred : declr_predicates) {
declarations.AddLine("bool " + pred + " = false;");
}
declarations.AddNewLine();
}
private:
@@ -753,7 +618,6 @@ private:
// Declarations
std::set<std::string> declr_register;
std::set<std::string> declr_predicates;
std::set<Attribute::Index> declr_input_attribute;
std::set<Attribute::Index> declr_output_attribute;
std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;

View File

@@ -9,10 +9,13 @@
#include <memory>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hw/hw.h"
#include "core/hw/lcd.h"
#include "core/memory.h"
#include "core/settings.h"
#include "core/tracer/recorder.h"

View File

@@ -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) {}

View File

@@ -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:

View File

@@ -56,28 +56,7 @@ void EmuWindow_SDL2::OnResize() {
UpdateCurrentFramebufferLayout(width, height);
}
void EmuWindow_SDL2::Fullscreen() {
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
return;
}
NGLOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
// Try a different fullscreening method
NGLOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
return;
}
NGLOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
// Fallback algorithm: Maximise window.
// Works on all systems (unless something is seriously wrong), so no fallback for this one.
NGLOG_INFO(Frontend, "Falling back on a maximised window...");
SDL_MaximizeWindow(render_window);
}
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
EmuWindow_SDL2::EmuWindow_SDL2() {
InputCommon::Init();
SDL_SetMainReady();
@@ -111,10 +90,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
exit(1);
}
if (fullscreen) {
Fullscreen();
}
gl_context = SDL_GL_CreateContext(render_window);
if (gl_context == nullptr) {

View File

@@ -12,7 +12,7 @@ struct SDL_Window;
class EmuWindow_SDL2 : public EmuWindow {
public:
explicit EmuWindow_SDL2(bool fullscreen);
EmuWindow_SDL2();
~EmuWindow_SDL2();
/// Swap buffers to display the next frame
@@ -43,9 +43,6 @@ private:
/// Called by PollEvents when any event that may cause the window to be resized occurs
void OnResize();
/// Called when user passes the fullscreen parameter flag
void Fullscreen();
/// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;

View File

@@ -50,7 +50,6 @@ static void PrintHelp(const char* argv0) {
std::cout << "Usage: " << argv0
<< " [options] <filename>\n"
"-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
"-f, --fullscreen Start in fullscreen mode\n"
"-h, --help Display this help and exit\n"
"-v, --version Output version information and exit\n";
}
@@ -77,18 +76,15 @@ int main(int argc, char** argv) {
#endif
std::string filepath;
bool fullscreen = false;
static struct option long_options[] = {
{"gdbport", required_argument, 0, 'g'},
{"fullscreen", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0},
};
while (optind < argc) {
char arg = getopt_long(argc, argv, "g:fhv", long_options, &option_index);
char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index);
if (arg != -1) {
switch (arg) {
case 'g':
@@ -102,10 +98,6 @@ int main(int argc, char** argv) {
exit(1);
}
break;
case 'f':
fullscreen = true;
NGLOG_INFO(Frontend, "Starting in fullscreen mode...");
break;
case 'h':
PrintHelp(argv[0]);
return 0;
@@ -145,7 +137,7 @@ int main(int argc, char** argv) {
Settings::values.use_gdbstub = use_gdbstub;
Settings::Apply();
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)};
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>()};
Core::System& system{Core::System::GetInstance()};