summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/common/fiber.cpp31
-rw-r--r--src/common/fiber.h4
-rw-r--r--src/core/cpu_manager.cpp13
-rw-r--r--src/core/cpu_manager.h2
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp25
-rw-r--r--src/core/hle/kernel/k_scheduler.h10
-rw-r--r--src/core/hle/kernel/k_thread.cpp50
-rw-r--r--src/core/hle/kernel/k_thread.h34
-rw-r--r--src/core/hle/kernel/kernel.cpp11
-rw-r--r--src/core/hle/kernel/process.cpp5
-rw-r--r--src/core/hle/kernel/svc.cpp8
-rw-r--r--src/input_common/mouse/mouse_input.cpp11
-rw-r--r--src/input_common/mouse/mouse_input.h12
-rw-r--r--src/tests/common/fibers.cpp39
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h45
-rw-r--r--src/video_core/compatible_formats.cpp49
-rw-r--r--src/video_core/compatible_formats.h5
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/opengl_copy_bgra.comp15
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp20
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h5
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp76
-rw-r--r--src/video_core/renderer_opengl/util_shaders.h22
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h5
-rw-r--r--src/video_core/texture_cache/image_base.cpp5
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp2
-rw-r--r--src/video_core/texture_cache/texture_cache.h15
-rw-r--r--src/video_core/texture_cache/util.cpp13
-rw-r--r--src/video_core/texture_cache/util.h9
-rw-r--r--src/yuzu/bootmanager.cpp25
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp4
-rw-r--r--src/yuzu/main.cpp19
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp24
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h7
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)
27 # /Zo - Enhanced debug info for optimized builds 27 # /Zo - Enhanced debug info for optimized builds
28 # /permissive- - Enables stricter C++ standards conformance checks 28 # /permissive- - Enables stricter C++ standards conformance checks
29 # /EHsc - C++-only exception handling semantics 29 # /EHsc - C++-only exception handling semantics
30 # /utf-8 - Set source and execution character sets to UTF-8
30 # /volatile:iso - Use strict standards-compliant volatile semantics. 31 # /volatile:iso - Use strict standards-compliant volatile semantics.
31 # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates 32 # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
32 # /Zc:inline - Let codegen omit inline functions in object files 33 # /Zc:inline - Let codegen omit inline functions in object files
@@ -38,6 +39,7 @@ if (MSVC)
38 /permissive- 39 /permissive-
39 /EHsc 40 /EHsc
40 /std:c++latest 41 /std:c++latest
42 /utf-8
41 /volatile:iso 43 /volatile:iso
42 /Zc:externConstexpr 44 /Zc:externConstexpr
43 /Zc:inline 45 /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 {
24 std::function<void(void*)> rewind_point; 24 std::function<void(void*)> rewind_point;
25 void* rewind_parameter{}; 25 void* rewind_parameter{};
26 void* start_parameter{}; 26 void* start_parameter{};
27 Fiber* previous_fiber; 27 std::shared_ptr<Fiber> previous_fiber;
28 bool is_thread_fiber{}; 28 bool is_thread_fiber{};
29 bool released{}; 29 bool released{};
30 30
@@ -47,7 +47,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) {
47 ASSERT(impl->previous_fiber != nullptr); 47 ASSERT(impl->previous_fiber != nullptr);
48 impl->previous_fiber->impl->context = transfer.fctx; 48 impl->previous_fiber->impl->context = transfer.fctx;
49 impl->previous_fiber->impl->guard.unlock(); 49 impl->previous_fiber->impl->guard.unlock();
50 impl->previous_fiber = nullptr; 50 impl->previous_fiber.reset();
51 impl->entry_point(impl->start_parameter); 51 impl->entry_point(impl->start_parameter);
52 UNREACHABLE(); 52 UNREACHABLE();
53} 53}
@@ -116,20 +116,23 @@ void Fiber::Rewind() {
116 boost::context::detail::jump_fcontext(impl->rewind_context, this); 116 boost::context::detail::jump_fcontext(impl->rewind_context, this);
117} 117}
118 118
119void Fiber::YieldTo(Fiber* from, Fiber* to) { 119void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
120 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 120 to.impl->guard.lock();
121 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 121 to.impl->previous_fiber = weak_from.lock();
122 to->impl->guard.lock(); 122
123 to->impl->previous_fiber = from; 123 auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
124 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to); 124
125 ASSERT(from->impl->previous_fiber != nullptr); 125 // "from" might no longer be valid if the thread was killed
126 from->impl->previous_fiber->impl->context = transfer.fctx; 126 if (auto from = weak_from.lock()) {
127 from->impl->previous_fiber->impl->guard.unlock(); 127 ASSERT(from->impl->previous_fiber != nullptr);
128 from->impl->previous_fiber = nullptr; 128 from->impl->previous_fiber->impl->context = transfer.fctx;
129 from->impl->previous_fiber->impl->guard.unlock();
130 from->impl->previous_fiber.reset();
131 }
129} 132}
130 133
131std::unique_ptr<Fiber> Fiber::ThreadToFiber() { 134std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
132 std::unique_ptr<Fiber> fiber = std::unique_ptr<Fiber>{new Fiber()}; 135 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
133 fiber->impl->guard.lock(); 136 fiber->impl->guard.lock();
134 fiber->impl->is_thread_fiber = true; 137 fiber->impl->is_thread_fiber = true;
135 return fiber; 138 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:
41 41
42 /// Yields control from Fiber 'from' to Fiber 'to' 42 /// Yields control from Fiber 'from' to Fiber 'to'
43 /// Fiber 'from' must be the currently running fiber. 43 /// Fiber 'from' must be the currently running fiber.
44 static void YieldTo(Fiber* from, Fiber* to); 44 static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
45 [[nodiscard]] static std::unique_ptr<Fiber> ThreadToFiber(); 45 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
46 46
47 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); 47 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
48 48
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() {
111 auto& kernel = system.Kernel(); 111 auto& kernel = system.Kernel();
112 kernel.CurrentScheduler()->OnThreadStart(); 112 kernel.CurrentScheduler()->OnThreadStart();
113 auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); 113 auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
114 auto host_context = thread->GetHostContext(); 114 auto& host_context = thread->GetHostContext();
115 host_context->SetRewindPoint(GuestRewindFunction, this); 115 host_context->SetRewindPoint(GuestRewindFunction, this);
116 MultiCoreRunGuestLoop(); 116 MultiCoreRunGuestLoop();
117} 117}
@@ -148,8 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
148 auto core = kernel.GetCurrentHostThreadID(); 148 auto core = kernel.GetCurrentHostThreadID();
149 auto& scheduler = *kernel.CurrentScheduler(); 149 auto& scheduler = *kernel.CurrentScheduler();
150 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 150 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
151 Common::Fiber::YieldTo(current_thread->GetHostContext(), 151 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
152 core_data[core].host_context.get());
153 ASSERT(scheduler.ContextSwitchPending()); 152 ASSERT(scheduler.ContextSwitchPending());
154 ASSERT(core == kernel.GetCurrentHostThreadID()); 153 ASSERT(core == kernel.GetCurrentHostThreadID());
155 scheduler.RescheduleCurrentCore(); 154 scheduler.RescheduleCurrentCore();
@@ -202,7 +201,7 @@ void CpuManager::SingleCoreRunGuestThread() {
202 auto& kernel = system.Kernel(); 201 auto& kernel = system.Kernel();
203 kernel.CurrentScheduler()->OnThreadStart(); 202 kernel.CurrentScheduler()->OnThreadStart();
204 auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); 203 auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
205 auto host_context = thread->GetHostContext(); 204 auto& host_context = thread->GetHostContext();
206 host_context->SetRewindPoint(GuestRewindFunction, this); 205 host_context->SetRewindPoint(GuestRewindFunction, this);
207 SingleCoreRunGuestLoop(); 206 SingleCoreRunGuestLoop();
208} 207}
@@ -246,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
246 auto core = kernel.GetCurrentHostThreadID(); 245 auto core = kernel.GetCurrentHostThreadID();
247 auto& scheduler = *kernel.CurrentScheduler(); 246 auto& scheduler = *kernel.CurrentScheduler();
248 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 247 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
249 Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context.get()); 248 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
250 ASSERT(scheduler.ContextSwitchPending()); 249 ASSERT(scheduler.ContextSwitchPending());
251 ASSERT(core == kernel.GetCurrentHostThreadID()); 250 ASSERT(core == kernel.GetCurrentHostThreadID());
252 scheduler.RescheduleCurrentCore(); 251 scheduler.RescheduleCurrentCore();
@@ -272,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
272 scheduler.Unload(scheduler.GetCurrentThread()); 271 scheduler.Unload(scheduler.GetCurrentThread());
273 272
274 auto& next_scheduler = kernel.Scheduler(current_core); 273 auto& next_scheduler = kernel.Scheduler(current_core);
275 Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); 274 Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
276 } 275 }
277 276
278 // May have changed scheduler 277 // May have changed scheduler
@@ -364,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {
364 363
365 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 364 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
366 data.is_running = true; 365 data.is_running = true;
367 Common::Fiber::YieldTo(data.host_context.get(), current_thread->GetHostContext()); 366 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
368 data.is_running = false; 367 data.is_running = false;
369 data.is_paused = true; 368 data.is_paused = true;
370 data.exit_barrier->Wait(); 369 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:
83 void RunThread(std::size_t core); 83 void RunThread(std::size_t core);
84 84
85 struct CoreData { 85 struct CoreData {
86 std::unique_ptr<Common::Fiber> host_context; 86 std::shared_ptr<Common::Fiber> host_context;
87 std::unique_ptr<Common::Event> enter_barrier; 87 std::unique_ptr<Common::Event> enter_barrier;
88 std::unique_ptr<Common::Event> exit_barrier; 88 std::unique_ptr<Common::Event> exit_barrier;
89 std::atomic<bool> is_running; 89 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) {
608} 608}
609 609
610KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) { 610KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) {
611 switch_fiber = std::make_unique<Common::Fiber>(OnSwitch, this); 611 switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
612 state.needs_scheduling.store(true); 612 state.needs_scheduling.store(true);
613 state.interrupt_task_thread_runnable = false; 613 state.interrupt_task_thread_runnable = false;
614 state.should_count_idle = false; 614 state.should_count_idle = false;
@@ -726,15 +726,15 @@ void KScheduler::ScheduleImpl() {
726 // Save context for previous thread 726 // Save context for previous thread
727 Unload(previous_thread); 727 Unload(previous_thread);
728 728
729 Common::Fiber* old_context; 729 std::shared_ptr<Common::Fiber>* old_context;
730 if (previous_thread != nullptr) { 730 if (previous_thread != nullptr) {
731 old_context = previous_thread->GetHostContext(); 731 old_context = &previous_thread->GetHostContext();
732 } else { 732 } else {
733 old_context = idle_thread->GetHostContext(); 733 old_context = &idle_thread->GetHostContext();
734 } 734 }
735 guard.unlock(); 735 guard.unlock();
736 736
737 Common::Fiber::YieldTo(old_context, switch_fiber.get()); 737 Common::Fiber::YieldTo(*old_context, *switch_fiber);
738 /// When a thread wakes up, the scheduler may have changed to other in another core. 738 /// When a thread wakes up, the scheduler may have changed to other in another core.
739 auto& next_scheduler = *system.Kernel().CurrentScheduler(); 739 auto& next_scheduler = *system.Kernel().CurrentScheduler();
740 next_scheduler.SwitchContextStep2(); 740 next_scheduler.SwitchContextStep2();
@@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() {
769 break; 769 break;
770 } 770 }
771 } 771 }
772 Common::Fiber* next_context; 772 auto thread = next_thread ? next_thread : idle_thread;
773 if (next_thread != nullptr) { 773 Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
774 next_context = next_thread->GetHostContext();
775 } else {
776 next_context = idle_thread->GetHostContext();
777 }
778 Common::Fiber::YieldTo(switch_fiber.get(), next_context);
779 } while (!is_switch_pending()); 774 } while (!is_switch_pending());
780 } 775 }
781} 776}
@@ -800,9 +795,9 @@ void KScheduler::Initialize() {
800 std::string name = "Idle Thread Id:" + std::to_string(core_id); 795 std::string name = "Idle Thread Id:" + std::to_string(core_id);
801 std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc(); 796 std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
802 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); 797 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
803 auto thread_res = KThread::Create(system, ThreadType::Main, name, 0, 798 auto thread_res = KThread::CreateThread(
804 KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0, 799 system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
805 nullptr, std::move(init_func), init_func_parameter); 800 static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
806 idle_thread = thread_res.Unwrap().get(); 801 idle_thread = thread_res.Unwrap().get();
807} 802}
808 803
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:
68 68
69 void OnThreadStart(); 69 void OnThreadStart();
70 70
71 [[nodiscard]] Common::Fiber* ControlContext() { 71 [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() {
72 return switch_fiber.get(); 72 return switch_fiber;
73 } 73 }
74 74
75 [[nodiscard]] const Common::Fiber* ControlContext() const { 75 [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const {
76 return switch_fiber.get(); 76 return switch_fiber;
77 } 77 }
78 78
79 [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread); 79 [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
@@ -178,7 +178,7 @@ private:
178 178
179 KThread* idle_thread; 179 KThread* idle_thread;
180 180
181 std::unique_ptr<Common::Fiber> switch_fiber{}; 181 std::shared_ptr<Common::Fiber> switch_fiber{};
182 182
183 struct SchedulingState { 183 struct SchedulingState {
184 std::atomic<bool> needs_scheduling; 184 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) {
991 } 991 }
992} 992}
993 993
994ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, 994std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
995 std::string name, VAddr entry_point, 995 return host_context;
996 u32 priority, u64 arg, s32 processor_id,
997 VAddr stack_top, Process* owner_process) {
998 std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
999 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
1000 return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
1001 owner_process, std::move(init_func), init_func_parameter);
1002} 996}
1003 997
1004ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, 998ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system,
1005 std::string name, VAddr entry_point, 999 ThreadType type_flags, std::string name,
1006 u32 priority, u64 arg, s32 processor_id, 1000 VAddr entry_point, u32 priority, u64 arg,
1007 VAddr stack_top, Process* owner_process, 1001 s32 processor_id, VAddr stack_top,
1008 std::function<void(void*)>&& thread_start_func, 1002 Process* owner_process) {
1009 void* thread_start_parameter) {
1010 auto& kernel = system.Kernel(); 1003 auto& kernel = system.Kernel();
1011 1004
1012 std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel); 1005 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
1023 auto& scheduler = kernel.GlobalSchedulerContext(); 1016 auto& scheduler = kernel.GlobalSchedulerContext();
1024 scheduler.AddThread(thread); 1017 scheduler.AddThread(thread);
1025 1018
1026 thread->host_context =
1027 std::make_unique<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
1028
1029 return MakeResult<std::shared_ptr<KThread>>(std::move(thread)); 1019 return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
1030} 1020}
1031 1021
1022ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(
1023 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
1024 u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
1025 std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) {
1026 auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg,
1027 processor_id, stack_top, owner_process);
1028
1029 if (thread_result.Succeeded()) {
1030 (*thread_result)->host_context =
1031 std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
1032 }
1033
1034 return thread_result;
1035}
1036
1037ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread(
1038 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
1039 u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) {
1040 std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
1041
1042 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
1043
1044 return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id,
1045 stack_top, owner_process, std::move(init_func), init_func_parameter);
1046}
1047
1032KThread* GetCurrentThreadPointer(KernelCore& kernel) { 1048KThread* GetCurrentThreadPointer(KernelCore& kernel) {
1033 return kernel.GetCurrentEmuThread(); 1049 return kernel.GetCurrentEmuThread();
1034} 1050}
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:
116 using WaiterList = boost::intrusive::list<KThread>; 116 using WaiterList = boost::intrusive::list<KThread>;
117 117
118 /** 118 /**
119 * Creates and returns a new thread. The new thread is immediately scheduled 119 * Creates and returns a new thread.
120 * @param system The instance of the whole system 120 * @param system The instance of the whole system
121 * @param name The friendly name desired for the thread 121 * @param name The friendly name desired for the thread
122 * @param entry_point The address at which the thread should start execution 122 * @param entry_point The address at which the thread should start execution
@@ -127,12 +127,12 @@ public:
127 * @param owner_process The parent process for the thread, if null, it's a kernel thread 127 * @param owner_process The parent process for the thread, if null, it's a kernel thread
128 * @return A shared pointer to the newly created thread 128 * @return A shared pointer to the newly created thread
129 */ 129 */
130 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( 130 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
131 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, 131 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
132 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process); 132 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
133 133
134 /** 134 /**
135 * Creates and returns a new thread. The new thread is immediately scheduled 135 * Creates and returns a new thread, with a specified entry point.
136 * @param system The instance of the whole system 136 * @param system The instance of the whole system
137 * @param name The friendly name desired for the thread 137 * @param name The friendly name desired for the thread
138 * @param entry_point The address at which the thread should start execution 138 * @param entry_point The address at which the thread should start execution
@@ -145,11 +145,27 @@ public:
145 * @param thread_start_parameter The parameter which will passed to host context on init 145 * @param thread_start_parameter The parameter which will passed to host context on init
146 * @return A shared pointer to the newly created thread 146 * @return A shared pointer to the newly created thread
147 */ 147 */
148 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( 148 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
149 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, 149 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
150 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process, 150 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
151 std::function<void(void*)>&& thread_start_func, void* thread_start_parameter); 151 std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
152 152
153 /**
154 * Creates and returns a new thread for the emulated "user" process.
155 * @param system The instance of the whole system
156 * @param name The friendly name desired for the thread
157 * @param entry_point The address at which the thread should start execution
158 * @param priority The thread's priority
159 * @param arg User data to pass to the thread
160 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
161 * @param stack_top The address of the thread's stack top
162 * @param owner_process The parent process for the thread, if null, it's a kernel thread
163 * @return A shared pointer to the newly created thread
164 */
165 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread(
166 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
167 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
168
153 [[nodiscard]] std::string GetName() const override { 169 [[nodiscard]] std::string GetName() const override {
154 return name; 170 return name;
155 } 171 }
@@ -293,13 +309,7 @@ public:
293 return thread_context_64; 309 return thread_context_64;
294 } 310 }
295 311
296 [[nodiscard]] Common::Fiber* GetHostContext() { 312 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
297 return host_context.get();
298 }
299
300 [[nodiscard]] const Common::Fiber* GetHostContext() const {
301 return host_context.get();
302 }
303 313
304 [[nodiscard]] ThreadState GetState() const { 314 [[nodiscard]] ThreadState GetState() const {
305 return thread_state & ThreadState::Mask; 315 return thread_state & ThreadState::Mask;
@@ -725,7 +735,7 @@ private:
725 Common::SpinLock context_guard{}; 735 Common::SpinLock context_guard{};
726 736
727 // For emulation 737 // For emulation
728 std::unique_ptr<Common::Fiber> host_context{}; 738 std::shared_ptr<Common::Fiber> host_context{};
729 739
730 // For debugging 740 // For debugging
731 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 741 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 {
181 std::string name = "Suspend Thread Id:" + std::to_string(i); 181 std::string name = "Suspend Thread Id:" + std::to_string(i);
182 std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc(); 182 std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
183 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); 183 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
184 auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0, 184 auto thread_res = KThread::CreateThread(
185 0, 0, static_cast<u32>(i), 0, nullptr, 185 system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
186 std::move(init_func), init_func_parameter); 186 nullptr, std::move(init_func), init_func_parameter);
187 187
188 suspend_threads[i] = std::move(thread_res).Unwrap(); 188 suspend_threads[i] = std::move(thread_res).Unwrap();
189 } 189 }
@@ -221,10 +221,9 @@ struct KernelCore::Impl {
221 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 221 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
222 KThread* GetHostDummyThread() { 222 KThread* GetHostDummyThread() {
223 const thread_local auto thread = 223 const thread_local auto thread =
224 KThread::Create( 224 KThread::CreateThread(
225 system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0, 225 system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
226 KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr, 226 KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)
227 []([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
228 .Unwrap(); 227 .Unwrap();
229 return thread.get(); 228 return thread.get();
230 } 229 }
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 {
40void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { 40void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
41 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); 41 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); 42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
43 auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0, 43 auto thread_res =
44 owner_process.GetIdealCoreId(), stack_top, &owner_process); 44 KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0,
45 owner_process.GetIdealCoreId(), stack_top, &owner_process);
45 46
46 std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap(); 47 std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
47 48
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
1532 std::shared_ptr<KThread> thread; 1532 std::shared_ptr<KThread> thread;
1533 { 1533 {
1534 KScopedLightLock lk{process.GetStateLock()}; 1534 KScopedLightLock lk{process.GetStateLock()};
1535 CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority, 1535 CASCADE_RESULT(thread,
1536 arg, core_id, stack_bottom, &process)); 1536 KThread::CreateUserThread(system, ThreadType::User, "", entry_point,
1537 priority, arg, core_id, stack_bottom, &process));
1537 } 1538 }
1538 1539
1539 const auto new_thread_handle = process.GetHandleTable().Create(thread); 1540 const auto new_thread_handle = process.GetHandleTable().Create(thread);
@@ -2626,7 +2627,8 @@ void Call(Core::System& system, u32 immediate) {
2626 kernel.ExitSVCProfile(); 2627 kernel.ExitSVCProfile();
2627 2628
2628 if (!thread->IsCallingSvc()) { 2629 if (!thread->IsCallingSvc()) {
2629 thread->GetHostContext()->Rewind(); 2630 auto* host_context = thread->GetHostContext().get();
2631 host_context->Rewind();
2630 } 2632 }
2631 2633
2632 system.EnterDynarmicProfile(); 2634 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() {
59 }); 59 });
60} 60}
61 61
62void Mouse::PressButton(int x, int y, int button_) { 62void Mouse::PressButton(int x, int y, MouseButton button_) {
63 const auto button_index = static_cast<std::size_t>(button_); 63 const auto button_index = static_cast<std::size_t>(button_);
64 if (button_index >= mouse_info.size()) { 64 if (button_index >= mouse_info.size()) {
65 return; 65 return;
@@ -67,7 +67,7 @@ void Mouse::PressButton(int x, int y, int button_) {
67 67
68 const auto button = 1U << button_index; 68 const auto button = 1U << button_index;
69 buttons |= static_cast<u16>(button); 69 buttons |= static_cast<u16>(button);
70 last_button = static_cast<MouseButton>(button_index); 70 last_button = button_;
71 71
72 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y); 72 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
73 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y); 73 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) {
129 } 129 }
130} 130}
131 131
132void Mouse::ReleaseButton(int button_) { 132void Mouse::ReleaseButton(MouseButton button_) {
133 const auto button_index = static_cast<std::size_t>(button_); 133 const auto button_index = static_cast<std::size_t>(button_);
134 if (button_index >= mouse_info.size()) { 134 if (button_index >= mouse_info.size()) {
135 return; 135 return;
@@ -152,6 +152,11 @@ void Mouse::BeginConfiguration() {
152 152
153void Mouse::EndConfiguration() { 153void Mouse::EndConfiguration() {
154 buttons = 0; 154 buttons = 0;
155 for (MouseInfo& info : mouse_info) {
156 info.tilt_speed = 0;
157 info.data.pressed = false;
158 info.data.axis = {0, 0};
159 }
155 last_button = MouseButton::Undefined; 160 last_button = MouseButton::Undefined;
156 mouse_queue.Clear(); 161 mouse_queue.Clear();
157 configuring = false; 162 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 {
18 18
19enum class MouseButton { 19enum class MouseButton {
20 Left, 20 Left,
21 Wheel,
22 Right, 21 Right,
23 Forward, 22 Wheel,
24 Backward, 23 Backward,
24 Forward,
25 Task,
26 Extra,
25 Undefined, 27 Undefined,
26}; 28};
27 29
@@ -51,7 +53,7 @@ public:
51 * @param y the y-coordinate of the cursor 53 * @param y the y-coordinate of the cursor
52 * @param button_ the button pressed 54 * @param button_ the button pressed
53 */ 55 */
54 void PressButton(int x, int y, int button_); 56 void PressButton(int x, int y, MouseButton button_);
55 57
56 /** 58 /**
57 * Signals that mouse has moved. 59 * Signals that mouse has moved.
@@ -65,7 +67,7 @@ public:
65 /** 67 /**
66 * Signals that a motion sensor tilt has ended. 68 * Signals that a motion sensor tilt has ended.
67 */ 69 */
68 void ReleaseButton(int button_); 70 void ReleaseButton(MouseButton button_);
69 71
70 [[nodiscard]] bool ToggleButton(std::size_t button_); 72 [[nodiscard]] bool ToggleButton(std::size_t button_);
71 [[nodiscard]] bool UnlockButton(std::size_t button_); 73 [[nodiscard]] bool UnlockButton(std::size_t button_);
@@ -99,7 +101,7 @@ private:
99 u16 lock_buttons{}; 101 u16 lock_buttons{};
100 std::thread update_thread; 102 std::thread update_thread;
101 MouseButton last_button{MouseButton::Undefined}; 103 MouseButton last_button{MouseButton::Undefined};
102 std::array<MouseInfo, 5> mouse_info; 104 std::array<MouseInfo, 7> mouse_info;
103 Common::SPSCQueue<MouseStatus> mouse_queue; 105 Common::SPSCQueue<MouseStatus> mouse_queue;
104 bool configuring{false}; 106 bool configuring{false};
105 bool update_thread_running{true}; 107 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() {
67 value++; 67 value++;
68 } 68 }
69 results[id] = value; 69 results[id] = value;
70 Fiber::YieldTo(work_fibers[id].get(), thread_fibers[id].get()); 70 Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
71} 71}
72 72
73void TestControl1::ExecuteThread(u32 id) { 73void TestControl1::ExecuteThread(u32 id) {
74 thread_ids.Register(id); 74 thread_ids.Register(id);
75 thread_fibers[id] = Fiber::ThreadToFiber(); 75 auto thread_fiber = Fiber::ThreadToFiber();
76 thread_fibers[id] = thread_fiber;
76 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); 77 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
77 items[id] = rand() % 256; 78 items[id] = rand() % 256;
78 Fiber::YieldTo(thread_fibers[id].get(), work_fibers[id].get()); 79 Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
79 thread_fibers[id]->Exit(); 80 thread_fibers[id]->Exit();
80} 81}
81 82
@@ -116,11 +117,11 @@ public:
116 for (u32 i = 0; i < 12000; i++) { 117 for (u32 i = 0; i < 12000; i++) {
117 value1 += i; 118 value1 += i;
118 } 119 }
119 Fiber::YieldTo(fiber1.get(), fiber3.get()); 120 Fiber::YieldTo(fiber1, *fiber3);
120 const u32 id = thread_ids.Get(); 121 const u32 id = thread_ids.Get();
121 assert1 = id == 1; 122 assert1 = id == 1;
122 value2 += 5000; 123 value2 += 5000;
123 Fiber::YieldTo(fiber1.get(), thread_fibers[id].get()); 124 Fiber::YieldTo(fiber1, *thread_fibers[id]);
124 } 125 }
125 126
126 void DoWork2() { 127 void DoWork2() {
@@ -128,7 +129,7 @@ public:
128 ; 129 ;
129 value2 = 2000; 130 value2 = 2000;
130 trap = false; 131 trap = false;
131 Fiber::YieldTo(fiber2.get(), fiber1.get()); 132 Fiber::YieldTo(fiber2, *fiber1);
132 assert3 = false; 133 assert3 = false;
133 } 134 }
134 135
@@ -136,19 +137,19 @@ public:
136 const u32 id = thread_ids.Get(); 137 const u32 id = thread_ids.Get();
137 assert2 = id == 0; 138 assert2 = id == 0;
138 value1 += 1000; 139 value1 += 1000;
139 Fiber::YieldTo(fiber3.get(), thread_fibers[id].get()); 140 Fiber::YieldTo(fiber3, *thread_fibers[id]);
140 } 141 }
141 142
142 void ExecuteThread(u32 id); 143 void ExecuteThread(u32 id);
143 144
144 void CallFiber1() { 145 void CallFiber1() {
145 const u32 id = thread_ids.Get(); 146 const u32 id = thread_ids.Get();
146 Fiber::YieldTo(thread_fibers[id].get(), fiber1.get()); 147 Fiber::YieldTo(thread_fibers[id], *fiber1);
147 } 148 }
148 149
149 void CallFiber2() { 150 void CallFiber2() {
150 const u32 id = thread_ids.Get(); 151 const u32 id = thread_ids.Get();
151 Fiber::YieldTo(thread_fibers[id].get(), fiber2.get()); 152 Fiber::YieldTo(thread_fibers[id], *fiber2);
152 } 153 }
153 154
154 void Exit(); 155 void Exit();
@@ -184,7 +185,8 @@ static void WorkControl2_3(void* control) {
184 185
185void TestControl2::ExecuteThread(u32 id) { 186void TestControl2::ExecuteThread(u32 id) {
186 thread_ids.Register(id); 187 thread_ids.Register(id);
187 thread_fibers[id] = Fiber::ThreadToFiber(); 188 auto thread_fiber = Fiber::ThreadToFiber();
189 thread_fibers[id] = thread_fiber;
188} 190}
189 191
190void TestControl2::Exit() { 192void TestControl2::Exit() {
@@ -239,23 +241,23 @@ public:
239 241
240 void DoWork1() { 242 void DoWork1() {
241 value1 += 1; 243 value1 += 1;
242 Fiber::YieldTo(fiber1.get(), fiber2.get()); 244 Fiber::YieldTo(fiber1, *fiber2);
243 const u32 id = thread_ids.Get(); 245 const u32 id = thread_ids.Get();
244 value3 += 1; 246 value3 += 1;
245 Fiber::YieldTo(fiber1.get(), thread_fibers[id].get()); 247 Fiber::YieldTo(fiber1, *thread_fibers[id]);
246 } 248 }
247 249
248 void DoWork2() { 250 void DoWork2() {
249 value2 += 1; 251 value2 += 1;
250 const u32 id = thread_ids.Get(); 252 const u32 id = thread_ids.Get();
251 Fiber::YieldTo(fiber2.get(), thread_fibers[id].get()); 253 Fiber::YieldTo(fiber2, *thread_fibers[id]);
252 } 254 }
253 255
254 void ExecuteThread(u32 id); 256 void ExecuteThread(u32 id);
255 257
256 void CallFiber1() { 258 void CallFiber1() {
257 const u32 id = thread_ids.Get(); 259 const u32 id = thread_ids.Get();
258 Fiber::YieldTo(thread_fibers[id].get(), fiber1.get()); 260 Fiber::YieldTo(thread_fibers[id], *fiber1);
259 } 261 }
260 262
261 void Exit(); 263 void Exit();
@@ -264,7 +266,7 @@ public:
264 u32 value2{}; 266 u32 value2{};
265 u32 value3{}; 267 u32 value3{};
266 ThreadIds thread_ids; 268 ThreadIds thread_ids;
267 std::vector<std::unique_ptr<Common::Fiber>> thread_fibers; 269 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
268 std::shared_ptr<Common::Fiber> fiber1; 270 std::shared_ptr<Common::Fiber> fiber1;
269 std::shared_ptr<Common::Fiber> fiber2; 271 std::shared_ptr<Common::Fiber> fiber2;
270}; 272};
@@ -281,7 +283,8 @@ static void WorkControl3_2(void* control) {
281 283
282void TestControl3::ExecuteThread(u32 id) { 284void TestControl3::ExecuteThread(u32 id) {
283 thread_ids.Register(id); 285 thread_ids.Register(id);
284 thread_fibers[id] = Fiber::ThreadToFiber(); 286 auto thread_fiber = Fiber::ThreadToFiber();
287 thread_fibers[id] = thread_fiber;
285} 288}
286 289
287void TestControl3::Exit() { 290void TestControl3::Exit() {
@@ -329,7 +332,7 @@ public:
329 332
330 void Execute() { 333 void Execute() {
331 thread_fiber = Fiber::ThreadToFiber(); 334 thread_fiber = Fiber::ThreadToFiber();
332 Fiber::YieldTo(thread_fiber.get(), fiber1.get()); 335 Fiber::YieldTo(thread_fiber, *fiber1);
333 thread_fiber->Exit(); 336 thread_fiber->Exit();
334 } 337 }
335 338
@@ -337,7 +340,7 @@ public:
337 fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); 340 fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this);
338 if (rewinded) { 341 if (rewinded) {
339 goal_reached = true; 342 goal_reached = true;
340 Fiber::YieldTo(fiber1.get(), thread_fiber.get()); 343 Fiber::YieldTo(fiber1, *thread_fiber);
341 } 344 }
342 rewinded = true; 345 rewinded = true;
343 fiber1->Rewind(); 346 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 @@
9#include <deque> 9#include <deque>
10#include <memory> 10#include <memory>
11#include <mutex> 11#include <mutex>
12#include <numeric>
12#include <span> 13#include <span>
13#include <unordered_map> 14#include <unordered_map>
14#include <vector> 15#include <vector>
@@ -91,7 +92,7 @@ class BufferCache {
91 }; 92 };
92 93
93public: 94public:
94 static constexpr u32 SKIP_CACHE_SIZE = 4096; 95 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096;
95 96
96 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, 97 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
97 Tegra::Engines::Maxwell3D& maxwell3d_, 98 Tegra::Engines::Maxwell3D& maxwell3d_,
@@ -240,9 +241,9 @@ private:
240 template <bool insert> 241 template <bool insert>
241 void ChangeRegister(BufferId buffer_id); 242 void ChangeRegister(BufferId buffer_id);
242 243
243 void SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); 244 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
244 245
245 void SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); 246 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
246 247
247 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, 248 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
248 std::span<BufferCopy> copies); 249 std::span<BufferCopy> copies);
@@ -297,6 +298,11 @@ private:
297 298
298 std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{}; 299 std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{};
299 300
301 std::array<u32, 16> uniform_cache_hits{};
302 std::array<u32, 16> uniform_cache_shots{};
303
304 u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
305
300 bool has_deleted_buffers = false; 306 bool has_deleted_buffers = false;
301 307
302 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty> 308 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty>
@@ -328,6 +334,19 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
328 334
329template <class P> 335template <class P>
330void BufferCache<P>::TickFrame() { 336void BufferCache<P>::TickFrame() {
337 // Calculate hits and shots and move hit bits to the right
338 const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end());
339 const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end());
340 std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1,
341 uniform_cache_hits.begin() + 1);
342 std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1,
343 uniform_cache_shots.begin() + 1);
344 uniform_cache_hits[0] = 0;
345 uniform_cache_shots[0] = 0;
346
347 const bool skip_preferred = hits * 256 < shots * 251;
348 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
349
331 delayed_destruction_ring.Tick(); 350 delayed_destruction_ring.Tick();
332} 351}
333 352
@@ -671,7 +690,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
671 const VAddr cpu_addr = binding.cpu_addr; 690 const VAddr cpu_addr = binding.cpu_addr;
672 const u32 size = binding.size; 691 const u32 size = binding.size;
673 Buffer& buffer = slot_buffers[binding.buffer_id]; 692 Buffer& buffer = slot_buffers[binding.buffer_id];
674 if (size <= SKIP_CACHE_SIZE && !buffer.IsRegionGpuModified(cpu_addr, size)) { 693 if (size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size)) {
675 if constexpr (IS_OPENGL) { 694 if constexpr (IS_OPENGL) {
676 if (runtime.HasFastBufferSubData()) { 695 if (runtime.HasFastBufferSubData()) {
677 // Fast path for Nvidia 696 // Fast path for Nvidia
@@ -692,7 +711,12 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
692 return; 711 return;
693 } 712 }
694 // Classic cached path 713 // Classic cached path
695 SynchronizeBuffer(buffer, cpu_addr, size); 714 const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size);
715 if (sync_cached) {
716 ++uniform_cache_hits[0];
717 }
718 ++uniform_cache_shots[0];
719
696 if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) { 720 if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) {
697 // Skip binding if it's not needed and if the bound buffer is not the fast version 721 // Skip binding if it's not needed and if the bound buffer is not the fast version
698 // This exists to avoid instances where the fast buffer is bound and a GPU write happens 722 // 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) {
1106} 1130}
1107 1131
1108template <class P> 1132template <class P>
1109void BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { 1133bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
1110 if (buffer.CpuAddr() == 0) { 1134 if (buffer.CpuAddr() == 0) {
1111 return; 1135 return true;
1112 } 1136 }
1113 SynchronizeBufferImpl(buffer, cpu_addr, size); 1137 return SynchronizeBufferImpl(buffer, cpu_addr, size);
1114} 1138}
1115 1139
1116template <class P> 1140template <class P>
1117void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { 1141bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
1118 boost::container::small_vector<BufferCopy, 4> copies; 1142 boost::container::small_vector<BufferCopy, 4> copies;
1119 u64 total_size_bytes = 0; 1143 u64 total_size_bytes = 0;
1120 u64 largest_copy = 0; 1144 u64 largest_copy = 0;
@@ -1128,10 +1152,11 @@ void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
1128 largest_copy = std::max(largest_copy, range_size); 1152 largest_copy = std::max(largest_copy, range_size);
1129 }); 1153 });
1130 if (total_size_bytes == 0) { 1154 if (total_size_bytes == 0) {
1131 return; 1155 return true;
1132 } 1156 }
1133 const std::span<BufferCopy> copies_span(copies.data(), copies.size()); 1157 const std::span<BufferCopy> copies_span(copies.data(), copies.size());
1134 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); 1158 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
1159 return false;
1135} 1160}
1136 1161
1137template <class P> 1162template <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{
48 PixelFormat::A2B10G10R10_UINT, 48 PixelFormat::A2B10G10R10_UINT,
49}; 49};
50 50
51constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{
52 PixelFormat::R16G16_FLOAT, PixelFormat::B10G11R11_FLOAT, PixelFormat::R32_FLOAT,
53 PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
54 PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
55 PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
56 PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT,
57 PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT,
58};
59
51// TODO: How should we handle 24 bits? 60// TODO: How should we handle 24 bits?
52 61
53constexpr std::array VIEW_CLASS_16_BITS{ 62constexpr std::array VIEW_CLASS_16_BITS{
@@ -205,7 +214,6 @@ constexpr Table MakeViewTable() {
205 EnableRange(view, VIEW_CLASS_128_BITS); 214 EnableRange(view, VIEW_CLASS_128_BITS);
206 EnableRange(view, VIEW_CLASS_96_BITS); 215 EnableRange(view, VIEW_CLASS_96_BITS);
207 EnableRange(view, VIEW_CLASS_64_BITS); 216 EnableRange(view, VIEW_CLASS_64_BITS);
208 EnableRange(view, VIEW_CLASS_32_BITS);
209 EnableRange(view, VIEW_CLASS_16_BITS); 217 EnableRange(view, VIEW_CLASS_16_BITS);
210 EnableRange(view, VIEW_CLASS_8_BITS); 218 EnableRange(view, VIEW_CLASS_8_BITS);
211 EnableRange(view, VIEW_CLASS_RGTC1_RED); 219 EnableRange(view, VIEW_CLASS_RGTC1_RED);
@@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() {
231 EnableRange(copy, COPY_CLASS_64_BITS); 239 EnableRange(copy, COPY_CLASS_64_BITS);
232 return copy; 240 return copy;
233} 241}
242
243constexpr Table MakeNativeBgrViewTable() {
244 Table copy = MakeViewTable();
245 EnableRange(copy, VIEW_CLASS_32_BITS);
246 return copy;
247}
248
249constexpr Table MakeNonNativeBgrViewTable() {
250 Table copy = MakeViewTable();
251 EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR);
252 return copy;
253}
254
255constexpr Table MakeNativeBgrCopyTable() {
256 Table copy = MakeCopyTable();
257 EnableRange(copy, VIEW_CLASS_32_BITS);
258 return copy;
259}
260
261constexpr Table MakeNonNativeBgrCopyTable() {
262 Table copy = MakeCopyTable();
263 EnableRange(copy, VIEW_CLASS_32_BITS);
264 return copy;
265}
234} // Anonymous namespace 266} // Anonymous namespace
235 267
236bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) { 268bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
269 bool native_bgr) {
237 if (broken_views) { 270 if (broken_views) {
238 // If format views are broken, only accept formats that are identical. 271 // If format views are broken, only accept formats that are identical.
239 return format_a == format_b; 272 return format_a == format_b;
240 } 273 }
241 static constexpr Table TABLE = MakeViewTable(); 274 static constexpr Table BGR_TABLE = MakeNativeBgrViewTable();
242 return IsSupported(TABLE, format_a, format_b); 275 static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable();
276 return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
243} 277}
244 278
245bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) { 279bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) {
246 static constexpr Table TABLE = MakeCopyTable(); 280 static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable();
247 return IsSupported(TABLE, format_a, format_b); 281 static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable();
282 return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
248} 283}
249 284
250} // namespace VideoCore::Surface 285} // 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 @@
8 8
9namespace VideoCore::Surface { 9namespace VideoCore::Surface {
10 10
11bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views); 11bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
12 bool native_bgr);
12 13
13bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); 14bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr);
14 15
15} // namespace VideoCore::Surface 16} // 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
5 convert_float_to_depth.frag 5 convert_float_to_depth.frag
6 full_screen_triangle.vert 6 full_screen_triangle.vert
7 opengl_copy_bc4.comp 7 opengl_copy_bc4.comp
8 opengl_copy_bgra.comp
8 opengl_present.frag 9 opengl_present.frag
9 opengl_present.vert 10 opengl_present.vert
10 pitch_unswizzle.comp 11 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 430 core
6
7layout (local_size_x = 4, local_size_y = 4) in;
8
9layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input;
10layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output;
11
12void main() {
13 vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID));
14 imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra);
15}
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_)
73 for (auto& stage_uniforms : fast_uniforms) { 73 for (auto& stage_uniforms : fast_uniforms) {
74 for (OGLBuffer& buffer : stage_uniforms) { 74 for (OGLBuffer& buffer : stage_uniforms) {
75 buffer.Create(); 75 buffer.Create();
76 glNamedBufferData(buffer.handle, BufferCache::SKIP_CACHE_SIZE, nullptr, GL_STREAM_DRAW); 76 glNamedBufferData(buffer.handle, BufferCache::DEFAULT_SKIP_CACHE_SIZE, nullptr,
77 GL_STREAM_DRAW);
77 } 78 }
78 } 79 }
79 for (auto& stage_uniforms : copy_uniforms) { 80 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 = {{
96 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT 96 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT
97 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT 97 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT
98 {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM 98 {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM
99 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM 99 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
100 {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT 100 {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT
101 {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT 101 {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT
102 {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT 102 {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT
@@ -125,7 +125,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
125 {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM 125 {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM
126 {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM 126 {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM
127 {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM 127 {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM
128 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM 128 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_SRGB
129 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB 129 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB
130 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB 130 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB
131 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB 131 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
@@ -396,6 +396,17 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {
396 } 396 }
397} 397}
398 398
399[[nodiscard]] bool IsPixelFormatBGR(PixelFormat format) {
400 switch (format) {
401 case PixelFormat::B5G6R5_UNORM:
402 case PixelFormat::B8G8R8A8_UNORM:
403 case PixelFormat::B8G8R8A8_SRGB:
404 return true;
405 default:
406 return false;
407 }
408}
409
399} // Anonymous namespace 410} // Anonymous namespace
400 411
401ImageBufferMap::~ImageBufferMap() { 412ImageBufferMap::~ImageBufferMap() {
@@ -512,6 +523,9 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) {
512 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) { 523 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
513 return false; 524 return false;
514 } 525 }
526 if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
527 return false;
528 }
515 return true; 529 return true;
516} 530}
517 531
@@ -520,6 +534,8 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
520 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) { 534 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
521 ASSERT(src.info.type == ImageType::e3D); 535 ASSERT(src.info.type == ImageType::e3D);
522 util_shaders.CopyBC4(dst, src, copies); 536 util_shaders.CopyBC4(dst, src, copies);
537 } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
538 util_shaders.CopyBGR(dst, src, copies);
523 } else { 539 } else {
524 UNREACHABLE(); 540 UNREACHABLE();
525 } 541 }
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:
86 86
87 FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; 87 FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const;
88 88
89 bool HasNativeBgr() const noexcept {
90 // OpenGL does not have native support for the BGR internal format
91 return false;
92 }
93
89 bool HasBrokenTextureViewFormats() const noexcept { 94 bool HasBrokenTextureViewFormats() const noexcept {
90 return has_broken_texture_view_formats; 95 return has_broken_texture_view_formats;
91 } 96 }
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 @@
14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" 14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" 15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
16#include "video_core/host_shaders/opengl_copy_bc4_comp.h" 16#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
17#include "video_core/host_shaders/opengl_copy_bgra_comp.h"
17#include "video_core/host_shaders/pitch_unswizzle_comp.h" 18#include "video_core/host_shaders/pitch_unswizzle_comp.h"
18#include "video_core/renderer_opengl/gl_resource_manager.h" 19#include "video_core/renderer_opengl/gl_resource_manager.h"
19#include "video_core/renderer_opengl/gl_shader_manager.h" 20#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -48,6 +49,11 @@ OGLProgram MakeProgram(std::string_view source) {
48 return program; 49 return program;
49} 50}
50 51
52size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) {
53 return static_cast<size_t>(copy.extent.width * copy.extent.height *
54 copy.src_subresource.num_layers);
55}
56
51} // Anonymous namespace 57} // Anonymous namespace
52 58
53UtilShaders::UtilShaders(ProgramManager& program_manager_) 59UtilShaders::UtilShaders(ProgramManager& program_manager_)
@@ -55,6 +61,7 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
55 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), 61 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
56 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), 62 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
57 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), 63 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
64 copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)),
58 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { 65 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
59 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); 66 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
60 swizzle_table_buffer.Create(); 67 swizzle_table_buffer.Create();
@@ -205,6 +212,43 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
205 program_manager.RestoreGuestCompute(); 212 program_manager.RestoreGuestCompute();
206} 213}
207 214
215void UtilShaders::CopyBGR(Image& dst_image, Image& src_image,
216 std::span<const VideoCommon::ImageCopy> copies) {
217 static constexpr GLuint BINDING_INPUT_IMAGE = 0;
218 static constexpr GLuint BINDING_OUTPUT_IMAGE = 1;
219 static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0};
220 const u32 bytes_per_block = BytesPerBlock(dst_image.info.format);
221 switch (bytes_per_block) {
222 case 2:
223 // BGR565 copy
224 for (const ImageCopy& copy : copies) {
225 ASSERT(copy.src_offset == zero_offset);
226 ASSERT(copy.dst_offset == zero_offset);
227 bgr_copy_pass.Execute(dst_image, src_image, copy);
228 }
229 break;
230 case 4: {
231 // BGRA8 copy
232 program_manager.BindHostCompute(copy_bgra_program.handle);
233 constexpr GLenum FORMAT = GL_RGBA8;
234 for (const ImageCopy& copy : copies) {
235 ASSERT(copy.src_offset == zero_offset);
236 ASSERT(copy.dst_offset == zero_offset);
237 glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(),
238 copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT);
239 glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(),
240 copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT);
241 glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth);
242 }
243 program_manager.RestoreGuestCompute();
244 break;
245 }
246 default:
247 UNREACHABLE();
248 break;
249 }
250}
251
208GLenum StoreFormat(u32 bytes_per_block) { 252GLenum StoreFormat(u32 bytes_per_block) {
209 switch (bytes_per_block) { 253 switch (bytes_per_block) {
210 case 1: 254 case 1:
@@ -222,4 +266,36 @@ GLenum StoreFormat(u32 bytes_per_block) {
222 return GL_R8UI; 266 return GL_R8UI;
223} 267}
224 268
269void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image,
270 const ImageCopy& copy) {
271 if (CopyBufferCreationNeeded(copy)) {
272 CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565);
273 }
274 // Copy from source to PBO
275 glPixelStorei(GL_PACK_ALIGNMENT, 1);
276 glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width);
277 glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle);
278 glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
279 copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
280 static_cast<GLsizei>(bgr16_pbo_size), nullptr);
281
282 // Copy from PBO to destination in reverse order
283 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
284 glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width);
285 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle);
286 glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
287 copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV,
288 nullptr);
289}
290
291bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) {
292 return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16);
293}
294
295void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) {
296 bgr16_pbo.Create();
297 bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16);
298 glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY);
299}
300
225} // namespace OpenGL 301} // 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;
19 19
20struct ImageBufferMap; 20struct ImageBufferMap;
21 21
22class Bgr565CopyPass {
23public:
24 Bgr565CopyPass() = default;
25 ~Bgr565CopyPass() = default;
26
27 void Execute(const Image& dst_image, const Image& src_image,
28 const VideoCommon::ImageCopy& copy);
29
30private:
31 [[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy);
32 void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format);
33
34 OGLBuffer bgr16_pbo;
35 size_t bgr16_pbo_size{};
36};
37
22class UtilShaders { 38class UtilShaders {
23public: 39public:
24 explicit UtilShaders(ProgramManager& program_manager); 40 explicit UtilShaders(ProgramManager& program_manager);
@@ -36,6 +52,9 @@ public:
36 void CopyBC4(Image& dst_image, Image& src_image, 52 void CopyBC4(Image& dst_image, Image& src_image,
37 std::span<const VideoCommon::ImageCopy> copies); 53 std::span<const VideoCommon::ImageCopy> copies);
38 54
55 void CopyBGR(Image& dst_image, Image& src_image,
56 std::span<const VideoCommon::ImageCopy> copies);
57
39private: 58private:
40 ProgramManager& program_manager; 59 ProgramManager& program_manager;
41 60
@@ -44,7 +63,10 @@ private:
44 OGLProgram block_linear_unswizzle_2d_program; 63 OGLProgram block_linear_unswizzle_2d_program;
45 OGLProgram block_linear_unswizzle_3d_program; 64 OGLProgram block_linear_unswizzle_3d_program;
46 OGLProgram pitch_unswizzle_program; 65 OGLProgram pitch_unswizzle_program;
66 OGLProgram copy_bgra_program;
47 OGLProgram copy_bc4_program; 67 OGLProgram copy_bc4_program;
68
69 Bgr565CopyPass bgr_copy_pass;
48}; 70};
49 71
50GLenum StoreFormat(u32 bytes_per_block); 72GLenum 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 {
93 // No known Vulkan driver has broken image views 93 // No known Vulkan driver has broken image views
94 return false; 94 return false;
95 } 95 }
96
97 bool HasNativeBgr() const noexcept {
98 // All known Vulkan drivers can natively handle BGR textures
99 return true;
100 }
96}; 101};
97 102
98class Image : public VideoCommon::ImageBase { 103class 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
120 if (lhs.info.type == ImageType::Linear) { 120 if (lhs.info.type == ImageType::Linear) {
121 base = SubresourceBase{.level = 0, .layer = 0}; 121 base = SubresourceBase{.level = 0, .layer = 0};
122 } else { 122 } else {
123 // We are passing relaxed formats as an option, having broken views or not won't matter 123 // We are passing relaxed formats as an option, having broken views/bgr or not won't matter
124 static constexpr bool broken_views = false; 124 static constexpr bool broken_views = false;
125 base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views); 125 static constexpr bool native_bgr = true;
126 base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);
126 } 127 }
127 if (!base) { 128 if (!base) {
128 LOG_ERROR(HW_GPU, "Image alias should have been flipped"); 129 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
24 .height = std::max(image_info.size.height >> range.base.level, 1u), 24 .height = std::max(image_info.size.height >> range.base.level, 1u),
25 .depth = std::max(image_info.size.depth >> range.base.level, 1u), 25 .depth = std::max(image_info.size.depth >> range.base.level, 1u),
26 } { 26 } {
27 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false), 27 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
28 "Image view format {} is incompatible with image format {}", info.format, 28 "Image view format {} is incompatible with image format {}", info.format,
29 image_info.format); 29 image_info.format);
30 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 30 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,
876 return ImageId{}; 876 return ImageId{};
877 } 877 }
878 const bool broken_views = runtime.HasBrokenTextureViewFormats(); 878 const bool broken_views = runtime.HasBrokenTextureViewFormats();
879 const bool native_bgr = runtime.HasNativeBgr();
879 ImageId image_id; 880 ImageId image_id;
880 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 881 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
881 if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { 882 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,
885 if (existing_image.gpu_addr == gpu_addr && existing.type == info.type && 886 if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
886 existing.pitch == info.pitch && 887 existing.pitch == info.pitch &&
887 IsPitchLinearSameSize(existing, info, strict_size) && 888 IsPitchLinearSameSize(existing, info, strict_size) &&
888 IsViewCompatible(existing.format, info.format, broken_views)) { 889 IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
889 image_id = existing_image_id; 890 image_id = existing_image_id;
890 return true; 891 return true;
891 } 892 }
892 } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) { 893 } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
894 native_bgr)) {
893 image_id = existing_image_id; 895 image_id = existing_image_id;
894 return true; 896 return true;
895 } 897 }
@@ -920,6 +922,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
920 ImageInfo new_info = info; 922 ImageInfo new_info = info;
921 const size_t size_bytes = CalculateGuestSizeInBytes(new_info); 923 const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
922 const bool broken_views = runtime.HasBrokenTextureViewFormats(); 924 const bool broken_views = runtime.HasBrokenTextureViewFormats();
925 const bool native_bgr = runtime.HasNativeBgr();
923 std::vector<ImageId> overlap_ids; 926 std::vector<ImageId> overlap_ids;
924 std::vector<ImageId> left_aliased_ids; 927 std::vector<ImageId> left_aliased_ids;
925 std::vector<ImageId> right_aliased_ids; 928 std::vector<ImageId> right_aliased_ids;
@@ -935,8 +938,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
935 return; 938 return;
936 } 939 }
937 static constexpr bool strict_size = true; 940 static constexpr bool strict_size = true;
938 const std::optional<OverlapResult> solution = 941 const std::optional<OverlapResult> solution = ResolveOverlap(
939 ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views); 942 new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);
940 if (solution) { 943 if (solution) {
941 gpu_addr = solution->gpu_addr; 944 gpu_addr = solution->gpu_addr;
942 cpu_addr = solution->cpu_addr; 945 cpu_addr = solution->cpu_addr;
@@ -946,10 +949,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
946 } 949 }
947 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; 950 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
948 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); 951 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
949 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) { 952 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
950 left_aliased_ids.push_back(overlap_id); 953 left_aliased_ids.push_back(overlap_id);
951 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, 954 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
952 broken_views)) { 955 broken_views, native_bgr)) {
953 right_aliased_ids.push_back(overlap_id); 956 right_aliased_ids.push_back(overlap_id);
954 } 957 }
955 }); 958 });
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
1035 1035
1036std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, 1036std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr,
1037 VAddr cpu_addr, const ImageBase& overlap, 1037 VAddr cpu_addr, const ImageBase& overlap,
1038 bool strict_size, bool broken_views) { 1038 bool strict_size, bool broken_views, bool native_bgr) {
1039 ASSERT(new_info.type != ImageType::Linear); 1039 ASSERT(new_info.type != ImageType::Linear);
1040 ASSERT(overlap.info.type != ImageType::Linear); 1040 ASSERT(overlap.info.type != ImageType::Linear);
1041 if (!IsLayerStrideCompatible(new_info, overlap.info)) { 1041 if (!IsLayerStrideCompatible(new_info, overlap.info)) {
1042 return std::nullopt; 1042 return std::nullopt;
1043 } 1043 }
1044 if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) { 1044 if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views, native_bgr)) {
1045 return std::nullopt; 1045 return std::nullopt;
1046 } 1046 }
1047 if (gpu_addr == overlap.gpu_addr) { 1047 if (gpu_addr == overlap.gpu_addr) {
@@ -1085,14 +1085,14 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) {
1085 1085
1086std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image, 1086std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image,
1087 GPUVAddr candidate_addr, RelaxedOptions options, 1087 GPUVAddr candidate_addr, RelaxedOptions options,
1088 bool broken_views) { 1088 bool broken_views, bool native_bgr) {
1089 const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr); 1089 const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);
1090 if (!base) { 1090 if (!base) {
1091 return std::nullopt; 1091 return std::nullopt;
1092 } 1092 }
1093 const ImageInfo& existing = image.info; 1093 const ImageInfo& existing = image.info;
1094 if (False(options & RelaxedOptions::Format)) { 1094 if (False(options & RelaxedOptions::Format)) {
1095 if (!IsViewCompatible(existing.format, candidate.format, broken_views)) { 1095 if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) {
1096 return std::nullopt; 1096 return std::nullopt;
1097 } 1097 }
1098 } 1098 }
@@ -1129,8 +1129,9 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
1129} 1129}
1130 1130
1131bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, 1131bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr,
1132 RelaxedOptions options, bool broken_views) { 1132 RelaxedOptions options, bool broken_views, bool native_bgr) {
1133 return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value(); 1133 return FindSubresource(candidate, image, candidate_addr, options, broken_views, native_bgr)
1134 .has_value();
1134} 1135}
1135 1136
1136void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, 1137void 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
87[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, 87[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
88 GPUVAddr gpu_addr, VAddr cpu_addr, 88 GPUVAddr gpu_addr, VAddr cpu_addr,
89 const ImageBase& overlap, 89 const ImageBase& overlap,
90 bool strict_size, bool broken_views); 90 bool strict_size, bool broken_views,
91 bool native_bgr);
91 92
92[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); 93[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs);
93 94
@@ -95,11 +96,11 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
95 const ImageBase& image, 96 const ImageBase& image,
96 GPUVAddr candidate_addr, 97 GPUVAddr candidate_addr,
97 RelaxedOptions options, 98 RelaxedOptions options,
98 bool broken_views); 99 bool broken_views, bool native_bgr);
99 100
100[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, 101[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image,
101 GPUVAddr candidate_addr, RelaxedOptions options, 102 GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views,
102 bool broken_views); 103 bool native_bgr);
103 104
104void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, 105void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
105 const ImageBase* src); 106 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) {
387 } 387 }
388} 388}
389 389
390MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) {
391 switch (button) {
392 case Qt::LeftButton:
393 return MouseInput::MouseButton::Left;
394 case Qt::RightButton:
395 return MouseInput::MouseButton::Right;
396 case Qt::MiddleButton:
397 return MouseInput::MouseButton::Wheel;
398 case Qt::BackButton:
399 return MouseInput::MouseButton::Backward;
400 case Qt::ForwardButton:
401 return MouseInput::MouseButton::Forward;
402 case Qt::TaskButton:
403 return MouseInput::MouseButton::Task;
404 default:
405 return MouseInput::MouseButton::Extra;
406 }
407}
408
390void GRenderWindow::mousePressEvent(QMouseEvent* event) { 409void GRenderWindow::mousePressEvent(QMouseEvent* event) {
391 // Touch input is handled in TouchBeginEvent 410 // Touch input is handled in TouchBeginEvent
392 if (event->source() == Qt::MouseEventSynthesizedBySystem) { 411 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
@@ -395,7 +414,8 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
395 414
396 auto pos = event->pos(); 415 auto pos = event->pos();
397 const auto [x, y] = ScaleTouch(pos); 416 const auto [x, y] = ScaleTouch(pos);
398 input_subsystem->GetMouse()->PressButton(x, y, event->button()); 417 const auto button = QtButtonToMouseButton(event->button());
418 input_subsystem->GetMouse()->PressButton(x, y, button);
399 419
400 if (event->button() == Qt::LeftButton) { 420 if (event->button() == Qt::LeftButton) {
401 this->TouchPressed(x, y, 0); 421 this->TouchPressed(x, y, 0);
@@ -429,7 +449,8 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
429 return; 449 return;
430 } 450 }
431 451
432 input_subsystem->GetMouse()->ReleaseButton(event->button()); 452 const auto button = QtButtonToMouseButton(event->button());
453 input_subsystem->GetMouse()->ReleaseButton(button);
433 454
434 if (event->button() == Qt::LeftButton) { 455 if (event->button() == Qt::LeftButton) {
435 this->TouchReleased(0); 456 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 {
28class InputSubsystem; 28class InputSubsystem;
29} 29}
30 30
31namespace MouseInput {
32enum class MouseButton;
33}
34
31namespace VideoCore { 35namespace VideoCore {
32enum class LoadCallbackStage; 36enum class LoadCallbackStage;
33} 37}
@@ -149,6 +153,9 @@ public:
149 void keyPressEvent(QKeyEvent* event) override; 153 void keyPressEvent(QKeyEvent* event) override;
150 void keyReleaseEvent(QKeyEvent* event) override; 154 void keyReleaseEvent(QKeyEvent* event) override;
151 155
156 /// Converts a Qt mouse button into MouseInput mouse button
157 static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
158
152 void mousePressEvent(QMouseEvent* event) override; 159 void mousePressEvent(QMouseEvent* event) override;
153 void mouseMoveEvent(QMouseEvent* event) override; 160 void mouseMoveEvent(QMouseEvent* event) override;
154 void mouseReleaseEvent(QMouseEvent* event) override; 161 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{{
235 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, 235 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
236 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, 236 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
237 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, 237 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
238 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}}, 238 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
239 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, 239 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
240 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, 240 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
241}}; 241}};
@@ -508,7 +508,7 @@ void Config::ReadControlValues() {
508 508
509 Settings::values.emulate_analog_keyboard = 509 Settings::values.emulate_analog_keyboard =
510 ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); 510 ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool();
511 Settings::values.mouse_panning = ReadSetting(QStringLiteral("mouse_panning"), false).toBool(); 511 Settings::values.mouse_panning = false;
512 Settings::values.mouse_panning_sensitivity = 512 Settings::values.mouse_panning_sensitivity =
513 ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat(); 513 ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat();
514 514
@@ -1182,7 +1182,6 @@ void Config::SaveControlValues() {
1182 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); 1182 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
1183 WriteSetting(QStringLiteral("emulate_analog_keyboard"), 1183 WriteSetting(QStringLiteral("emulate_analog_keyboard"),
1184 Settings::values.emulate_analog_keyboard, false); 1184 Settings::values.emulate_analog_keyboard, false);
1185 WriteSetting(QStringLiteral("mouse_panning"), Settings::values.mouse_panning, false);
1186 WriteSetting(QStringLiteral("mouse_panning_sensitivity"), 1185 WriteSetting(QStringLiteral("mouse_panning_sensitivity"),
1187 Settings::values.mouse_panning_sensitivity, 1.0f); 1186 Settings::values.mouse_panning_sensitivity, 1.0f);
1188 qt_config->endGroup(); 1187 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 @@
21#include "input_common/mouse/mouse_poller.h" 21#include "input_common/mouse/mouse_poller.h"
22#include "input_common/udp/udp.h" 22#include "input_common/udp/udp.h"
23#include "ui_configure_input_player.h" 23#include "ui_configure_input_player.h"
24#include "yuzu/bootmanager.h"
24#include "yuzu/configuration/config.h" 25#include "yuzu/configuration/config.h"
25#include "yuzu/configuration/configure_input_player.h" 26#include "yuzu/configuration/configure_input_player.h"
26#include "yuzu/configuration/configure_input_player_widget.h" 27#include "yuzu/configuration/configure_input_player_widget.h"
@@ -1362,7 +1363,8 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1362 return; 1363 return;
1363 } 1364 }
1364 1365
1365 input_subsystem->GetMouse()->PressButton(0, 0, event->button()); 1366 const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
1367 input_subsystem->GetMouse()->PressButton(0, 0, button);
1366} 1368}
1367 1369
1368void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1370void 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() {
854 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this), 854 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),
855 &QShortcut::activated, this, [&] { 855 &QShortcut::activated, this, [&] {
856 Settings::values.mouse_panning = !Settings::values.mouse_panning; 856 Settings::values.mouse_panning = !Settings::values.mouse_panning;
857 if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { 857 if (Settings::values.mouse_panning) {
858 mouse_hide_timer.start();
859 render_window->installEventFilter(render_window); 858 render_window->installEventFilter(render_window);
860 render_window->setAttribute(Qt::WA_Hover, true); 859 render_window->setAttribute(Qt::WA_Hover, true);
861 } 860 }
@@ -1208,11 +1207,14 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1208 renderer_status_button->setDisabled(true); 1207 renderer_status_button->setDisabled(true);
1209 1208
1210 if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { 1209 if (UISettings::values.hide_mouse || Settings::values.mouse_panning) {
1211 mouse_hide_timer.start();
1212 render_window->installEventFilter(render_window); 1210 render_window->installEventFilter(render_window);
1213 render_window->setAttribute(Qt::WA_Hover, true); 1211 render_window->setAttribute(Qt::WA_Hover, true);
1214 } 1212 }
1215 1213
1214 if (UISettings::values.hide_mouse) {
1215 mouse_hide_timer.start();
1216 }
1217
1216 std::string title_name; 1218 std::string title_name;
1217 std::string title_version; 1219 std::string title_version;
1218 const auto res = system.GetGameName(title_name); 1220 const auto res = system.GetGameName(title_name);
@@ -2372,12 +2374,15 @@ void GMainWindow::OnConfigure() {
2372 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { 2374 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
2373 render_window->installEventFilter(render_window); 2375 render_window->installEventFilter(render_window);
2374 render_window->setAttribute(Qt::WA_Hover, true); 2376 render_window->setAttribute(Qt::WA_Hover, true);
2375 mouse_hide_timer.start();
2376 } else { 2377 } else {
2377 render_window->removeEventFilter(render_window); 2378 render_window->removeEventFilter(render_window);
2378 render_window->setAttribute(Qt::WA_Hover, false); 2379 render_window->setAttribute(Qt::WA_Hover, false);
2379 } 2380 }
2380 2381
2382 if (UISettings::values.hide_mouse) {
2383 mouse_hide_timer.start();
2384 }
2385
2381 UpdateStatusButtons(); 2386 UpdateStatusButtons();
2382} 2387}
2383 2388
@@ -2615,8 +2620,7 @@ void GMainWindow::UpdateUISettings() {
2615} 2620}
2616 2621
2617void GMainWindow::HideMouseCursor() { 2622void GMainWindow::HideMouseCursor() {
2618 if (emu_thread == nullptr || 2623 if (emu_thread == nullptr && UISettings::values.hide_mouse) {
2619 (!UISettings::values.hide_mouse && !Settings::values.mouse_panning)) {
2620 mouse_hide_timer.stop(); 2624 mouse_hide_timer.stop();
2621 ShowMouseCursor(); 2625 ShowMouseCursor();
2622 return; 2626 return;
@@ -2626,8 +2630,7 @@ void GMainWindow::HideMouseCursor() {
2626 2630
2627void GMainWindow::ShowMouseCursor() { 2631void GMainWindow::ShowMouseCursor() {
2628 render_window->unsetCursor(); 2632 render_window->unsetCursor();
2629 if (emu_thread != nullptr && 2633 if (emu_thread != nullptr && UISettings::values.hide_mouse) {
2630 (UISettings::values.hide_mouse || Settings::values.mouse_panning)) {
2631 mouse_hide_timer.start(); 2634 mouse_hide_timer.start();
2632 } 2635 }
2633} 2636}
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) {
35 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0); 35 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);
36} 36}
37 37
38MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
39 switch (button) {
40 case SDL_BUTTON_LEFT:
41 return MouseInput::MouseButton::Left;
42 case SDL_BUTTON_RIGHT:
43 return MouseInput::MouseButton::Right;
44 case SDL_BUTTON_MIDDLE:
45 return MouseInput::MouseButton::Wheel;
46 case SDL_BUTTON_X1:
47 return MouseInput::MouseButton::Backward;
48 case SDL_BUTTON_X2:
49 return MouseInput::MouseButton::Forward;
50 default:
51 return MouseInput::MouseButton::Undefined;
52 }
53}
54
38void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 55void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
56 const auto mouse_button = SDLButtonToMouseButton(button);
39 if (button == SDL_BUTTON_LEFT) { 57 if (button == SDL_BUTTON_LEFT) {
40 if (state == SDL_PRESSED) { 58 if (state == SDL_PRESSED) {
41 TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); 59 TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
42 } else { 60 } else {
43 TouchReleased(0); 61 TouchReleased(0);
44 } 62 }
45 } else if (button == SDL_BUTTON_RIGHT) { 63 } else {
46 if (state == SDL_PRESSED) { 64 if (state == SDL_PRESSED) {
47 input_subsystem->GetMouse()->PressButton(x, y, button); 65 input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
48 } else { 66 } else {
49 input_subsystem->GetMouse()->ReleaseButton(button); 67 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
50 } 68 }
51 } 69 }
52} 70}
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 {
18class InputSubsystem; 18class InputSubsystem;
19} 19}
20 20
21namespace MouseInput {
22enum class MouseButton;
23}
24
21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
22public: 26public:
23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); 27 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
@@ -42,6 +46,9 @@ protected:
42 /// Called by WaitEvent when the mouse moves. 46 /// Called by WaitEvent when the mouse moves.
43 void OnMouseMotion(s32 x, s32 y); 47 void OnMouseMotion(s32 x, s32 y);
44 48
49 /// Converts a SDL mouse button into MouseInput mouse button
50 MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const;
51
45 /// Called by WaitEvent when a mouse button is pressed or released 52 /// Called by WaitEvent when a mouse button is pressed or released
46 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 53 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
47 54