Compare commits

..

27 Commits

Author SHA1 Message Date
Lioncash
d71cad6ed0 core/hle/kernel/mutex: Remove usages of global system accessors
Removes the use of global system accessors, and instead uses the
explicit interface provided.
2019-03-14 20:55:52 -04:00
Lioncash
555cd26ec2 core/hle/kernel: Make Mutex a per-process class.
Makes it an instantiable class like it is in the actual kernel. This
will also allow removing reliance on global accessors in a following
change, now that we can encapsulate a reference to the system instance
in the class.
2019-03-14 20:55:52 -04:00
bunnei
2d9546848e Merge pull request #2230 from lioncash/global
kernel/process: Remove use of global system accessors
2019-03-14 20:42:46 -04:00
bunnei
8bd17aa044 Merge pull request #2216 from ReinUsesLisp/rasterizer-system
gl_rasterizer: Use system instance passed from argument
2019-03-14 16:37:05 -04:00
bunnei
4e6c667586 Merge pull request #2227 from lioncash/override
renderer_opengl/gl_global_cache: Add missing override specifiers
2019-03-13 17:05:49 -04:00
bunnei
e7850a7f11 Merge pull request #2226 from lioncash/private
kernel/server_port: Make data members private
2019-03-13 14:44:21 -04:00
bunnei
c1ea6a39a0 Merge pull request #2223 from lioncash/error
core/hle/result: Tidy up the base error code result header.
2019-03-13 14:43:14 -04:00
bunnei
0a923b4ab3 Merge pull request #2187 from FearlessTobi/port-sdl-things
Port various Citra changes to input_common, including deadzone support
2019-03-13 11:46:57 -04:00
bunnei
e8a21f5276 Merge pull request #2166 from lioncash/vi-init-service
service/vi: Unstub GetDisplayService
2019-03-13 10:01:54 -04:00
bunnei
71c4e876ef Merge pull request #2231 from ReinUsesLisp/fixup-bias
video_core/texture: Fix up sampler lod bias
2019-03-13 09:58:58 -04:00
ReinUsesLisp
a63295a872 video_core/texture: Fix up sampler lod bias 2019-03-13 00:45:54 -03:00
Lioncash
6eddb60db0 kernel/process: Remove use of global system accessors
Now that we pass in a reference to the system instance, we can utilize
it to eliminate the global accessors in Process-related code.
2019-03-12 19:03:28 -04:00
Lioncash
3350c0a779 renderer_opengl/gl_global_cache: Replace indexing for assignment with insert_or_assign
The previous code had some minor issues with it, really not a big deal,
but amending it is basically 'free', so I figured, "why not?".

With the standard container maps, when:

map[key] = thing;

is done, this can cause potentially undesirable behavior in certain
scenarios. In particular, if there's no value associated with the key,
then the map constructs a default initialized instance of the value
type.

In this case, since it's a std::shared_ptr (as a type alias) that is
the value type, this will construct a std::shared_pointer, and then
assign over it (with objects that are quite large, or actively heap
allocate this can be extremely undesirable).

We also make the function take the region by value, as we can avoid a
copy (and by extension with std::shared_ptr, a copy causes an atomic
reference count increment), in certain scenarios when ownership isn't a
concern (i.e. when ReserveGlobalRegion is called with an rvalue
reference, then no copy at all occurs). So, it's more-or-less a "free"
gain without many downsides.
2019-03-11 12:20:35 -04:00
Lioncash
1070c020db renderer_opengl/gl_global_cache: Append missing override specifiers
Two of the functions here are overridden functions, so we can append
these specifiers to make it explicit.
2019-03-11 12:02:30 -04:00
Lioncash
aa44eb639b kernel/server_port: Make data members private
With this, all kernel objects finally have all of their data members
behind an interface, making it nicer to reason about interactions with
other code (as external code no longer has the freedom to totally alter
internals and potentially messing up invariants).
2019-03-11 10:41:05 -04:00
ReinUsesLisp
a6c048920e gl_rasterizer: Use system instance passed from argument 2019-03-11 03:17:21 -03:00
Lioncash
0c28ab92e6 core/hle/result: Remove now-unnecessary manually defined copy assignment operator
Previously this was required, as BitField wasn't trivially copyable.
BitField has since been made trivially copyable, so now this isn't
required anymore.
2019-03-10 18:34:20 -04:00
Lioncash
3f602dde0f core/hle/result: Amend error in comment description for ResultCode
Gets rid of another holdover from Citra, and describes the OS on the
Switch instead.
2019-03-10 18:29:31 -04:00
Lioncash
f7ec0bcfc2 core/hle/result: Remove now-unused constructor for ResultCode
Now that the final stray ErrorDescription member was relocated, we can
finally remove it and its relevant constructor in the ResultCode union.
2019-03-10 18:26:12 -04:00
Lioncash
d870cc5ad7 core/hle/result: Relocate IPC error code to ipc_helpers
Relocates the error code to where it's most related, similar to how all
the other error codes are. Previously we were including a non-generic
error in the main result code header.
2019-03-10 18:23:42 -04:00
B3n30
71817afbe9 fixup! Joystick: Allow for background events; Add deadzone to SDLAnalog 2019-03-02 19:12:46 +01:00
Weiyi Wang
8b98f60e3c input/sdl: lock map mutex after SDL call
Any SDL invocation can call the even callback on the same thread, which can call GetSDLJoystickBySDLID and eventually cause double lock on joystick_map_mutex. To avoid this, lock guard should be placed as closer as possible to the object accessing code, so that any SDL invocation is with the mutex unlocked
2019-03-02 19:09:58 +01:00
James Rowe
09ac66388c Input: Remove global variables from SDL Input
Changes the interface as well to remove any unique methods that
frontends needed to call such as StartJoystickEventHandler by
conditionally starting the polling thread only if the frontend hasn't
started it already. Additionally, moves all global state into a single
SDLState class in order to guarantee that the destructors are called in
the proper order
2019-03-02 19:09:34 +01:00
James Rowe
c8554d218b Input: Copy current SDL.h/cpp files to impl
This should make reviewing much easier as you can then see what changed
happened between the old file and the new one
2019-03-02 18:38:11 +01:00
Lioncash
92ea1c32d6 service/vi: Unstub GetDisplayService
This function is also supposed to check its given policy type with the
permission of the service itself. This implements the necessary
machinery to unstub these functions.

Policy::User seems to just be basic access (which is probably why vi:u
is restricted to that policy), while the other policy seems to be for
extended abilities regarding which displays can be managed and queried,
so this is assumed to be for a background compositor (which I've named,
appropriately, Policy::Compositor).
2019-02-26 20:16:23 -05:00
Lioncash
254b1e3df7 core/ipc_helper: Allow popping all signed value types with RequestParser
There's no real reason this shouldn't be allowed, given some values sent
via a request can be signed. This also makes it less annoying to work
with popping enum values, given an enum class with no type specifier
will work out of the box now.

It's also kind of an oversight to allow popping s64 values, but nothing
else.
2019-02-26 18:10:36 -05:00
Lioncash
1b2872eebc service/vi: Remove use of a module class
This didn't really provide much benefit here, especially since the
subsequent change requires that the behavior for each service's
GetDisplayService differs in a minor detail.

This also arguably makes the services nicer to read, since it gets rid
of an indirection in the class hierarchy.
2019-02-26 17:44:03 -05:00
36 changed files with 1100 additions and 876 deletions

View File

