diff options
Diffstat (limited to '')
38 files changed, 470 insertions, 168 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1cfd3bbc9..8bd7e5f72 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,7 @@ if (MSVC) # /Zo - Enhanced debug info for optimized builds # /permissive- - Enables stricter C++ standards conformance checks # /EHsc - C++-only exception handling semantics + # /utf-8 - Set source and execution character sets to UTF-8 # /volatile:iso - Use strict standards-compliant volatile semantics. # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates # /Zc:inline - Let codegen omit inline functions in object files @@ -38,6 +39,7 @@ if (MSVC) /permissive- /EHsc /std:c++latest + /utf-8 /volatile:iso /Zc:externConstexpr /Zc:inline diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index b8e98b12a..39532ff58 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -24,7 +24,7 @@ struct Fiber::FiberImpl { std::function<void(void*)> rewind_point; void* rewind_parameter{}; void* start_parameter{}; - Fiber* previous_fiber; + std::shared_ptr<Fiber> previous_fiber; bool is_thread_fiber{}; bool released{}; @@ -47,7 +47,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) { ASSERT(impl->previous_fiber != nullptr); impl->previous_fiber->impl->context = transfer.fctx; impl->previous_fiber->impl->guard.unlock(); - impl->previous_fiber = nullptr; + impl->previous_fiber.reset(); impl->entry_point(impl->start_parameter); UNREACHABLE(); } @@ -116,20 +116,23 @@ void Fiber::Rewind() { boost::context::detail::jump_fcontext(impl->rewind_context, this); } -void Fiber::YieldTo(Fiber* from, Fiber* to) { - ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); - ASSERT_MSG(to != nullptr, "Next fiber is null!"); - to->impl->guard.lock(); - to->impl->previous_fiber = from; - auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to); - ASSERT(from->impl->previous_fiber != nullptr); - from->impl->previous_fiber->impl->context = transfer.fctx; - from->impl->previous_fiber->impl->guard.unlock(); - from->impl->previous_fiber = nullptr; +void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) { + to.impl->guard.lock(); + to.impl->previous_fiber = weak_from.lock(); + + auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to); + + // "from" might no longer be valid if the thread was killed + if (auto from = weak_from.lock()) { + ASSERT(from->impl->previous_fiber != nullptr); + from->impl->previous_fiber->impl->context = transfer.fctx; + from->impl->previous_fiber->impl->guard.unlock(); + from->impl->previous_fiber.reset(); + } } -std::unique_ptr<Fiber> Fiber::ThreadToFiber() { - std::unique_ptr<Fiber> fiber = std::unique_ptr<Fiber>{new Fiber()}; +std::shared_ptr<Fiber> Fiber::ThreadToFiber() { + std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; fiber->impl->guard.lock(); fiber->impl->is_thread_fiber = true; return fiber; diff --git a/src/common/fiber.h b/src/common/fiber.h index 6924f7996..f2a8ff29a 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -41,8 +41,8 @@ public: /// Yields control from Fiber 'from' to Fiber 'to' /// Fiber 'from' must be the currently running fiber. - static void YieldTo(Fiber* from, Fiber* to); - [[nodiscard]] static std::unique_ptr<Fiber> ThreadToFiber(); + static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); + [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index c35438c6f..bdb374792 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -111,7 +111,7 @@ void CpuManager::MultiCoreRunGuestThread() { auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - auto host_context = thread->GetHostContext(); + auto& host_context = thread->GetHostContext(); host_context->SetRewindPoint(GuestRewindFunction, this); MultiCoreRunGuestLoop(); } @@ -148,8 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() { auto core = kernel.GetCurrentHostThreadID(); auto& scheduler = *kernel.CurrentScheduler(); Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - Common::Fiber::YieldTo(current_thread->GetHostContext(), - core_data[core].host_context.get()); + Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); ASSERT(scheduler.ContextSwitchPending()); ASSERT(core == kernel.GetCurrentHostThreadID()); scheduler.RescheduleCurrentCore(); @@ -202,7 +201,7 @@ void CpuManager::SingleCoreRunGuestThread() { auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - auto host_context = thread->GetHostContext(); + auto& host_context = thread->GetHostContext(); host_context->SetRewindPoint(GuestRewindFunction, this); SingleCoreRunGuestLoop(); } @@ -246,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() { auto core = kernel.GetCurrentHostThreadID(); auto& scheduler = *kernel.CurrentScheduler(); Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context.get()); + Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context); ASSERT(scheduler.ContextSwitchPending()); ASSERT(core == kernel.GetCurrentHostThreadID()); scheduler.RescheduleCurrentCore(); @@ -272,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { scheduler.Unload(scheduler.GetCurrentThread()); auto& next_scheduler = kernel.Scheduler(current_core); - Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); + Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext()); } // May have changed scheduler @@ -364,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) { auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); data.is_running = true; - Common::Fiber::YieldTo(data.host_context.get(), current_thread->GetHostContext()); + Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); data.is_running = false; data.is_paused = true; data.exit_barrier->Wait(); diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 5ea149f1f..17420c941 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -83,7 +83,7 @@ private: void RunThread(std::size_t core); struct CoreData { - std::unique_ptr<Common::Fiber> host_context; + std::shared_ptr<Common::Fiber> host_context; std::unique_ptr<Common::Event> enter_barrier; std::unique_ptr<Common::Event> exit_barrier; std::atomic<bool> is_running; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 465036f3d..e7de48476 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -608,7 +608,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { } KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) { - switch_fiber = std::make_unique<Common::Fiber>(OnSwitch, this); + switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); state.needs_scheduling.store(true); state.interrupt_task_thread_runnable = false; state.should_count_idle = false; @@ -726,15 +726,15 @@ void KScheduler::ScheduleImpl() { // Save context for previous thread Unload(previous_thread); - Common::Fiber* old_context; + std::shared_ptr<Common::Fiber>* old_context; if (previous_thread != nullptr) { - old_context = previous_thread->GetHostContext(); + old_context = &previous_thread->GetHostContext(); } else { - old_context = idle_thread->GetHostContext(); + old_context = &idle_thread->GetHostContext(); } guard.unlock(); - Common::Fiber::YieldTo(old_context, switch_fiber.get()); + Common::Fiber::YieldTo(*old_context, *switch_fiber); /// When a thread wakes up, the scheduler may have changed to other in another core. auto& next_scheduler = *system.Kernel().CurrentScheduler(); next_scheduler.SwitchContextStep2(); @@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() { break; } } - Common::Fiber* next_context; - if (next_thread != nullptr) { - next_context = next_thread->GetHostContext(); - } else { - next_context = idle_thread->GetHostContext(); - } - Common::Fiber::YieldTo(switch_fiber.get(), next_context); + auto thread = next_thread ? next_thread : idle_thread; + Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext()); } while (!is_switch_pending()); } } @@ -800,9 +795,9 @@ void KScheduler::Initialize() { std::string name = "Idle Thread Id:" + std::to_string(core_id); std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - auto thread_res = KThread::Create(system, ThreadType::Main, name, 0, - KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0, - nullptr, std::move(init_func), init_func_parameter); + auto thread_res = KThread::CreateThread( + system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0, + static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter); idle_thread = thread_res.Unwrap().get(); } diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index a4285c595..f595b9a5c 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -68,12 +68,12 @@ public: void OnThreadStart(); - [[nodiscard]] Common::Fiber* ControlContext() { - return switch_fiber.get(); + [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() { + return switch_fiber; } - [[nodiscard]] const Common::Fiber* ControlContext() const { - return switch_fiber.get(); + [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const { + return switch_fiber; } [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread); @@ -178,7 +178,7 @@ private: KThread* idle_thread; - std::unique_ptr<Common::Fiber> switch_fiber{}; + std::shared_ptr<Common::Fiber> switch_fiber{}; struct SchedulingState { std::atomic<bool> needs_scheduling; diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index f49e31b72..e0f53287c 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -991,22 +991,15 @@ void KThread::SetState(ThreadState state) { } } -ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, - u32 priority, u64 arg, s32 processor_id, - VAddr stack_top, Process* owner_process) { - std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); - void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, - owner_process, std::move(init_func), init_func_parameter); +std::shared_ptr<Common::Fiber>& KThread::GetHostContext() { + return host_context; } -ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, - u32 priority, u64 arg, s32 processor_id, - VAddr stack_top, Process* owner_process, - std::function<void(void*)>&& thread_start_func, - void* thread_start_parameter) { +ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system, + ThreadType type_flags, std::string name, + VAddr entry_point, u32 priority, u64 arg, + s32 processor_id, VAddr stack_top, + Process* owner_process) { auto& kernel = system.Kernel(); std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel); @@ -1023,12 +1016,35 @@ ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, Thread auto& scheduler = kernel.GlobalSchedulerContext(); scheduler.AddThread(thread); - thread->host_context = - std::make_unique<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); - return MakeResult<std::shared_ptr<KThread>>(std::move(thread)); } +ResultVal<std::shared_ptr<KThread>> KThread::CreateThread( + Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, + u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process, + std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) { + auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg, + processor_id, stack_top, owner_process); + + if (thread_result.Succeeded()) { + (*thread_result)->host_context = + std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); + } + + return thread_result; +} + +ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread( + Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, + u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) { + std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); + + void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); + + return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id, + stack_top, owner_process, std::move(init_func), init_func_parameter); +} + KThread* GetCurrentThreadPointer(KernelCore& kernel) { return kernel.GetCurrentEmuThread(); } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index a2893d939..1c19b23dc 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -116,7 +116,7 @@ public: using WaiterList = boost::intrusive::list<KThread>; /** - * Creates and returns a new thread. The new thread is immediately scheduled + * Creates and returns a new thread. * @param system The instance of the whole system * @param name The friendly name desired for the thread * @param entry_point The address at which the thread should start execution @@ -127,12 +127,12 @@ public: * @param owner_process The parent process for the thread, if null, it's a kernel thread * @return A shared pointer to the newly created thread */ - [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( + [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread( Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process); /** - * Creates and returns a new thread. The new thread is immediately scheduled + * Creates and returns a new thread, with a specified entry point. * @param system The instance of the whole system * @param name The friendly name desired for the thread * @param entry_point The address at which the thread should start execution @@ -145,11 +145,27 @@ public: * @param thread_start_parameter The parameter which will passed to host context on init * @return A shared pointer to the newly created thread */ - [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( + [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread( Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process, std::function<void(void*)>&& thread_start_func, void* thread_start_parameter); + /** + * Creates and returns a new thread for the emulated "user" process. + * @param system The instance of the whole system + * @param name The friendly name desired for the thread + * @param entry_point The address at which the thread should start execution + * @param priority The thread's priority + * @param arg User data to pass to the thread + * @param processor_id The ID(s) of the processors on which the thread is desired to be run + * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread, if null, it's a kernel thread + * @return A shared pointer to the newly created thread + */ + [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread( + Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process); + [[nodiscard]] std::string GetName() const override { return name; } @@ -293,13 +309,7 @@ public: return thread_context_64; } - [[nodiscard]] Common::Fiber* GetHostContext() { - return host_context.get(); - } - - [[nodiscard]] const Common::Fiber* GetHostContext() const { - return host_context.get(); - } + [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); [[nodiscard]] ThreadState GetState() const { return thread_state & ThreadState::Mask; @@ -725,7 +735,7 @@ private: Common::SpinLock context_guard{}; // For emulation - std::unique_ptr<Common::Fiber> host_context{}; + std::shared_ptr<Common::Fiber> host_context{}; // For debugging std::vector<KSynchronizationObject*> wait_objects_for_debugging; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 331cf3a60..780008b08 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -181,9 +181,9 @@ struct KernelCore::Impl { std::string name = "Suspend Thread Id:" + std::to_string(i); std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0, - 0, 0, static_cast<u32>(i), 0, nullptr, - std::move(init_func), init_func_parameter); + auto thread_res = KThread::CreateThread( + system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0, + nullptr, std::move(init_func), init_func_parameter); suspend_threads[i] = std::move(thread_res).Unwrap(); } @@ -221,10 +221,9 @@ struct KernelCore::Impl { // Gets the dummy KThread for the caller, allocating a new one if this is the first time KThread* GetHostDummyThread() { const thread_local auto thread = - KThread::Create( + KThread::CreateThread( system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0, - KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr, - []([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr) + KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr) .Unwrap(); return thread.get(); } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 73b85d6f9..9d5956ead 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -40,8 +40,9 @@ namespace { void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); - auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0, - owner_process.GetIdealCoreId(), stack_top, &owner_process); + auto thread_res = + KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0, + owner_process.GetIdealCoreId(), stack_top, &owner_process); std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap(); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index d04116115..326d3b9ec 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1532,8 +1532,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e std::shared_ptr<KThread> thread; { KScopedLightLock lk{process.GetStateLock()}; - CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority, - arg, core_id, stack_bottom, &process)); + CASCADE_RESULT(thread, + KThread::CreateUserThread(system, ThreadType::User, "", entry_point, + priority, arg, core_id, stack_bottom, &process)); } const auto new_thread_handle = process.GetHandleTable().Create(thread); @@ -2626,7 +2627,8 @@ void Call(Core::System& system, u32 immediate) { kernel.ExitSVCProfile(); if (!thread->IsCallingSvc()) { - thread->GetHostContext()->Rewind(); + auto* host_context = thread->GetHostContext().get(); + host_context->Rewind(); } system.EnterDynarmicProfile(); diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp index 6818d5eee..329e416c7 100644 --- a/src/input_common/mouse/mouse_input.cpp +++ b/src/input_common/mouse/mouse_input.cpp @@ -59,7 +59,7 @@ void Mouse::UpdateYuzuSettings() { }); } -void Mouse::PressButton(int x, int y, int button_) { +void Mouse::PressButton(int x, int y, MouseButton button_) { const auto button_index = static_cast<std::size_t>(button_); if (button_index >= mouse_info.size()) { return; @@ -67,7 +67,7 @@ void Mouse::PressButton(int x, int y, int button_) { const auto button = 1U << button_index; buttons |= static_cast<u16>(button); - last_button = static_cast<MouseButton>(button_index); + last_button = button_; mouse_info[button_index].mouse_origin = Common::MakeVec(x, y); mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y); @@ -129,7 +129,7 @@ void Mouse::MouseMove(int x, int y, int center_x, int center_y) { } } -void Mouse::ReleaseButton(int button_) { +void Mouse::ReleaseButton(MouseButton button_) { const auto button_index = static_cast<std::size_t>(button_); if (button_index >= mouse_info.size()) { return; @@ -152,6 +152,11 @@ void Mouse::BeginConfiguration() { void Mouse::EndConfiguration() { buttons = 0; + for (MouseInfo& info : mouse_info) { + info.tilt_speed = 0; + info.data.pressed = false; + info.data.axis = {0, 0}; + } last_button = MouseButton::Undefined; mouse_queue.Clear(); configuring = false; diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h index a75f0ecb5..750d9b011 100644 --- a/src/input_common/mouse/mouse_input.h +++ b/src/input_common/mouse/mouse_input.h @@ -18,10 +18,12 @@ namespace MouseInput { enum class MouseButton { Left, - Wheel, Right, - Forward, + Wheel, Backward, + Forward, + Task, + Extra, Undefined, }; @@ -51,7 +53,7 @@ public: * @param y the y-coordinate of the cursor * @param button_ the button pressed */ - void PressButton(int x, int y, int button_); + void PressButton(int x, int y, MouseButton button_); /** * Signals that mouse has moved. @@ -65,7 +67,7 @@ public: /** * Signals that a motion sensor tilt has ended. */ - void ReleaseButton(int button_); + void ReleaseButton(MouseButton button_); [[nodiscard]] bool ToggleButton(std::size_t button_); [[nodiscard]] bool UnlockButton(std::size_t button_); @@ -99,7 +101,7 @@ private: u16 lock_buttons{}; std::thread update_thread; MouseButton last_button{MouseButton::Undefined}; - std::array<MouseInfo, 5> mouse_info; + std::array<MouseInfo, 7> mouse_info; Common::SPSCQueue<MouseStatus> mouse_queue; bool configuring{false}; bool update_thread_running{true}; diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index b2ded2065..751cbe196 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -67,15 +67,16 @@ void TestControl1::DoWork() { value++; } results[id] = value; - Fiber::YieldTo(work_fibers[id].get(), thread_fibers[id].get()); + Fiber::YieldTo(work_fibers[id], *thread_fibers[id]); } void TestControl1::ExecuteThread(u32 id) { thread_ids.Register(id); - thread_fibers[id] = Fiber::ThreadToFiber(); + auto thread_fiber = Fiber::ThreadToFiber(); + thread_fibers[id] = thread_fiber; work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); items[id] = rand() % 256; - Fiber::YieldTo(thread_fibers[id].get(), work_fibers[id].get()); + Fiber::YieldTo(thread_fibers[id], *work_fibers[id]); thread_fibers[id]->Exit(); } @@ -116,11 +117,11 @@ public: for (u32 i = 0; i < 12000; i++) { value1 += i; } - Fiber::YieldTo(fiber1.get(), fiber3.get()); + Fiber::YieldTo(fiber1, *fiber3); const u32 id = thread_ids.Get(); assert1 = id == 1; value2 += 5000; - Fiber::YieldTo(fiber1.get(), thread_fibers[id].get()); + Fiber::YieldTo(fiber1, *thread_fibers[id]); } void DoWork2() { @@ -128,7 +129,7 @@ public: ; value2 = 2000; trap = false; - Fiber::YieldTo(fiber2.get(), fiber1.get()); + Fiber::YieldTo(fiber2, *fiber1); assert3 = false; } @@ -136,19 +137,19 @@ public: const u32 id = thread_ids.Get(); assert2 = id == 0; value1 += 1000; - Fiber::YieldTo(fiber3.get(), thread_fibers[id].get()); + Fiber::YieldTo(fiber3, *thread_fibers[id]); } void ExecuteThread(u32 id); void CallFiber1() { const u32 id = thread_ids.Get(); - Fiber::YieldTo(thread_fibers[id].get(), fiber1.get()); + Fiber::YieldTo(thread_fibers[id], *fiber1); } void CallFiber2() { const u32 id = thread_ids.Get(); - Fiber::YieldTo(thread_fibers[id].get(), fiber2.get()); + Fiber::YieldTo(thread_fibers[id], *fiber2); } void Exit(); @@ -184,7 +185,8 @@ static void WorkControl2_3(void* control) { void TestControl2::ExecuteThread(u32 id) { thread_ids.Register(id); - thread_fibers[id] = Fiber::ThreadToFiber(); + auto thread_fiber = Fiber::ThreadToFiber(); + thread_fibers[id] = thread_fiber; } void TestControl2::Exit() { @@ -239,23 +241,23 @@ public: void DoWork1() { value1 += 1; - Fiber::YieldTo(fiber1.get(), fiber2.get()); + Fiber::YieldTo(fiber1, *fiber2); const u32 id = thread_ids.Get(); value3 += 1; - Fiber::YieldTo(fiber1.get(), thread_fibers[id].get()); + Fiber::YieldTo(fiber1, *thread_fibers[id]); } void DoWork2() { value2 += 1; const u32 id = thread_ids.Get(); - Fiber::YieldTo(fiber2.get(), thread_fibers[id].get()); + Fiber::YieldTo(fiber2, *thread_fibers[id]); } void ExecuteThread(u32 id); void CallFiber1() { const u32 id = thread_ids.Get(); - Fiber::YieldTo(thread_fibers[id].get(), fiber1.get()); + Fiber::YieldTo(thread_fibers[id], *fiber1); } void Exit(); @@ -264,7 +266,7 @@ public: u32 value2{}; u32 value3{}; ThreadIds thread_ids; - std::vector<std::unique_ptr<Common::Fiber>> thread_fibers; + std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; std::shared_ptr<Common::Fiber> fiber1; std::shared_ptr<Common::Fiber> fiber2; }; @@ -281,7 +283,8 @@ static void WorkControl3_2(void* control) { void TestControl3::ExecuteThread(u32 id) { thread_ids.Register(id); - thread_fibers[id] = Fiber::ThreadToFiber(); + auto thread_fiber = Fiber::ThreadToFiber(); + thread_fibers[id] = thread_fiber; } void TestControl3::Exit() { @@ -329,7 +332,7 @@ public: void Execute() { thread_fiber = Fiber::ThreadToFiber(); - Fiber::YieldTo(thread_fiber.get(), fiber1.get()); + Fiber::YieldTo(thread_fiber, *fiber1); thread_fiber->Exit(); } @@ -337,7 +340,7 @@ public: fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); if (rewinded) { goal_reached = true; - Fiber::YieldTo(fiber1.get(), thread_fiber.get()); + Fiber::YieldTo(fiber1, *thread_fiber); } rewinded = true; fiber1->Rewind(); diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 2a6844ab1..4de1e37e5 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -9,6 +9,7 @@ #include <deque> #include <memory> #include <mutex> +#include <numeric> #include <span> #include <unordered_map> #include <vector> @@ -91,7 +92,7 @@ class BufferCache { }; public: - static constexpr u32 SKIP_CACHE_SIZE = 4096; + static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096; explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, @@ -240,9 +241,9 @@ private: template <bool insert> void ChangeRegister(BufferId buffer_id); - void SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); + bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); - void SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); + bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, std::span<BufferCopy> copies); @@ -297,6 +298,11 @@ private: std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{}; + std::array<u32, 16> uniform_cache_hits{}; + std::array<u32, 16> uniform_cache_shots{}; + + u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE; + bool has_deleted_buffers = false; std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty> @@ -328,6 +334,19 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_, template <class P> void BufferCache<P>::TickFrame() { + // Calculate hits and shots and move hit bits to the right + const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end()); + const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end()); + std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1, + uniform_cache_hits.begin() + 1); + std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1, + uniform_cache_shots.begin() + 1); + uniform_cache_hits[0] = 0; + uniform_cache_shots[0] = 0; + + const bool skip_preferred = hits * 256 < shots * 251; + uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; + delayed_destruction_ring.Tick(); } @@ -671,7 +690,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 const VAddr cpu_addr = binding.cpu_addr; const u32 size = binding.size; Buffer& buffer = slot_buffers[binding.buffer_id]; - if (size <= SKIP_CACHE_SIZE && !buffer.IsRegionGpuModified(cpu_addr, size)) { + if (size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size)) { if constexpr (IS_OPENGL) { if (runtime.HasFastBufferSubData()) { // Fast path for Nvidia @@ -692,7 +711,12 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 return; } // Classic cached path - SynchronizeBuffer(buffer, cpu_addr, size); + const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size); + if (sync_cached) { + ++uniform_cache_hits[0]; + } + ++uniform_cache_shots[0]; + if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) { // Skip binding if it's not needed and if the bound buffer is not the fast version // This exists to avoid instances where the fast buffer is bound and a GPU write happens @@ -1106,15 +1130,15 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) { } template <class P> -void BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { +bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { if (buffer.CpuAddr() == 0) { - return; + return true; } - SynchronizeBufferImpl(buffer, cpu_addr, size); + return SynchronizeBufferImpl(buffer, cpu_addr, size); } template <class P> -void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { +bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { boost::container::small_vector<BufferCopy, 4> copies; u64 total_size_bytes = 0; u64 largest_copy = 0; @@ -1128,10 +1152,11 @@ void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s largest_copy = std::max(largest_copy, range_size); }); if (total_size_bytes == 0) { - return; + return true; } const std::span<BufferCopy> copies_span(copies.data(), copies.size()); UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); + return false; } template <class P> diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp index acf2668dc..8317d0636 100644 --- a/src/video_core/compatible_formats.cpp +++ b/src/video_core/compatible_formats.cpp @@ -48,6 +48,15 @@ constexpr std::array VIEW_CLASS_32_BITS{ PixelFormat::A2B10G10R10_UINT, }; +constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{ + PixelFormat::R16G16_FLOAT, PixelFormat::B10G11R11_FLOAT, PixelFormat::R32_FLOAT, + PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT, + PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM, + PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM, + PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT, + PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT, +}; + // TODO: How should we handle 24 bits? constexpr std::array VIEW_CLASS_16_BITS{ @@ -205,7 +214,6 @@ constexpr Table MakeViewTable() { EnableRange(view, VIEW_CLASS_128_BITS); EnableRange(view, VIEW_CLASS_96_BITS); EnableRange(view, VIEW_CLASS_64_BITS); - EnableRange(view, VIEW_CLASS_32_BITS); EnableRange(view, VIEW_CLASS_16_BITS); EnableRange(view, VIEW_CLASS_8_BITS); EnableRange(view, VIEW_CLASS_RGTC1_RED); @@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() { EnableRange(copy, COPY_CLASS_64_BITS); return copy; } + +constexpr Table MakeNativeBgrViewTable() { + Table copy = MakeViewTable(); + EnableRange(copy, VIEW_CLASS_32_BITS); + return copy; +} + +constexpr Table MakeNonNativeBgrViewTable() { + Table copy = MakeViewTable(); + EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR); + return copy; +} + +constexpr Table MakeNativeBgrCopyTable() { + Table copy = MakeCopyTable(); + EnableRange(copy, VIEW_CLASS_32_BITS); + return copy; +} + +constexpr Table MakeNonNativeBgrCopyTable() { + Table copy = MakeCopyTable(); + EnableRange(copy, VIEW_CLASS_32_BITS); + return copy; +} } // Anonymous namespace -bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) { +bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views, + bool native_bgr) { if (broken_views) { // If format views are broken, only accept formats that are identical. return format_a == format_b; } - static constexpr Table TABLE = MakeViewTable(); - return IsSupported(TABLE, format_a, format_b); + static constexpr Table BGR_TABLE = MakeNativeBgrViewTable(); + static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable(); + return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b); } -bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) { - static constexpr Table TABLE = MakeCopyTable(); - return IsSupported(TABLE, format_a, format_b); +bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) { + static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable(); + static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable(); + return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b); } } // namespace VideoCore::Surface diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h index 9a0522988..55745e042 100644 --- a/src/video_core/compatible_formats.h +++ b/src/video_core/compatible_formats.h @@ -8,8 +8,9 @@ namespace VideoCore::Surface { -bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views); +bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views, + bool native_bgr); -bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); +bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr); } // namespace VideoCore::Surface diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 970120acc..3494318ca 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -5,6 +5,7 @@ set(SHADER_FILES convert_float_to_depth.frag full_screen_triangle.vert opengl_copy_bc4.comp + opengl_copy_bgra.comp opengl_present.frag opengl_present.vert pitch_unswizzle.comp diff --git a/src/video_core/host_shaders/opengl_copy_bgra.comp b/src/video_core/host_shaders/opengl_copy_bgra.comp new file mode 100644 index 000000000..2571a4abf --- /dev/null +++ b/src/video_core/host_shaders/opengl_copy_bgra.comp @@ -0,0 +1,15 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 430 core + +layout (local_size_x = 4, local_size_y = 4) in; + +layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input; +layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output; + +void main() { + vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID)); + imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra); +} diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 6da3906a4..c225d1fc9 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -73,7 +73,8 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_) for (auto& stage_uniforms : fast_uniforms) { for (OGLBuffer& buffer : stage_uniforms) { buffer.Create(); - glNamedBufferData(buffer.handle, BufferCache::SKIP_CACHE_SIZE, nullptr, GL_STREAM_DRAW); + glNamedBufferData(buffer.handle, BufferCache::DEFAULT_SKIP_CACHE_SIZE, nullptr, + GL_STREAM_DRAW); } } for (auto& stage_uniforms : copy_uniforms) { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 12434db67..e028677e9 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -96,7 +96,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{ {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM - {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT @@ -125,7 +125,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{ {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM - {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB @@ -396,6 +396,17 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) { } } +[[nodiscard]] bool IsPixelFormatBGR(PixelFormat format) { + switch (format) { + case PixelFormat::B5G6R5_UNORM: + case PixelFormat::B8G8R8A8_UNORM: + case PixelFormat::B8G8R8A8_SRGB: + return true; + default: + return false; + } +} + } // Anonymous namespace ImageBufferMap::~ImageBufferMap() { @@ -512,6 +523,9 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) { if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) { return false; } + if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { + return false; + } return true; } @@ -520,6 +534,8 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src, if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) { ASSERT(src.info.type == ImageType::e3D); util_shaders.CopyBC4(dst, src, copies); + } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { + util_shaders.CopyBGR(dst, src, copies); } else { UNREACHABLE(); } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index a6172f009..3fbaa102f 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -86,6 +86,11 @@ public: FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; + bool HasNativeBgr() const noexcept { + // OpenGL does not have native support for the BGR internal format + return false; + } + bool HasBrokenTextureViewFormats() const noexcept { return has_broken_texture_view_formats; } diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index 31ec68505..2fe4799bc 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -14,6 +14,7 @@ #include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" #include "video_core/host_shaders/opengl_copy_bc4_comp.h" +#include "video_core/host_shaders/opengl_copy_bgra_comp.h" #include "video_core/host_shaders/pitch_unswizzle_comp.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -48,6 +49,11 @@ OGLProgram MakeProgram(std::string_view source) { return program; } +size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) { + return static_cast<size_t>(copy.extent.width * copy.extent.height * + copy.src_subresource.num_layers); +} + } // Anonymous namespace UtilShaders::UtilShaders(ProgramManager& program_manager_) @@ -55,6 +61,7 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_) block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), + copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)), copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); swizzle_table_buffer.Create(); @@ -205,6 +212,43 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im program_manager.RestoreGuestCompute(); } +void UtilShaders::CopyBGR(Image& dst_image, Image& src_image, + std::span<const VideoCommon::ImageCopy> copies) { + static constexpr GLuint BINDING_INPUT_IMAGE = 0; + static constexpr GLuint BINDING_OUTPUT_IMAGE = 1; + static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0}; + const u32 bytes_per_block = BytesPerBlock(dst_image.info.format); + switch (bytes_per_block) { + case 2: + // BGR565 copy + for (const ImageCopy& copy : copies) { + ASSERT(copy.src_offset == zero_offset); + ASSERT(copy.dst_offset == zero_offset); + bgr_copy_pass.Execute(dst_image, src_image, copy); + } + break; + case 4: { + // BGRA8 copy + program_manager.BindHostCompute(copy_bgra_program.handle); + constexpr GLenum FORMAT = GL_RGBA8; + for (const ImageCopy& copy : copies) { + ASSERT(copy.src_offset == zero_offset); + ASSERT(copy.dst_offset == zero_offset); + glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(), + copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT); + glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(), + copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT); + glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth); + } + program_manager.RestoreGuestCompute(); + break; + } + default: + UNREACHABLE(); + break; + } +} + GLenum StoreFormat(u32 bytes_per_block) { switch (bytes_per_block) { case 1: @@ -222,4 +266,36 @@ GLenum StoreFormat(u32 bytes_per_block) { return GL_R8UI; } +void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image, + const ImageCopy& copy) { + if (CopyBufferCreationNeeded(copy)) { + CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565); + } + // Copy from source to PBO + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width); + glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle); + glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, + copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + static_cast<GLsizei>(bgr16_pbo_size), nullptr); + + // Copy from PBO to destination in reverse order + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle); + glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, + copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, + nullptr); +} + +bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) { + return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16); +} + +void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) { + bgr16_pbo.Create(); + bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16); + glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY); +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h index 7b1d16b09..93b009743 100644 --- a/src/video_core/renderer_opengl/util_shaders.h +++ b/src/video_core/renderer_opengl/util_shaders.h @@ -19,6 +19,22 @@ class ProgramManager; struct ImageBufferMap; +class Bgr565CopyPass { +public: + Bgr565CopyPass() = default; + ~Bgr565CopyPass() = default; + + void Execute(const Image& dst_image, const Image& src_image, + const VideoCommon::ImageCopy& copy); + +private: + [[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy); + void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format); + + OGLBuffer bgr16_pbo; + size_t bgr16_pbo_size{}; +}; + class UtilShaders { public: explicit UtilShaders(ProgramManager& program_manager); @@ -36,6 +52,9 @@ public: void CopyBC4(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies); + void CopyBGR(Image& dst_image, Image& src_image, + std::span<const VideoCommon::ImageCopy> copies); + private: ProgramManager& program_manager; @@ -44,7 +63,10 @@ private: OGLProgram block_linear_unswizzle_2d_program; OGLProgram block_linear_unswizzle_3d_program; OGLProgram pitch_unswizzle_program; + OGLProgram copy_bgra_program; OGLProgram copy_bc4_program; + + Bgr565CopyPass bgr_copy_pass; }; GLenum StoreFormat(u32 bytes_per_block); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index b08c23459..3aee27ce0 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -93,6 +93,11 @@ struct TextureCacheRuntime { // No known Vulkan driver has broken image views return false; } + + bool HasNativeBgr() const noexcept { + // All known Vulkan drivers can natively handle BGR textures + return true; + } }; class Image : public VideoCommon::ImageBase { diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp index 959b3f115..9914926b3 100644 --- a/src/video_core/texture_cache/image_base.cpp +++ b/src/video_core/texture_cache/image_base.cpp @@ -120,9 +120,10 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i if (lhs.info.type == ImageType::Linear) { base = SubresourceBase{.level = 0, .layer = 0}; } else { - // We are passing relaxed formats as an option, having broken views or not won't matter + // We are passing relaxed formats as an option, having broken views/bgr or not won't matter static constexpr bool broken_views = false; - base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views); + static constexpr bool native_bgr = true; + base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr); } if (!base) { LOG_ERROR(HW_GPU, "Image alias should have been flipped"); diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp index 18f72e508..f89a40b4c 100644 --- a/src/video_core/texture_cache/image_view_base.cpp +++ b/src/video_core/texture_cache/image_view_base.cpp @@ -24,7 +24,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i .height = std::max(image_info.size.height >> range.base.level, 1u), .depth = std::max(image_info.size.depth >> range.base.level, 1u), } { - ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false), + ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true), "Image view format {} is incompatible with image format {}", info.format, image_info.format); const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index b1da69971..98e33c3a0 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -876,6 +876,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, return ImageId{}; } const bool broken_views = runtime.HasBrokenTextureViewFormats(); + const bool native_bgr = runtime.HasNativeBgr(); ImageId image_id; const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { @@ -885,11 +886,12 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, if (existing_image.gpu_addr == gpu_addr && existing.type == info.type && existing.pitch == info.pitch && IsPitchLinearSameSize(existing, info, strict_size) && - IsViewCompatible(existing.format, info.format, broken_views)) { + IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) { image_id = existing_image_id; return true; } - } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) { + } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views, + native_bgr)) { image_id = existing_image_id; return true; } @@ -920,6 +922,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA ImageInfo new_info = info; const size_t size_bytes = CalculateGuestSizeInBytes(new_info); const bool broken_views = runtime.HasBrokenTextureViewFormats(); + const bool native_bgr = runtime.HasNativeBgr(); std::vector<ImageId> overlap_ids; std::vector<ImageId> left_aliased_ids; std::vector<ImageId> right_aliased_ids; @@ -935,8 +938,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA return; } static constexpr bool strict_size = true; - const std::optional<OverlapResult> solution = - ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views); + const std::optional<OverlapResult> solution = ResolveOverlap( + new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr); if (solution) { gpu_addr = solution->gpu_addr; cpu_addr = solution->cpu_addr; @@ -946,10 +949,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA } static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); - if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) { + if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) { left_aliased_ids.push_back(overlap_id); } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, - broken_views)) { + broken_views, native_bgr)) { right_aliased_ids.push_back(overlap_id); } }); diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index a0bc1f7b6..2c42d1449 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -1035,13 +1035,13 @@ bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool stri std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, VAddr cpu_addr, const ImageBase& overlap, - bool strict_size, bool broken_views) { + bool strict_size, bool broken_views, bool native_bgr) { ASSERT(new_info.type != ImageType::Linear); ASSERT(overlap.info.type != ImageType::Linear); if (!IsLayerStrideCompatible(new_info, overlap.info)) { return std::nullopt; } - if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) { + if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views, native_bgr)) { return std::nullopt; } if (gpu_addr == overlap.gpu_addr) { @@ -1085,14 +1085,14 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) { std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, RelaxedOptions options, - bool broken_views) { + bool broken_views, bool native_bgr) { const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr); if (!base) { return std::nullopt; } const ImageInfo& existing = image.info; if (False(options & RelaxedOptions::Format)) { - if (!IsViewCompatible(existing.format, candidate.format, broken_views)) { + if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) { return std::nullopt; } } @@ -1129,8 +1129,9 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const } bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, - RelaxedOptions options, bool broken_views) { - return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value(); + RelaxedOptions options, bool broken_views, bool native_bgr) { + return FindSubresource(candidate, image, candidate_addr, options, broken_views, native_bgr) + .has_value(); } void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index 52a9207d6..4d0072867 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -87,7 +87,8 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima [[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, VAddr cpu_addr, const ImageBase& overlap, - bool strict_size, bool broken_views); + bool strict_size, bool broken_views, + bool native_bgr); [[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); @@ -95,11 +96,11 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima const ImageBase& image, GPUVAddr candidate_addr, RelaxedOptions options, - bool broken_views); + bool broken_views, bool native_bgr); [[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, - GPUVAddr candidate_addr, RelaxedOptions options, - bool broken_views); + GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views, + bool native_bgr); void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, const ImageBase* src); diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 50938fb04..15c09e0ad 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -387,6 +387,25 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { } } +MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) { + switch (button) { + case Qt::LeftButton: + return MouseInput::MouseButton::Left; + case Qt::RightButton: + return MouseInput::MouseButton::Right; + case Qt::MiddleButton: + return MouseInput::MouseButton::Wheel; + case Qt::BackButton: + return MouseInput::MouseButton::Backward; + case Qt::ForwardButton: + return MouseInput::MouseButton::Forward; + case Qt::TaskButton: + return MouseInput::MouseButton::Task; + default: + return MouseInput::MouseButton::Extra; + } +} + void GRenderWindow::mousePressEvent(QMouseEvent* event) { // Touch input is handled in TouchBeginEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) { @@ -395,7 +414,8 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { auto pos = event->pos(); const auto [x, y] = ScaleTouch(pos); - input_subsystem->GetMouse()->PressButton(x, y, event->button()); + const auto button = QtButtonToMouseButton(event->button()); + input_subsystem->GetMouse()->PressButton(x, y, button); if (event->button() == Qt::LeftButton) { this->TouchPressed(x, y, 0); @@ -429,7 +449,8 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { return; } - input_subsystem->GetMouse()->ReleaseButton(event->button()); + const auto button = QtButtonToMouseButton(event->button()); + input_subsystem->GetMouse()->ReleaseButton(button); if (event->button() == Qt::LeftButton) { this->TouchReleased(0); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index b5ec7de07..acfe2bc8c 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -28,6 +28,10 @@ namespace InputCommon { class InputSubsystem; } +namespace MouseInput { +enum class MouseButton; +} + namespace VideoCore { enum class LoadCallbackStage; } @@ -149,6 +153,9 @@ public: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; + /// Converts a Qt mouse button into MouseInput mouse button + static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button); + void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index b319d69fc..1bac57bb2 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -235,7 +235,7 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{ {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, - {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}}, + {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, }}; @@ -508,7 +508,7 @@ void Config::ReadControlValues() { Settings::values.emulate_analog_keyboard = ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); - Settings::values.mouse_panning = ReadSetting(QStringLiteral("mouse_panning"), false).toBool(); + Settings::values.mouse_panning = false; Settings::values.mouse_panning_sensitivity = ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat(); @@ -1182,7 +1182,6 @@ void Config::SaveControlValues() { WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); WriteSetting(QStringLiteral("emulate_analog_keyboard"), Settings::values.emulate_analog_keyboard, false); - WriteSetting(QStringLiteral("mouse_panning"), Settings::values.mouse_panning, false); WriteSetting(QStringLiteral("mouse_panning_sensitivity"), Settings::values.mouse_panning_sensitivity, 1.0f); qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 55adbd53d..c9318c562 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -21,6 +21,7 @@ #include "input_common/mouse/mouse_poller.h" #include "input_common/udp/udp.h" #include "ui_configure_input_player.h" +#include "yuzu/bootmanager.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_input_player_widget.h" @@ -1362,7 +1363,8 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { return; } - input_subsystem->GetMouse()->PressButton(0, 0, event->button()); + const auto button = GRenderWindow::QtButtonToMouseButton(event->button()); + input_subsystem->GetMouse()->PressButton(0, 0, button); } void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0ba7c07cc..56d892a31 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -854,8 +854,7 @@ void GMainWindow::InitializeHotkeys() { connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this), &QShortcut::activated, this, [&] { Settings::values.mouse_panning = !Settings::values.mouse_panning; - if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { - mouse_hide_timer.start(); + if (Settings::values.mouse_panning) { render_window->installEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, true); } @@ -1208,11 +1207,14 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { renderer_status_button->setDisabled(true); if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { - mouse_hide_timer.start(); render_window->installEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, true); } + if (UISettings::values.hide_mouse) { + mouse_hide_timer.start(); + } + std::string title_name; std::string title_version; const auto res = system.GetGameName(title_name); @@ -2372,12 +2374,15 @@ void GMainWindow::OnConfigure() { if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { render_window->installEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, true); - mouse_hide_timer.start(); } else { render_window->removeEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, false); } + if (UISettings::values.hide_mouse) { + mouse_hide_timer.start(); + } + UpdateStatusButtons(); } @@ -2615,8 +2620,7 @@ void GMainWindow::UpdateUISettings() { } void GMainWindow::HideMouseCursor() { - if (emu_thread == nullptr || - (!UISettings::values.hide_mouse && !Settings::values.mouse_panning)) { + if (emu_thread == nullptr && UISettings::values.hide_mouse) { mouse_hide_timer.stop(); ShowMouseCursor(); return; @@ -2626,8 +2630,7 @@ void GMainWindow::HideMouseCursor() { void GMainWindow::ShowMouseCursor() { render_window->unsetCursor(); - if (emu_thread != nullptr && - (UISettings::values.hide_mouse || Settings::values.mouse_panning)) { + if (emu_thread != nullptr && UISettings::values.hide_mouse) { mouse_hide_timer.start(); } } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 7e391ab89..ce8b7c218 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -35,18 +35,36 @@ void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { input_subsystem->GetMouse()->MouseMove(x, y, 0, 0); } +MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { + switch (button) { + case SDL_BUTTON_LEFT: + return MouseInput::MouseButton::Left; + case SDL_BUTTON_RIGHT: + return MouseInput::MouseButton::Right; + case SDL_BUTTON_MIDDLE: + return MouseInput::MouseButton::Wheel; + case SDL_BUTTON_X1: + return MouseInput::MouseButton::Backward; + case SDL_BUTTON_X2: + return MouseInput::MouseButton::Forward; + default: + return MouseInput::MouseButton::Undefined; + } +} + void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { + const auto mouse_button = SDLButtonToMouseButton(button); if (button == SDL_BUTTON_LEFT) { if (state == SDL_PRESSED) { TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); } else { TouchReleased(0); } - } else if (button == SDL_BUTTON_RIGHT) { + } else { if (state == SDL_PRESSED) { - input_subsystem->GetMouse()->PressButton(x, y, button); + input_subsystem->GetMouse()->PressButton(x, y, mouse_button); } else { - input_subsystem->GetMouse()->ReleaseButton(button); + input_subsystem->GetMouse()->ReleaseButton(mouse_button); } } } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 51a12a6a9..0e17bbca7 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -18,6 +18,10 @@ namespace InputCommon { class InputSubsystem; } +namespace MouseInput { +enum class MouseButton; +} + class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { public: explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); @@ -42,6 +46,9 @@ protected: /// Called by WaitEvent when the mouse moves. void OnMouseMotion(s32 x, s32 y); + /// Converts a SDL mouse button into MouseInput mouse button + MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const; + /// Called by WaitEvent when a mouse button is pressed or released void OnMouseButton(u32 button, u8 state, s32 x, s32 y); |