@@ -19,9 +19,12 @@
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/result.h"
namespace IPC {
constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
class RequestHelperBase {
protected:
Kernel::HLERequestContext* context = nullptr;
@@ -362,6 +365,11 @@ inline u32 RequestParser::Pop() {
return cmdbuf[index++];
}
template <>
inline s32 RequestParser::Pop() {
return static_cast<s32>(Pop<u32>());
}
template <typename T>
void RequestParser::PopRaw(T& value) {
std::memcpy(&value, cmdbuf + index, sizeof(T));
@@ -392,6 +400,16 @@ inline u64 RequestParser::Pop() {
return msw << 32 | lsw;
}
template <>
inline s8 RequestParser::Pop() {
return static_cast<s8>(Pop<u8>());
}
template <>
inline s16 RequestParser::Pop() {
return static_cast<s16>(Pop<u16>());
}
template <>
inline s64 RequestParser::Pop() {
return static_cast<s64>(Pop<u64>());

View File

@@ -33,10 +33,11 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
if (server_port->hle_handler)
server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
else
server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions));
if (server_port->HasHLEHandler()) {
server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
} else {
server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions));
}
// Wake the threads waiting on the ServerPort
server_port->WakeupAllWaitingThreads();

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
#include <utility>
#include <vector>
@@ -10,8 +9,11 @@
#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"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -57,41 +59,47 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
}
}
ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle,
Mutex::Mutex(Core::System& system) : system{system} {}
Mutex::~Mutex() = default;
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 ERR_INVALID_ADDRESS;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
Thread* const current_thread = system.CurrentScheduler().GetCurrentThread();
SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
// thread.
ASSERT(requesting_thread == GetCurrentThread());
ASSERT(requesting_thread == current_thread);
u32 addr_value = Memory::Read32(address);
const 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)
if (holding_thread == nullptr) {
return ERR_INVALID_HANDLE;
}
// Wait until the mutex is released
GetCurrentThread()->SetMutexWaitAddress(address);
GetCurrentThread()->SetWaitHandle(requesting_thread_handle);
current_thread->SetMutexWaitAddress(address);
current_thread->SetWaitHandle(requesting_thread_handle);
GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex);
GetCurrentThread()->InvalidateWakeupCallback();
current_thread->SetStatus(ThreadStatus::WaitMutex);
current_thread->InvalidateWakeupCallback();
// Update the lock holder thread's priority to prevent priority inversion.
holding_thread->AddMutexWaiter(GetCurrentThread());
holding_thread->AddMutexWaiter(current_thread);
Core::System::GetInstance().PrepareReschedule();
system.PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -102,7 +110,8 @@ ResultCode Mutex::Release(VAddr address) {
return ERR_INVALID_ADDRESS;
}
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
// There are no more threads waiting for the mutex, release it completely.
if (thread == nullptr) {
@@ -111,7 +120,7 @@ ResultCode Mutex::Release(VAddr address) {
}
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, GetCurrentThread(), thread);
TransferMutexOwnership(address, current_thread, thread);
u32 mutex_value = thread->GetWaitHandle();

View File

@@ -5,32 +5,34 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Kernel {
namespace Core {
class System;
}
class HandleTable;
class Thread;
namespace Kernel {
class Mutex final {
public:
explicit Mutex(Core::System& system);
~Mutex();
/// 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;
/// Attempts to acquire a mutex at the specified address.
static ResultCode TryAcquire(HandleTable& handle_table, VAddr address,
Handle holding_thread_handle, Handle requesting_thread_handle);
ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle);
/// Releases the mutex at the specified address.
static ResultCode Release(VAddr address);
ResultCode Release(VAddr address);
private:
Mutex() = default;
~Mutex() = default;
Core::System& system;
};
} // namespace Kernel

View File

@@ -133,7 +133,7 @@ void Process::PrepareForTermination() {
if (thread->GetOwnerProcess() != this)
continue;
if (thread == GetCurrentThread())
if (thread == system.CurrentScheduler().GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
@@ -145,7 +145,6 @@ void Process::PrepareForTermination() {
}
};
const auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0).GetThreadList());
stop_threads(system.Scheduler(1).GetThreadList());
stop_threads(system.Scheduler(2).GetThreadList());
@@ -228,13 +227,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
// Clear instruction cache in CPU JIT
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
system.InvalidateCpuInstructionCaches();
}
Process::Process(Core::System& system) : WaitObject{system.Kernel()}, address_arbiter{system} {}
Process::Process(Core::System& system)
: WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
Process::~Process() = default;
void Process::Acquire(Thread* thread) {

View File

@@ -14,6 +14,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/wait_object.h"
@@ -165,6 +166,16 @@ public:
return address_arbiter;
}
/// Gets a reference to the process' mutex lock.
Mutex& GetMutex() {
return mutex;
}
/// Gets a const reference to the process' mutex lock
const Mutex& GetMutex() const {
return mutex;
}
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -266,7 +277,7 @@ public:
void FreeTLSSlot(VAddr tls_address);
private:
explicit Process(Core::System& kernel);
explicit Process(Core::System& system);
~Process() override;
/// Checks if the specified thread should wait until this process is available.
@@ -327,9 +338,18 @@ private:
/// Per-process address arbiter.
AddressArbiter address_arbiter;
/// The per-process mutex lock instance used for handling various
/// forms of services, such as lock arbitration, and condition
/// variable related facilities.
Mutex mutex;
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
/// System context
Core::System& system;
/// Name of this process
std::string name;
};

View File

@@ -26,6 +26,10 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
return MakeResult(std::move(session));
}
void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) {
pending_sessions.push_back(std::move(pending_session));
}
bool ServerPort::ShouldWait(Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
return pending_sessions.empty();

View File

@@ -22,6 +22,8 @@ class SessionRequestHandler;
class ServerPort final : public WaitObject {
public:
using HLEHandler = std::shared_ptr<SessionRequestHandler>;
/**
* Creates a pair of ServerPort and an associated ClientPort.
*
@@ -51,22 +53,27 @@ public:
*/
ResultVal<SharedPtr<ServerSession>> Accept();
/// Whether or not this server port has an HLE handler available.
bool HasHLEHandler() const {
return hle_handler != nullptr;
}
/// Gets the HLE handler for this port.
HLEHandler GetHLEHandler() const {
return hle_handler;
}
/**
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
* will inherit a reference to this handler.
*/
void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) {
void SetHleHandler(HLEHandler hle_handler_) {
hle_handler = std::move(hle_handler_);
}
std::string name; ///< Name of port (optional)
/// ServerSessions waiting to be accepted by the port
std::vector<SharedPtr<ServerSession>> pending_sessions;
/// This session's HLE request handler template (optional)
/// ServerSessions created from this port inherit a reference to this handler.
std::shared_ptr<SessionRequestHandler> hle_handler;
/// Appends a ServerSession to the collection of ServerSessions
/// waiting to be accepted by this port.
void AppendPendingSession(SharedPtr<ServerSession> pending_session);
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -74,6 +81,16 @@ public:
private:
explicit ServerPort(KernelCore& kernel);
~ServerPort() override;
/// ServerSessions waiting to be accepted by the port
std::vector<SharedPtr<ServerSession>> pending_sessions;
/// This session's HLE request handler template (optional)
/// ServerSessions created from this port inherit a reference to this handler.
HLEHandler hle_handler;
/// Name of the port (optional)
std::string name;
};
} // namespace Kernel

View File

@@ -551,9 +551,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
return ERR_INVALID_ADDRESS;
}
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
requesting_thread_handle);
}
/// Unlock a mutex
@@ -571,7 +571,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return ERR_INVALID_ADDRESS;
}
return Mutex::Release(mutex_addr);
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
return current_process->GetMutex().Release(mutex_addr);
}
enum class BreakType : u32 {
@@ -1336,11 +1337,15 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
const auto& handle_table = current_process->GetHandleTable();
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
CASCADE_CODE(Mutex::Release(mutex_addr));
const auto release_result = current_process->GetMutex().Release(mutex_addr);
if (release_result.IsError()) {
return release_result;
}
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->SetCondVarWaitAddress(condition_variable_addr);

View File

@@ -7,6 +7,8 @@
#include <optional>
#include <vector>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -267,8 +269,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
// waiters list is consistent and return without doing anything.
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(iter != wait_mutex_threads.end());
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr != wait_mutex_threads.end());
return;
}
@@ -276,16 +278,11 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == nullptr);
// Ensure that the thread is not already in the list of mutex waiters
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(iter == wait_mutex_threads.end());
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr == wait_mutex_threads.end());
// Keep the list in an ordered fashion
const auto insertion_point = std::find_if(
wait_mutex_threads.begin(), wait_mutex_threads.end(),
[&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
wait_mutex_threads.insert(insertion_point, thread);
thread->lock_owner = this;
wait_mutex_threads.emplace_back(std::move(thread));
UpdatePriority();
}
@@ -293,43 +290,32 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == this);
// Ensure that the thread is in the list of mutex waiters
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(iter != wait_mutex_threads.end());
wait_mutex_threads.erase(iter);
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr != wait_mutex_threads.end());
boost::remove_erase(wait_mutex_threads, thread);
thread->lock_owner = nullptr;
UpdatePriority();
}
void Thread::UpdatePriority() {
// If any of the threads waiting on the mutex have a higher priority
// (taking into account priority inheritance), then this thread inherits
// that thread's priority.
// Find the highest priority among all the threads that are waiting for this thread's lock
u32 new_priority = nominal_priority;
if (!wait_mutex_threads.empty()) {
if (wait_mutex_threads.front()->current_priority < new_priority) {
new_priority = wait_mutex_threads.front()->current_priority;
}
for (const auto& thread : wait_mutex_threads) {
if (thread->nominal_priority < new_priority)
new_priority = thread->nominal_priority;
}
if (new_priority == current_priority) {
if (new_priority == current_priority)
return;
}
scheduler->SetThreadPriority(this, new_priority);
current_priority = new_priority;
if (!lock_owner) {
return;
}
// Ensure that the thread is within the correct location in the waiting list.
lock_owner->RemoveMutexWaiter(this);
lock_owner->AddMutexWaiter(this);
// Recursively update the priority of the thread that depends on the priority of this one.
lock_owner->UpdatePriority();
if (lock_owner)
lock_owner->UpdatePriority();
}
void Thread::ChangeCore(u32 core, u64 mask) {

View File

@@ -398,14 +398,8 @@ private:
VAddr entry_point = 0;
VAddr stack_top = 0;
/// Nominal thread priority, as set by the emulated application.
/// The nominal priority is the thread priority without priority
/// inheritance taken into account.
u32 nominal_priority = 0;
/// Current thread priority. This may change over the course of the
/// thread's lifetime in order to facilitate priority inheritance.
u32 current_priority = 0;
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
u64 last_running_ticks = 0; ///< CPU tick when thread was last running

View File

@@ -12,14 +12,6 @@
// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
/**
* Detailed description of the error. Code 0 always means success.
*/
enum class ErrorDescription : u32 {
Success = 0,
RemoteProcessDead = 301,
};
/**
* Identifies the module which caused the error. Error codes can be propagated through a call
* chain, meaning that this doesn't always correspond to the module where the API call made is
@@ -120,7 +112,7 @@ enum class ErrorModule : u32 {
ShopN = 811,
};
/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
union ResultCode {
u32 raw;
@@ -133,17 +125,9 @@ union ResultCode {
constexpr explicit ResultCode(u32 raw) : raw(raw) {}
constexpr ResultCode(ErrorModule module, ErrorDescription description)
: ResultCode(module, static_cast<u32>(description)) {}
constexpr ResultCode(ErrorModule module_, u32 description_)
: raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
constexpr ResultCode& operator=(const ResultCode& o) {
raw = o.raw;
return *this;
}
constexpr bool IsSuccess() const {
return raw == 0;
}

View File

@@ -11,7 +11,6 @@
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_port.h"
@@ -168,7 +167,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
case IPC::CommandType::Close: {
IPC::ResponseBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead);
return IPC::ERR_REMOTE_PROCESS_DEAD;
}
case IPC::CommandType::ControlWithContext:
case IPC::CommandType::Control: {

View File

@@ -67,7 +67,7 @@ public:
if (port == nullptr) {
return nullptr;
}
return std::static_pointer_cast<T>(port->hle_handler);
return std::static_pointer_cast<T>(port->GetHLEHandler());
}
void InvokeControlRequest(Kernel::HLERequestContext& context);

View File

@@ -24,6 +24,7 @@
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/service.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
#include "core/hle/service/vi/vi_s.h"
@@ -33,6 +34,7 @@
namespace Service::VI {
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
@@ -1203,26 +1205,40 @@ IApplicationDisplayService::IApplicationDisplayService(
RegisterHandlers(functions);
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
static bool IsValidServiceAccess(Permission permission, Policy policy) {
if (permission == Permission::User) {
return policy == Policy::User;
}
Module::Interface::~Interface() = default;
if (permission == Permission::System || permission == Permission::Manager) {
return policy == Policy::User || policy == Policy::Compositor;
}
void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
return false;
}
void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
Permission permission) {
IPC::RequestParser rp{ctx};
const auto policy = rp.PopEnum<Policy>();
if (!IsValidServiceAccess(permission, policy)) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_PERMISSION_DENIED);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger));
}
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
auto module = std::make_shared<Module>();
std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
}
} // namespace Service::VI

View File

@@ -4,12 +4,21 @@
#pragma once
#include "core/hle/service/service.h"
#include <memory>
#include "common/common_types.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::NVFlinger {
class NVFlinger;
}
namespace Service::SM {
class ServiceManager;
}
namespace Service::VI {
enum class DisplayResolution : u32 {
@@ -19,22 +28,25 @@ enum class DisplayResolution : u32 {
UndockedHeight = 720,
};
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~Interface() override;
void GetDisplayService(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
/// Permission level for a particular VI service instance
enum class Permission {
User,
System,
Manager,
};
/// A policy type that may be requested via GetDisplayService and
/// GetDisplayServiceWithProxyNameExchange
enum class Policy {
User,
Compositor,
};
namespace detail {
void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission);
} // namespace detail
/// Registers all VI services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);

View File

@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
namespace Service::VI {
VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) {
VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{2, &VI_M::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
VI_M::~VI_M() = default;
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::Manager);
}
} // namespace Service::VI

View File

@@ -4,14 +4,27 @@
#pragma once
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::NVFlinger {
class NVFlinger;
}
namespace Service::VI {
class VI_M final : public Module::Interface {
class VI_M final : public ServiceFramework<VI_M> {
public:
explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_M() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
} // namespace Service::VI

View File

@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_s.h"
namespace Service::VI {
VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) {
VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{1, &VI_S::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
VI_S::~VI_S() = default;
void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::System);
}
} // namespace Service::VI

View File

@@ -4,14 +4,27 @@
#pragma once
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::NVFlinger {
class NVFlinger;
}
namespace Service::VI {
class VI_S final : public Module::Interface {
class VI_S final : public ServiceFramework<VI_S> {
public:
explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_S() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
} // namespace Service::VI

View File

@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_u.h"
namespace Service::VI {
VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) {
VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{0, &VI_U::GetDisplayService, "GetDisplayService"},
};
@@ -16,4 +18,10 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
VI_U::~VI_U() = default;
void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::User);
}
} // namespace Service::VI

View File

@@ -4,14 +4,27 @@
#pragma once
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::NVFlinger {
class NVFlinger;
}
namespace Service::VI {
class VI_U final : public Module::Interface {
class VI_U final : public ServiceFramework<VI_U> {
public:
explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_U() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
} // namespace Service::VI

View File

@@ -7,15 +7,18 @@ add_library(input_common STATIC
main.h
motion_emu.cpp
motion_emu.h
$<$<BOOL:${SDL2_FOUND}>:sdl/sdl.cpp sdl/sdl.h>
sdl/sdl.cpp
sdl/sdl.h
)
create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC core PRIVATE common)
if(SDL2_FOUND)
target_sources(input_common PRIVATE
sdl/sdl_impl.cpp
sdl/sdl_impl.h
)
target_link_libraries(input_common PRIVATE SDL2)
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
endif()
create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC core PRIVATE common)

View File

@@ -17,10 +17,7 @@ namespace InputCommon {
static std::shared_ptr<Keyboard> keyboard;
static std::shared_ptr<MotionEmu> motion_emu;
#ifdef HAVE_SDL2
static std::thread poll_thread;
#endif
static std::unique_ptr<SDL::State> sdl;
void Init() {
keyboard = std::make_shared<Keyboard>();
@@ -30,15 +27,7 @@ void Init() {
motion_emu = std::make_shared<MotionEmu>();
Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
#ifdef HAVE_SDL2
SDL::Init();
#endif
}
void StartJoystickEventHandler() {
#ifdef HAVE_SDL2
poll_thread = std::thread(SDL::PollLoop);
#endif
sdl = SDL::Init();
}
void Shutdown() {
@@ -47,11 +36,7 @@ void Shutdown() {
Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
motion_emu.reset();
#ifdef HAVE_SDL2
SDL::Shutdown();
poll_thread.join();
#endif
sdl.reset();
}
Keyboard* GetKeyboard() {
@@ -88,7 +73,7 @@ namespace Polling {
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
#ifdef HAVE_SDL2
return SDL::Polling::GetPollers(type);
return sdl->GetPollers(type);
#else
return {};
#endif

View File

@@ -20,8 +20,6 @@ void Init();
/// Deregisters all built-in input device factories and shuts them down.
void Shutdown();
void StartJoystickEventHandler();
class Keyboard;
/// Gets the keyboard button device factory.

View File

@@ -1,631 +1,19 @@
// Copyright 2017 Citra Emulator Project
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <atomic>
#include <cmath>
#include <functional>
#include <iterator>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
#include <SDL.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/param_package.h"
#include "common/threadsafe_queue.h"
#include "input_common/main.h"
#include "input_common/sdl/sdl.h"
#ifdef HAVE_SDL2
#include "input_common/sdl/sdl_impl.h"
#endif
namespace InputCommon {
namespace InputCommon::SDL {
namespace SDL {
class SDLJoystick;
class SDLButtonFactory;
class SDLAnalogFactory;
/// Map of GUID of a list of corresponding virtual Joysticks
static std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
static std::mutex joystick_map_mutex;
static std::shared_ptr<SDLButtonFactory> button_factory;
static std::shared_ptr<SDLAnalogFactory> analog_factory;
/// Used by the Pollers during config
static std::atomic<bool> polling;
static Common::SPSCQueue<SDL_Event> event_queue;
static std::atomic<bool> initialized = false;
static std::string GetGUID(SDL_Joystick* joystick) {
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
char guid_str[33];
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
return guid_str;
std::unique_ptr<State> Init() {
#ifdef HAVE_SDL2
return std::make_unique<SDLState>();
#else
return std::make_unique<NullState>();
#endif
}
class SDLJoystick {
public:
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
void SetButton(int button, bool value) {
std::lock_guard<std::mutex> lock(mutex);
state.buttons[button] = value;
}
bool GetButton(int button) const {
std::lock_guard<std::mutex> lock(mutex);
return state.buttons.at(button);
}
void SetAxis(int axis, Sint16 value) {
std::lock_guard<std::mutex> lock(mutex);
state.axes[axis] = value;
}
float GetAxis(int axis) const {
std::lock_guard<std::mutex> lock(mutex);
return state.axes.at(axis) / 32767.0f;
}
std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
float x = GetAxis(axis_x);
float y = GetAxis(axis_y);
y = -y; // 3DS uses an y-axis inverse from SDL
// Make sure the coordinates are in the unit circle,
// otherwise normalize it.
float r = x * x + y * y;
if (r > 1.0f) {
r = std::sqrt(r);
x /= r;
y /= r;
}
return std::make_tuple(x, y);
}
void SetHat(int hat, Uint8 direction) {
std::lock_guard<std::mutex> lock(mutex);
state.hats[hat] = direction;
}
bool GetHatDirection(int hat, Uint8 direction) const {
std::lock_guard<std::mutex> lock(mutex);
return (state.hats.at(hat) & direction) != 0;
}
/**
* The guid of the joystick
*/
const std::string& GetGUID() const {
return guid;
}
/**
* The number of joystick from the same type that were connected before this joystick
*/
int GetPort() const {
return port;
}
SDL_Joystick* GetSDLJoystick() const {
return sdl_joystick.get();
}
void SetSDLJoystick(SDL_Joystick* joystick,
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
sdl_joystick =
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
}
private:
struct State {
std::unordered_map<int, bool> buttons;
std::unordered_map<int, Sint16> axes;
std::unordered_map<int, Uint8> hats;
} state;
std::string guid;
int port;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
mutable std::mutex mutex;
};
/**
* Get the nth joystick with the corresponding GUID
*/
static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
while (it->second.size() <= port) {
auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
[](SDL_Joystick*) {});
it->second.emplace_back(std::move(joystick));
}
return it->second[port];
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
return joystick_map[guid].emplace_back(std::move(joystick));
}
/**
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
* it to a SDLJoystick with the same guid and that port
*/
static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
auto map_it = joystick_map.find(guid);
if (map_it != joystick_map.end()) {
auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
return sdl_joystick == joystick->GetSDLJoystick();
});
if (vec_it != map_it->second.end()) {
// This is the common case: There is already an existing SDL_Joystick maped to a
// SDLJoystick. return the SDLJoystick
return *vec_it;
}
// Search for a SDLJoystick without a mapped SDL_Joystick...
auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) {
return !joystick->GetSDLJoystick();
});
if (nullptr_it != map_it->second.end()) {
// ... and map it
(*nullptr_it)->SetSDLJoystick(sdl_joystick);
return *nullptr_it;
}
// There is no SDLJoystick without a mapped SDL_Joystick
// Create a new SDLJoystick
auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
return map_it->second.emplace_back(std::move(joystick));
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
return joystick_map[guid].emplace_back(std::move(joystick));
}
void InitJoystick(int joystick_index) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
if (!sdl_joystick) {
LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
return;
}
std::string guid = GetGUID(sdl_joystick);
if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
joystick_map[guid].emplace_back(std::move(joystick));
return;
}
auto& joystick_guid_list = joystick_map[guid];
const auto it = std::find_if(
joystick_guid_list.begin(), joystick_guid_list.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
if (it != joystick_guid_list.end()) {
(*it)->SetSDLJoystick(sdl_joystick);
return;
}
auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
joystick_guid_list.emplace_back(std::move(joystick));
}
void CloseJoystick(SDL_Joystick* sdl_joystick) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
std::string guid = GetGUID(sdl_joystick);
// This call to guid is save since the joystick is guranteed to be in that map
auto& joystick_guid_list = joystick_map[guid];
const auto joystick_it =
std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
return joystick->GetSDLJoystick() == sdl_joystick;
});
(*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
}
void HandleGameControllerEvent(const SDL_Event& event) {
switch (event.type) {
case SDL_JOYBUTTONUP: {
auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
if (joystick) {
joystick->SetButton(event.jbutton.button, false);
}
break;
}
case SDL_JOYBUTTONDOWN: {
auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
if (joystick) {
joystick->SetButton(event.jbutton.button, true);
}
break;
}
case SDL_JOYHATMOTION: {
auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
if (joystick) {
joystick->SetHat(event.jhat.hat, event.jhat.value);
}
break;
}
case SDL_JOYAXISMOTION: {
auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
if (joystick) {
joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
}
break;
}
case SDL_JOYDEVICEREMOVED:
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
break;
case SDL_JOYDEVICEADDED:
LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
InitJoystick(event.jdevice.which);
break;
}
}
void CloseSDLJoysticks() {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
joystick_map.clear();
}
void PollLoop() {
if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
return;
}
SDL_Event event;
while (initialized) {
// Wait for 10 ms or until an event happens
if (SDL_WaitEventTimeout(&event, 10)) {
// Don't handle the event if we are configuring
if (polling) {
event_queue.Push(event);
} else {
HandleGameControllerEvent(event);
}
}
}
CloseSDLJoysticks();
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
class SDLButton final : public Input::ButtonDevice {
public:
explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
: joystick(std::move(joystick_)), button(button_) {}
bool GetStatus() const override {
return joystick->GetButton(button);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int button;
};
class SDLDirectionButton final : public Input::ButtonDevice {
public:
explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
: joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
bool GetStatus() const override {
return joystick->GetHatDirection(hat, direction);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int hat;
Uint8 direction;
};
class SDLAxisButton final : public Input::ButtonDevice {
public:
explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
bool trigger_if_greater_)
: joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
trigger_if_greater(trigger_if_greater_) {}
bool GetStatus() const override {
float axis_value = joystick->GetAxis(axis);
if (trigger_if_greater)
return axis_value > threshold;
return axis_value < threshold;
}
private:
std::shared_ptr<SDLJoystick> joystick;
int axis;
float threshold;
bool trigger_if_greater;
};
class SDLAnalog final : public Input::AnalogDevice {
public:
SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_)
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_) {}
std::tuple<float, float> GetStatus() const override {
return joystick->GetAnalog(axis_x, axis_y);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int axis_x;
int axis_y;
};
/// A button device factory that creates button devices from SDL joystick
class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
public:
/**
* Creates a button device from a joystick button
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type to bind
* - "button"(optional): the index of the button to bind
* - "hat"(optional): the index of the hat to bind as direction buttons
* - "axis"(optional): the index of the axis to bind
* - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
* "down", "left" or "right"
* - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
* triggered if the axis value crosses
* - "direction"(only used for axis): "+" means the button is triggered when the axis
* value is greater than the threshold; "-" means the button is triggered when the axis
* value is smaller than the threshold
*/
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
auto joystick = GetSDLJoystickByGUID(guid, port);
if (params.Has("hat")) {
const int hat = params.Get("hat", 0);
const std::string direction_name = params.Get("direction", "");
Uint8 direction;
if (direction_name == "up") {
direction = SDL_HAT_UP;
} else if (direction_name == "down") {
direction = SDL_HAT_DOWN;
} else if (direction_name == "left") {
direction = SDL_HAT_LEFT;
} else if (direction_name == "right") {
direction = SDL_HAT_RIGHT;
} else {
direction = 0;
}
// This is necessary so accessing GetHat with hat won't crash
joystick->SetHat(hat, SDL_HAT_CENTERED);
return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
}
if (params.Has("axis")) {
const int axis = params.Get("axis", 0);
const float threshold = params.Get("threshold", 0.5f);
const std::string direction_name = params.Get("direction", "");
bool trigger_if_greater;
if (direction_name == "+") {
trigger_if_greater = true;
} else if (direction_name == "-") {
trigger_if_greater = false;
} else {
trigger_if_greater = true;
LOG_ERROR(Input, "Unknown direction '{}'", direction_name);
}
// This is necessary so accessing GetAxis with axis won't crash
joystick->SetAxis(axis, 0);
return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
}
const int button = params.Get("button", 0);
// This is necessary so accessing GetButton with button won't crash
joystick->SetButton(button, false);
return std::make_unique<SDLButton>(joystick, button);
}
};
/// An analog device factory that creates analog devices from SDL joystick
class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
/**
* Creates analog device from joystick axes
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
* - "axis_x": the index of the axis to be bind as x-axis
* - "axis_y": the index of the axis to be bind as y-axis
*/
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
auto joystick = GetSDLJoystickByGUID(guid, port);
// This is necessary so accessing GetAxis with axis_x and axis_y won't crash
joystick->SetAxis(axis_x, 0);
joystick->SetAxis(axis_y, 0);
return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y);
}
};
void Init() {
using namespace Input;
RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
polling = false;
initialized = true;
}
void Shutdown() {
if (initialized) {
using namespace Input;
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
initialized = false;
}
}
Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
Common::ParamPackage params({{"engine", "sdl"}});
switch (event.type) {
case SDL_JOYAXISMOTION: {
auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis", event.jaxis.axis);
if (event.jaxis.value > 0) {
params.Set("direction", "+");
params.Set("threshold", "0.5");
} else {
params.Set("direction", "-");
params.Set("threshold", "-0.5");
}
break;
}
case SDL_JOYBUTTONUP: {
auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("button", event.jbutton.button);
break;
}
case SDL_JOYHATMOTION: {
auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("hat", event.jhat.hat);
switch (event.jhat.value) {
case SDL_HAT_UP:
params.Set("direction", "up");
break;
case SDL_HAT_DOWN:
params.Set("direction", "down");
break;
case SDL_HAT_LEFT:
params.Set("direction", "left");
break;
case SDL_HAT_RIGHT:
params.Set("direction", "right");
break;
default:
return {};
}
break;
}
}
return params;
}
namespace Polling {
class SDLPoller : public InputCommon::Polling::DevicePoller {
public:
void Start() override {
event_queue.Clear();
polling = true;
}
void Stop() override {
polling = false;
}
};
class SDLButtonPoller final : public SDLPoller {
public:
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (event_queue.Pop(event)) {
switch (event.type) {
case SDL_JOYAXISMOTION:
if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
break;
}
case SDL_JOYBUTTONUP:
case SDL_JOYHATMOTION:
return SDLEventToButtonParamPackage(event);
}
}
return {};
}
};
class SDLAnalogPoller final : public SDLPoller {
public:
void Start() override {
SDLPoller::Start();
// Reset stored axes
analog_xaxis = -1;
analog_yaxis = -1;
analog_axes_joystick = -1;
}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (event_queue.Pop(event)) {
if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
// An analog device needs two axes, so we need to store the axis for later and wait for
// a second SDL event. The axes also must be from the same joystick.
int axis = event.jaxis.axis;
if (analog_xaxis == -1) {
analog_xaxis = axis;
analog_axes_joystick = event.jaxis.which;
} else if (analog_yaxis == -1 && analog_xaxis != axis &&
analog_axes_joystick == event.jaxis.which) {
analog_yaxis = axis;
}
}
Common::ParamPackage params;
if (analog_xaxis != -1 && analog_yaxis != -1) {
auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("engine", "sdl");
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis_x", analog_xaxis);
params.Set("axis_y", analog_yaxis);
analog_xaxis = -1;
analog_yaxis = -1;
analog_axes_joystick = -1;
return params;
}
return params;
}
private:
int analog_xaxis = -1;
int analog_yaxis = -1;
SDL_JoystickID analog_axes_joystick = -1;
};
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) {
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
switch (type) {
case InputCommon::Polling::DeviceType::Analog:
pollers.push_back(std::make_unique<SDLAnalogPoller>());
break;
case InputCommon::Polling::DeviceType::Button:
pollers.push_back(std::make_unique<SDLButtonPoller>());
break;
}
return pollers;
}
} // namespace Polling
} // namespace SDL
} // namespace InputCommon
} // namespace InputCommon::SDL

View File

@@ -1,4 +1,4 @@
// Copyright 2017 Citra Emulator Project
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -7,45 +7,36 @@
#include <memory>
#include <vector>
#include "core/frontend/input.h"
#include "input_common/main.h"
union SDL_Event;
namespace Common {
class ParamPackage;
}
namespace InputCommon {
namespace Polling {
} // namespace Common
namespace InputCommon::Polling {
class DevicePoller;
enum class DeviceType;
} // namespace Polling
} // namespace InputCommon
} // namespace InputCommon::Polling
namespace InputCommon {
namespace SDL {
namespace InputCommon::SDL {
/// Initializes and registers SDL device factories
void Init();
class State {
public:
/// Unresisters SDL device factories and shut them down.
virtual ~State() = default;
/// Unresisters SDL device factories and shut them down.
void Shutdown();
virtual std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) = 0;
};
/// Needs to be called before SDL_QuitSubSystem.
void CloseSDLJoysticks();
class NullState : public State {
public:
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) override {}
};
/// Handle SDL_Events for joysticks from SDL_PollEvent
void HandleGameControllerEvent(const SDL_Event& event);
std::unique_ptr<State> Init();
/// A Loop that calls HandleGameControllerEvent until Shutdown is called
void PollLoop();
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event);
namespace Polling {
/// Get all DevicePoller that use the SDL backend for a specific device type
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type);
} // namespace Polling
} // namespace SDL
} // namespace InputCommon
} // namespace InputCommon::SDL

View File

@@ -0,0 +1,669 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <atomic>
#include <cmath>
#include <functional>
#include <iterator>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
#include <SDL.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/param_package.h"
#include "common/threadsafe_queue.h"
#include "core/frontend/input.h"
#include "input_common/sdl/sdl_impl.h"
namespace InputCommon {
namespace SDL {
static std::string GetGUID(SDL_Joystick* joystick) {
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
char guid_str[33];
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
return guid_str;
}
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
static int SDLEventWatcher(void* userdata, SDL_Event* event) {
SDLState* sdl_state = reinterpret_cast<SDLState*>(userdata);
// Don't handle the event if we are configuring
if (sdl_state->polling) {
sdl_state->event_queue.Push(*event);
} else {
sdl_state->HandleGameControllerEvent(*event);
}
return 0;
}
class SDLJoystick {
public:
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
void SetButton(int button, bool value) {
std::lock_guard<std::mutex> lock(mutex);
state.buttons[button] = value;
}
bool GetButton(int button) const {
std::lock_guard<std::mutex> lock(mutex);
return state.buttons.at(button);
}
void SetAxis(int axis, Sint16 value) {
std::lock_guard<std::mutex> lock(mutex);
state.axes[axis] = value;
}
float GetAxis(int axis) const {
std::lock_guard<std::mutex> lock(mutex);
return state.axes.at(axis) / 32767.0f;
}
std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
float x = GetAxis(axis_x);
float y = GetAxis(axis_y);
y = -y; // 3DS uses an y-axis inverse from SDL
// Make sure the coordinates are in the unit circle,
// otherwise normalize it.
float r = x * x + y * y;
if (r > 1.0f) {
r = std::sqrt(r);
x /= r;
y /= r;
}
return std::make_tuple(x, y);
}
void SetHat(int hat, Uint8 direction) {
std::lock_guard<std::mutex> lock(mutex);
state.hats[hat] = direction;
}
bool GetHatDirection(int hat, Uint8 direction) const {
std::lock_guard<std::mutex> lock(mutex);
return (state.hats.at(hat) & direction) != 0;
}
/**
* The guid of the joystick
*/
const std::string& GetGUID() const {
return guid;
}
/**
* The number of joystick from the same type that were connected before this joystick
*/
int GetPort() const {
return port;
}
SDL_Joystick* GetSDLJoystick() const {
return sdl_joystick.get();
}
void SetSDLJoystick(SDL_Joystick* joystick,
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
sdl_joystick =
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
}
private:
struct State {
std::unordered_map<int, bool> buttons;
std::unordered_map<int, Sint16> axes;
std::unordered_map<int, Uint8> hats;
} state;
std::string guid;
int port;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
mutable std::mutex mutex;
};
/**
* Get the nth joystick with the corresponding GUID
*/
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
while (it->second.size() <= port) {
auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
[](SDL_Joystick*) {});
it->second.emplace_back(std::move(joystick));
}
return it->second[port];
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
return joystick_map[guid].emplace_back(std::move(joystick));
}
/**
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
* it to a SDLJoystick with the same guid and that port
*/
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard<std::mutex> lock(joystick_map_mutex);
auto map_it = joystick_map.find(guid);
if (map_it != joystick_map.end()) {
auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
return sdl_joystick == joystick->GetSDLJoystick();
});
if (vec_it != map_it->second.end()) {
// This is the common case: There is already an existing SDL_Joystick maped to a
// SDLJoystick. return the SDLJoystick
return *vec_it;
}
// Search for a SDLJoystick without a mapped SDL_Joystick...
auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) {
return !joystick->GetSDLJoystick();
});
if (nullptr_it != map_it->second.end()) {
// ... and map it
(*nullptr_it)->SetSDLJoystick(sdl_joystick);
return *nullptr_it;
}
// There is no SDLJoystick without a mapped SDL_Joystick
// Create a new SDLJoystick
auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
return map_it->second.emplace_back(std::move(joystick));
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
return joystick_map[guid].emplace_back(std::move(joystick));
}
void SDLState::InitJoystick(int joystick_index) {
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
if (!sdl_joystick) {
LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
return;
}
std::string guid = GetGUID(sdl_joystick);
std::lock_guard<std::mutex> lock(joystick_map_mutex);
if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
joystick_map[guid].emplace_back(std::move(joystick));
return;
}
auto& joystick_guid_list = joystick_map[guid];
const auto it = std::find_if(
joystick_guid_list.begin(), joystick_guid_list.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
if (it != joystick_guid_list.end()) {
(*it)->SetSDLJoystick(sdl_joystick);
return;
}
auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
joystick_guid_list.emplace_back(std::move(joystick));
}
void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
std::string guid = GetGUID(sdl_joystick);
std::shared_ptr<SDLJoystick> joystick;
{
std::lock_guard<std::mutex> lock(joystick_map_mutex);
// This call to guid is safe since the joystick is guaranteed to be in the map
auto& joystick_guid_list = joystick_map[guid];
const auto joystick_it =
std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
return joystick->GetSDLJoystick() == sdl_joystick;
});
joystick = *joystick_it;
}
// Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback
// which locks the mutex again
joystick->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
}
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
switch (event.type) {
case SDL_JOYBUTTONUP: {
if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
joystick->SetButton(event.jbutton.button, false);
}
break;
}
case SDL_JOYBUTTONDOWN: {
if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
joystick->SetButton(event.jbutton.button, true);
}
break;
}
case SDL_JOYHATMOTION: {
if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
joystick->SetHat(event.jhat.hat, event.jhat.value);
}
break;
}
case SDL_JOYAXISMOTION: {
if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
}
break;
}
case SDL_JOYDEVICEREMOVED:
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
break;
case SDL_JOYDEVICEADDED:
LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
InitJoystick(event.jdevice.which);
break;
}
}
void SDLState::CloseJoysticks() {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
joystick_map.clear();
}
class SDLButton final : public Input::ButtonDevice {
public:
explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
: joystick(std::move(joystick_)), button(button_) {}
bool GetStatus() const override {
return joystick->GetButton(button);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int button;
};
class SDLDirectionButton final : public Input::ButtonDevice {
public:
explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
: joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
bool GetStatus() const override {
return joystick->GetHatDirection(hat, direction);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int hat;
Uint8 direction;
};
class SDLAxisButton final : public Input::ButtonDevice {
public:
explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
bool trigger_if_greater_)
: joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
trigger_if_greater(trigger_if_greater_) {}
bool GetStatus() const override {
float axis_value = joystick->GetAxis(axis);
if (trigger_if_greater)
return axis_value > threshold;
return axis_value < threshold;
}
private:
std::shared_ptr<SDLJoystick> joystick;
int axis;
float threshold;
bool trigger_if_greater;
};
class SDLAnalog final : public Input::AnalogDevice {
public:
SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_)
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {}
std::tuple<float, float> GetStatus() const override {
const auto [x, y] = joystick->GetAnalog(axis_x, axis_y);
const float r = std::sqrt((x * x) + (y * y));
if (r > deadzone) {
return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
y / r * (r - deadzone) / (1 - deadzone));
}
return std::make_tuple<float, float>(0.0f, 0.0f);
}
private:
std::shared_ptr<SDLJoystick> joystick;
const int axis_x;
const int axis_y;
const float deadzone;
};
/// A button device factory that creates button devices from SDL joystick
class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
public:
explicit SDLButtonFactory(SDLState& state_) : state(state_) {}
/**
* Creates a button device from a joystick button
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type to bind
* - "button"(optional): the index of the button to bind
* - "hat"(optional): the index of the hat to bind as direction buttons
* - "axis"(optional): the index of the axis to bind
* - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
* "down", "left" or "right"
* - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
* triggered if the axis value crosses
* - "direction"(only used for axis): "+" means the button is triggered when the axis
* value is greater than the threshold; "-" means the button is triggered when the axis
* value is smaller than the threshold
*/
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
if (params.Has("hat")) {
const int hat = params.Get("hat", 0);
const std::string direction_name = params.Get("direction", "");
Uint8 direction;
if (direction_name == "up") {
direction = SDL_HAT_UP;
} else if (direction_name == "down") {
direction = SDL_HAT_DOWN;
} else if (direction_name == "left") {
direction = SDL_HAT_LEFT;
} else if (direction_name == "right") {
direction = SDL_HAT_RIGHT;
} else {
direction = 0;
}
// This is necessary so accessing GetHat with hat won't crash
joystick->SetHat(hat, SDL_HAT_CENTERED);
return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
}
if (params.Has("axis")) {
const int axis = params.Get("axis", 0);
const float threshold = params.Get("threshold", 0.5f);
const std::string direction_name = params.Get("direction", "");
bool trigger_if_greater;
if (direction_name == "+") {
trigger_if_greater = true;
} else if (direction_name == "-") {
trigger_if_greater = false;
} else {
trigger_if_greater = true;
LOG_ERROR(Input, "Unknown direction {}", direction_name);
}
// This is necessary so accessing GetAxis with axis won't crash
joystick->SetAxis(axis, 0);
return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
}
const int button = params.Get("button", 0);
// This is necessary so accessing GetButton with button won't crash
joystick->SetButton(button, false);
return std::make_unique<SDLButton>(joystick, button);
}
private:
SDLState& state;
};
/// An analog device factory that creates analog devices from SDL joystick
class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
/**
* Creates analog device from joystick axes
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
* - "axis_x": the index of the axis to be bind as x-axis
* - "axis_y": the index of the axis to be bind as y-axis
*/
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
// This is necessary so accessing GetAxis with axis_x and axis_y won't crash
joystick->SetAxis(axis_x, 0);
joystick->SetAxis(axis_y, 0);
return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone);
}
private:
SDLState& state;
};
SDLState::SDLState() {
using namespace Input;
RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
// If the frontend is going to manage the event loop, then we dont start one here
start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
return;
}
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
LOG_ERROR(Input, "Failed to set Hint for background events", SDL_GetError());
}
SDL_AddEventWatch(&SDLEventWatcher, this);
initialized = true;
if (start_thread) {
poll_thread = std::thread([&] {
using namespace std::chrono_literals;
SDL_Event event;
while (initialized) {
SDL_PumpEvents();
std::this_thread::sleep_for(std::chrono::duration(10ms));
}
});
}
// Because the events for joystick connection happens before we have our event watcher added, we
// can just open all the joysticks right here
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
InitJoystick(i);
}
}
SDLState::~SDLState() {
using namespace Input;
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
CloseJoysticks();
SDL_DelEventWatch(&SDLEventWatcher, this);
initialized = false;
if (start_thread) {
poll_thread.join();
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
}
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
Common::ParamPackage params({{"engine", "sdl"}});
switch (event.type) {
case SDL_JOYAXISMOTION: {
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis", event.jaxis.axis);
if (event.jaxis.value > 0) {
params.Set("direction", "+");
params.Set("threshold", "0.5");
} else {
params.Set("direction", "-");
params.Set("threshold", "-0.5");
}
break;
}
case SDL_JOYBUTTONUP: {
auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("button", event.jbutton.button);
break;
}
case SDL_JOYHATMOTION: {
auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("hat", event.jhat.hat);
switch (event.jhat.value) {
case SDL_HAT_UP:
params.Set("direction", "up");
break;
case SDL_HAT_DOWN:
params.Set("direction", "down");
break;
case SDL_HAT_LEFT:
params.Set("direction", "left");
break;
case SDL_HAT_RIGHT:
params.Set("direction", "right");
break;
default:
return {};
}
break;
}
}
return params;
}
namespace Polling {
class SDLPoller : public InputCommon::Polling::DevicePoller {
public:
explicit SDLPoller(SDLState& state_) : state(state_) {}
void Start() override {
state.event_queue.Clear();
state.polling = true;
}
void Stop() override {
state.polling = false;
}
protected:
SDLState& state;
};
class SDLButtonPoller final : public SDLPoller {
public:
explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
switch (event.type) {
case SDL_JOYAXISMOTION:
if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
break;
}
case SDL_JOYBUTTONUP:
case SDL_JOYHATMOTION:
return SDLEventToButtonParamPackage(state, event);
}
}
return {};
}
};
class SDLAnalogPoller final : public SDLPoller {
public:
explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {}
void Start() override {
SDLPoller::Start();
// Reset stored axes
analog_xaxis = -1;
analog_yaxis = -1;
analog_axes_joystick = -1;
}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
// An analog device needs two axes, so we need to store the axis for later and wait for
// a second SDL event. The axes also must be from the same joystick.
int axis = event.jaxis.axis;
if (analog_xaxis == -1) {
analog_xaxis = axis;
analog_axes_joystick = event.jaxis.which;
} else if (analog_yaxis == -1 && analog_xaxis != axis &&
analog_axes_joystick == event.jaxis.which) {
analog_yaxis = axis;
}
}
Common::ParamPackage params;
if (analog_xaxis != -1 && analog_yaxis != -1) {
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("engine", "sdl");
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis_x", analog_xaxis);
params.Set("axis_y", analog_yaxis);
analog_xaxis = -1;
analog_yaxis = -1;
analog_axes_joystick = -1;
return params;
}
return params;
}
private:
int analog_xaxis = -1;
int analog_yaxis = -1;
SDL_JoystickID analog_axes_joystick = -1;
};
} // namespace Polling
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers(
InputCommon::Polling::DeviceType type) {
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
switch (type) {
case InputCommon::Polling::DeviceType::Analog:
pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
break;
case InputCommon::Polling::DeviceType::Button:
pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
break;
return pollers;
}
}
} // namespace SDL
} // namespace InputCommon

View File

@@ -0,0 +1,64 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include <memory>
#include <thread>
#include "common/threadsafe_queue.h"
#include "input_common/sdl/sdl.h"
union SDL_Event;
using SDL_Joystick = struct _SDL_Joystick;
using SDL_JoystickID = s32;
namespace InputCommon::SDL {
class SDLJoystick;
class SDLButtonFactory;
class SDLAnalogFactory;
class SDLState : public State {
public:
/// Initializes and registers SDL device factories
SDLState();
/// Unresisters SDL device factories and shut them down.
~SDLState() override;
/// Handle SDL_Events for joysticks from SDL_PollEvent
void HandleGameControllerEvent(const SDL_Event& event);
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
/// Get all DevicePoller that use the SDL backend for a specific device type
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) override;
/// Used by the Pollers during config
std::atomic<bool> polling = false;
Common::SPSCQueue<SDL_Event> event_queue;
private:
void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick);
/// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks();
/// Map of GUID of a list of corresponding virtual Joysticks
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory;
bool start_thread = false;
std::atomic<bool> initialized = false;
std::thread poll_thread;
};
} // namespace InputCommon::SDL

View File

@@ -57,8 +57,8 @@ GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 si
return region;
}
void GlobalRegionCacheOpenGL::ReserveGlobalRegion(const GlobalRegion& region) {
reserve[region->GetAddr()] = region;
void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
reserve.insert_or_assign(region->GetAddr(), std::move(region));
}
GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)

View File

@@ -30,12 +30,12 @@ public:
explicit CachedGlobalRegion(VAddr addr, u32 size);
/// Gets the address of the shader in guest memory, required for cache management
VAddr GetAddr() const {
VAddr GetAddr() const override {
return addr;
}
/// Gets the size of the shader in guest memory, required for cache management
std::size_t GetSizeInBytes() const {
std::size_t GetSizeInBytes() const override {
return size;
}
@@ -70,7 +70,7 @@ public:
private:
GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const;
GlobalRegion GetUncachedGlobalRegion(VAddr addr, u32 size);
void ReserveGlobalRegion(const GlobalRegion& region);
void ReserveGlobalRegion(GlobalRegion region);
std::unordered_map<VAddr, GlobalRegion> reserve;
};

View File

@@ -102,8 +102,9 @@ struct FramebufferCacheKey {
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system,
ScreenInfo& info)
: res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, emu_window{window},
screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) {
: res_cache{*this}, shader_cache{*this, system}, global_cache{*this},
emu_window{window}, system{system}, screen_info{info},
buffer_cache(*this, STREAM_BUFFER_SIZE) {
// Create sampler objects
for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
@@ -138,7 +139,7 @@ void RasterizerOpenGL::CheckExtensions() {
}
GLuint RasterizerOpenGL::SetupVertexFormat() {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
auto& gpu = system.GPU().Maxwell3D();
const auto& regs = gpu.regs;
if (!gpu.dirty_flags.vertex_attrib_format) {
@@ -207,7 +208,7 @@ GLuint RasterizerOpenGL::SetupVertexFormat() {
}
void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
auto& gpu = system.GPU().Maxwell3D();
const auto& regs = gpu.regs;
if (gpu.dirty_flags.vertex_array.none())
@@ -248,7 +249,7 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
}
DrawParameters RasterizerOpenGL::SetupDraw() {
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& gpu = system.GPU().Maxwell3D();
const auto& regs = gpu.regs;
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
@@ -297,7 +298,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
MICROPROFILE_SCOPE(OpenGL_Shader);
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
auto& gpu = system.GPU().Maxwell3D();
BaseBindings base_bindings;
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
@@ -413,7 +414,7 @@ void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey,
}
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
std::size_t size = 0;
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -431,7 +432,7 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
}
std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
return static_cast<std::size_t>(regs.index_array.count) *
static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
@@ -487,7 +488,7 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents,
std::optional<std::size_t> single_color_target) {
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
auto& gpu = system.GPU().Maxwell3D();
const auto& regs = gpu.regs;
const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
@@ -581,7 +582,7 @@ void RasterizerOpenGL::Clear() {
const auto prev_state{state};
SCOPE_EXIT({ prev_state.Apply(); });
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
bool use_color{};
bool use_depth{};
bool use_stencil{};
@@ -672,7 +673,7 @@ void RasterizerOpenGL::DrawArrays() {
return;
MICROPROFILE_SCOPE(OpenGL_Drawing);
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
auto& gpu = system.GPU().Maxwell3D();
const auto& regs = gpu.regs;
ConfigureFramebuffers(state);
@@ -892,7 +893,7 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
const Shader& shader, GLuint program_handle,
BaseBindings base_bindings) {
MICROPROFILE_SCOPE(OpenGL_UBO);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& gpu = system.GPU();
const auto& maxwell3d = gpu.Maxwell3D();
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
const auto& entries = shader->GetShaderEntries().const_buffers;
@@ -971,7 +972,7 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade
void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
GLuint program_handle, BaseBindings base_bindings) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& gpu = system.GPU();
const auto& maxwell3d = gpu.Maxwell3D();
const auto& entries = shader->GetShaderEntries().samplers;
@@ -998,7 +999,7 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s
}
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
const bool geometry_shaders_enabled =
regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
const std::size_t viewport_count =
@@ -1021,7 +1022,7 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
void RasterizerOpenGL::SyncClipEnabled(
const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{
regs.clip_distance_enabled.c0 != 0, regs.clip_distance_enabled.c1 != 0,
regs.clip_distance_enabled.c2 != 0, regs.clip_distance_enabled.c3 != 0,
@@ -1038,7 +1039,7 @@ void RasterizerOpenGL::SyncClipCoef() {
}
void RasterizerOpenGL::SyncCullMode() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.cull.enabled = regs.cull.enabled != 0;
@@ -1062,14 +1063,14 @@ void RasterizerOpenGL::SyncCullMode() {
}
void RasterizerOpenGL::SyncPrimitiveRestart() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.primitive_restart.enabled = regs.primitive_restart.enabled;
state.primitive_restart.index = regs.primitive_restart.index;
}
void RasterizerOpenGL::SyncDepthTestState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.depth.test_enabled = regs.depth_test_enable != 0;
state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
@@ -1081,7 +1082,7 @@ void RasterizerOpenGL::SyncDepthTestState() {
}
void RasterizerOpenGL::SyncStencilTestState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.stencil.test_enabled = regs.stencil_enable != 0;
if (!regs.stencil_enable) {
@@ -1115,7 +1116,7 @@ void RasterizerOpenGL::SyncStencilTestState() {
}
void RasterizerOpenGL::SyncColorMask() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
const std::size_t count =
regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
for (std::size_t i = 0; i < count; i++) {
@@ -1129,18 +1130,18 @@ void RasterizerOpenGL::SyncColorMask() {
}
void RasterizerOpenGL::SyncMultiSampleState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
}
void RasterizerOpenGL::SyncFragmentColorClampState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
}
void RasterizerOpenGL::SyncBlendState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.blend_color.red = regs.blend_color.r;
state.blend_color.green = regs.blend_color.g;
@@ -1182,7 +1183,7 @@ void RasterizerOpenGL::SyncBlendState() {
}
void RasterizerOpenGL::SyncLogicOpState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.logic_op.enabled = regs.logic_op.enable != 0;
@@ -1196,7 +1197,7 @@ void RasterizerOpenGL::SyncLogicOpState() {
}
void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
const bool geometry_shaders_enabled =
regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
const std::size_t viewport_count =
@@ -1218,17 +1219,17 @@ void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
}
void RasterizerOpenGL::SyncTransformFeedback() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented");
}
void RasterizerOpenGL::SyncPointState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.point.size = regs.point_size;
}
void RasterizerOpenGL::SyncPolygonOffset() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0;
state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0;
@@ -1238,7 +1239,7 @@ void RasterizerOpenGL::SyncPolygonOffset() {
}
void RasterizerOpenGL::CheckAlphaTests() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& regs = system.GPU().Maxwell3D().regs;
UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1,
"Alpha Testing is enabled with more than one rendertarget");
}

View File

@@ -215,6 +215,7 @@ private:
GlobalRegionCacheOpenGL global_cache;
Core::Frontend::EmuWindow& emu_window;
Core::System& system;
ScreenInfo& screen_info;

View File

@@ -324,7 +324,7 @@ struct TSCEntry {
float GetLodBias() const {
// Sign extend the 13-bit value.
constexpr u32 mask = 1U << (13 - 1);
return static_cast<float>((mip_lod_bias ^ mask) - mask) / 256.0f;
return static_cast<s32>((mip_lod_bias ^ mask) - mask) / 256.0f;
}
std::array<float, 4> GetBorderColor() const {

View File

@@ -123,7 +123,6 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
setAttribute(Qt::WA_AcceptTouchEvents);
InputCommon::Init();
InputCommon::StartJoystickEventHandler();
connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent),
&GMainWindow::OnLoadComplete);
}

View File

@@ -135,16 +135,16 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
}
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
InputCommon::Init();
SDL_SetMainReady();
// Initialize the window
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
}
InputCommon::Init();
SDL_SetMainReady();
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
@@ -201,11 +201,9 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
}
EmuWindow_SDL2::~EmuWindow_SDL2() {
InputCommon::SDL::CloseSDLJoysticks();
InputCommon::Shutdown();
SDL_GL_DeleteContext(gl_context);
SDL_Quit();
InputCommon::Shutdown();
}
void EmuWindow_SDL2::SwapBuffers() {
@@ -262,7 +260,6 @@ void EmuWindow_SDL2::PollEvents() {
is_open = false;
break;
default:
InputCommon::SDL::HandleGameControllerEvent(event);
break;
}
}