summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/polyfill_thread.h119
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp2
-rw-r--r--src/core/hle/kernel/k_memory_layout.h6
-rw-r--r--src/core/hle/kernel/k_thread.cpp21
-rw-r--r--src/core/hle/kernel/k_thread.h24
-rw-r--r--src/core/hle/kernel/kernel.cpp39
-rw-r--r--src/input_common/drivers/gc_adapter.cpp4
-rw-r--r--src/input_common/drivers/joycon.cpp7
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp210
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h18
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp18
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h23
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp8
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h52
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp13
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp37
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp49
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp6
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp14
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h5
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc7
-rw-r--r--src/shader_recompiler/frontend/ir/type.h31
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp3
-rw-r--r--src/shader_recompiler/frontend/ir/value.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp8
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp25
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/fsr.cpp148
-rw-r--r--src/video_core/fsr.h19
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt30
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr.frag108
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag9
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag9
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.cpp101
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.h43
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp90
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp144
-rw-r--r--src/video_core/texture_cache/image_info.cpp4
-rw-r--r--src/video_core/texture_cache/samples_helper.h44
-rw-r--r--src/yuzu/configuration/configure_graphics.ui2
-rw-r--r--src/yuzu/configuration/input_profiles.cpp7
-rw-r--r--src/yuzu/main.cpp9
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp21
-rw-r--r--src/yuzu/multiplayer/direct_connect.ui23
-rw-r--r--src/yuzu/multiplayer/validation.h25
55 files changed, 1102 insertions, 518 deletions
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index 5a8d1ce08..b4c94a5fc 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -11,6 +11,8 @@
11 11
12#ifdef __cpp_lib_jthread 12#ifdef __cpp_lib_jthread
13 13
14#include <chrono>
15#include <condition_variable>
14#include <stop_token> 16#include <stop_token>
15#include <thread> 17#include <thread>
16 18
@@ -21,23 +23,36 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
21 cv.wait(lock, token, std::move(pred)); 23 cv.wait(lock, token, std::move(pred));
22} 24}
23 25
26template <typename Rep, typename Period>
27bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
28 std::condition_variable_any cv;
29 std::mutex m;
30
31 // Perform the timed wait.
32 std::unique_lock lk{m};
33 return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); });
34}
35
24} // namespace Common 36} // namespace Common
25 37
26#else 38#else
27 39
28#include <atomic> 40#include <atomic>
41#include <chrono>
42#include <condition_variable>
29#include <functional> 43#include <functional>
30#include <list> 44#include <map>
31#include <memory> 45#include <memory>
32#include <mutex> 46#include <mutex>
33#include <optional> 47#include <optional>
34#include <thread> 48#include <thread>
35#include <type_traits> 49#include <type_traits>
50#include <utility>
36 51
37namespace std { 52namespace std {
38namespace polyfill { 53namespace polyfill {
39 54
40using stop_state_callbacks = list<function<void()>>; 55using stop_state_callback = size_t;
41 56
42class stop_state { 57class stop_state {
43public: 58public:
@@ -45,61 +60,69 @@ public:
45 ~stop_state() = default; 60 ~stop_state() = default;
46 61
47 bool request_stop() { 62 bool request_stop() {
48 stop_state_callbacks callbacks; 63 unique_lock lk{m_lock};
49 64
50 { 65 if (m_stop_requested) {
51 scoped_lock lk{m_lock}; 66 // Already set, nothing to do.
67 return false;
68 }
52 69
53 if (m_stop_requested.load()) { 70 // Mark stop requested.
54 // Already set, nothing to do 71 m_stop_requested = true;
55 return false;
56 }
57 72
58 // Set as requested 73 while (!m_callbacks.empty()) {
59 m_stop_requested = true; 74 // Get an iterator to the first element.
75 const auto it = m_callbacks.begin();
60 76
61 // Copy callback list 77 // Move the callback function out of the map.
62 callbacks = m_callbacks; 78 function<void()> f;
63 } 79 swap(it->second, f);
80
81 // Erase the now-empty map element.
82 m_callbacks.erase(it);
64 83
65 for (auto callback : callbacks) { 84 // Run the callback.
66 callback(); 85 if (f) {
86 f();
87 }
67 } 88 }
68 89
69 return true; 90 return true;
70 } 91 }
71 92
72 bool stop_requested() const { 93 bool stop_requested() const {
73 return m_stop_requested.load(); 94 unique_lock lk{m_lock};
95 return m_stop_requested;
74 } 96 }
75 97
76 stop_state_callbacks::const_iterator insert_callback(function<void()> f) { 98 stop_state_callback insert_callback(function<void()> f) {
77 stop_state_callbacks::const_iterator ret{}; 99 unique_lock lk{m_lock};
78 bool should_run{};
79
80 {
81 scoped_lock lk{m_lock};
82 should_run = m_stop_requested.load();
83 m_callbacks.push_front(f);
84 ret = m_callbacks.begin();
85 }
86 100
87 if (should_run) { 101 if (m_stop_requested) {
88 f(); 102 // Stop already requested. Don't insert anything,
103 // just run the callback synchronously.
104 if (f) {
105 f();
106 }
107 return 0;
89 } 108 }
90 109
110 // Insert the callback.
111 stop_state_callback ret = ++m_next_callback;
112 m_callbacks.emplace(ret, move(f));
91 return ret; 113 return ret;
92 } 114 }
93 115
94 void remove_callback(stop_state_callbacks::const_iterator it) { 116 void remove_callback(stop_state_callback cb) {
95 scoped_lock lk{m_lock}; 117 unique_lock lk{m_lock};
96 m_callbacks.erase(it); 118 m_callbacks.erase(cb);
97 } 119 }
98 120
99private: 121private:
100 mutex m_lock; 122 mutable recursive_mutex m_lock;
101 atomic<bool> m_stop_requested; 123 map<stop_state_callback, function<void()>> m_callbacks;
102 stop_state_callbacks m_callbacks; 124 stop_state_callback m_next_callback{0};
125 bool m_stop_requested{false};
103}; 126};
104 127
105} // namespace polyfill 128} // namespace polyfill
@@ -209,7 +232,7 @@ public:
209 } 232 }
210 ~stop_callback() { 233 ~stop_callback() {
211 if (m_stop_state && m_callback) { 234 if (m_stop_state && m_callback) {
212 m_stop_state->remove_callback(*m_callback); 235 m_stop_state->remove_callback(m_callback);
213 } 236 }
214 } 237 }
215 238
@@ -220,7 +243,7 @@ public:
220 243
221private: 244private:
222 shared_ptr<polyfill::stop_state> m_stop_state; 245 shared_ptr<polyfill::stop_state> m_stop_state;
223 optional<polyfill::stop_state_callbacks::const_iterator> m_callback; 246 polyfill::stop_state_callback m_callback;
224}; 247};
225 248
226template <typename Callback> 249template <typename Callback>
@@ -318,6 +341,28 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
318 cv.wait(lock, [&] { return pred() || token.stop_requested(); }); 341 cv.wait(lock, [&] { return pred() || token.stop_requested(); });
319} 342}
320 343
344template <typename Rep, typename Period>
345bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
346 if (token.stop_requested()) {
347 return false;
348 }
349
350 bool stop_requested = false;
351 std::condition_variable cv;
352 std::mutex m;
353
354 std::stop_callback cb(token, [&] {
355 // Wake up the waiting thread.
356 std::unique_lock lk{m};
357 stop_requested = true;
358 cv.notify_one();
359 });
360
361 // Perform the timed wait.
362 std::unique_lock lk{m};
363 return !cv.wait_for(lk, rel_time, [&] { return stop_requested; });
364}
365
321} // namespace Common 366} // namespace Common
322 367
323#endif 368#endif
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 124149697..0c6b20db3 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -171,7 +171,7 @@ Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value)
171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); 171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
172 172
173 // Update the lock. 173 // Update the lock.
174 cur_thread->SetAddressKey(addr, value); 174 cur_thread->SetUserAddressKey(addr, value);
175 owner_thread->AddWaiter(cur_thread); 175 owner_thread->AddWaiter(cur_thread);
176 176
177 // Begin waiting. 177 // Begin waiting.
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 43185320d..d791acbe3 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -68,7 +68,7 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
68 68
69 // Add the current thread as a waiter on the owner. 69 // Add the current thread as a waiter on the owner.
70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL); 70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
71 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); 71 cur_thread->SetKernelAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
72 owner_thread->AddWaiter(cur_thread); 72 owner_thread->AddWaiter(cur_thread);
73 73
74 // Begin waiting to hold the lock. 74 // Begin waiting to hold the lock.
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index fd6e1d3e6..17fa1a6ed 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -67,9 +67,9 @@ constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + 67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
68 KernelSlabHeapSize + KernelPageBufferHeapSize; 68 KernelSlabHeapSize + KernelPageBufferHeapSize;
69 69
70constexpr bool IsKernelAddressKey(VAddr key) { 70//! NB: Use KThread::GetAddressKeyIsKernel().
71 return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; 71//! See explanation for deviation of GetAddressKey.
72} 72bool IsKernelAddressKey(VAddr key) = delete;
73 73
74constexpr bool IsKernelAddress(VAddr address) { 74constexpr bool IsKernelAddress(VAddr address) {
75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; 75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 21207fe99..84ff3c64b 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -330,7 +330,7 @@ void KThread::Finalize() {
330 KThread* const waiter = std::addressof(*it); 330 KThread* const waiter = std::addressof(*it);
331 331
332 // The thread shouldn't be a kernel waiter. 332 // The thread shouldn't be a kernel waiter.
333 ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); 333 ASSERT(!waiter->GetAddressKeyIsKernel());
334 334
335 // Clear the lock owner. 335 // Clear the lock owner.
336 waiter->SetLockOwner(nullptr); 336 waiter->SetLockOwner(nullptr);
@@ -763,19 +763,6 @@ void KThread::Continue() {
763 KScheduler::OnThreadStateChanged(kernel, this, old_state); 763 KScheduler::OnThreadStateChanged(kernel, this, old_state);
764} 764}
765 765
766void KThread::WaitUntilSuspended() {
767 // Make sure we have a suspend requested.
768 ASSERT(IsSuspendRequested());
769
770 // Loop until the thread is not executing on any core.
771 for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
772 KThread* core_thread{};
773 do {
774 core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
775 } while (core_thread == this);
776 }
777}
778
779Result KThread::SetActivity(Svc::ThreadActivity activity) { 766Result KThread::SetActivity(Svc::ThreadActivity activity) {
780 // Lock ourselves. 767 // Lock ourselves.
781 KScopedLightLock lk(activity_pause_lock); 768 KScopedLightLock lk(activity_pause_lock);
@@ -897,7 +884,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
897 } 884 }
898 885
899 // Keep track of how many kernel waiters we have. 886 // Keep track of how many kernel waiters we have.
900 if (IsKernelAddressKey(thread->GetAddressKey())) { 887 if (thread->GetAddressKeyIsKernel()) {
901 ASSERT((num_kernel_waiters++) >= 0); 888 ASSERT((num_kernel_waiters++) >= 0);
902 KScheduler::SetSchedulerUpdateNeeded(kernel); 889 KScheduler::SetSchedulerUpdateNeeded(kernel);
903 } 890 }
@@ -911,7 +898,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
911 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 898 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
912 899
913 // Keep track of how many kernel waiters we have. 900 // Keep track of how many kernel waiters we have.
914 if (IsKernelAddressKey(thread->GetAddressKey())) { 901 if (thread->GetAddressKeyIsKernel()) {
915 ASSERT((num_kernel_waiters--) > 0); 902 ASSERT((num_kernel_waiters--) > 0);
916 KScheduler::SetSchedulerUpdateNeeded(kernel); 903 KScheduler::SetSchedulerUpdateNeeded(kernel);
917 } 904 }
@@ -987,7 +974,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
987 KThread* thread = std::addressof(*it); 974 KThread* thread = std::addressof(*it);
988 975
989 // Keep track of how many kernel waiters we have. 976 // Keep track of how many kernel waiters we have.
990 if (IsKernelAddressKey(thread->GetAddressKey())) { 977 if (thread->GetAddressKeyIsKernel()) {
991 ASSERT((num_kernel_waiters--) > 0); 978 ASSERT((num_kernel_waiters--) > 0);
992 KScheduler::SetSchedulerUpdateNeeded(kernel); 979 KScheduler::SetSchedulerUpdateNeeded(kernel);
993 } 980 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 7cd94a340..9d771de0e 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -214,8 +214,6 @@ public:
214 214
215 void Continue(); 215 void Continue();
216 216
217 void WaitUntilSuspended();
218
219 constexpr void SetSyncedIndex(s32 index) { 217 constexpr void SetSyncedIndex(s32 index) {
220 synced_index = index; 218 synced_index = index;
221 } 219 }
@@ -607,13 +605,30 @@ public:
607 return address_key_value; 605 return address_key_value;
608 } 606 }
609 607
610 void SetAddressKey(VAddr key) { 608 [[nodiscard]] bool GetAddressKeyIsKernel() const {
609 return address_key_is_kernel;
610 }
611
612 //! NB: intentional deviation from official kernel.
613 //
614 // Separate SetAddressKey into user and kernel versions
615 // to cope with arbitrary host pointers making their way
616 // into things.
617
618 void SetUserAddressKey(VAddr key) {
611 address_key = key; 619 address_key = key;
620 address_key_is_kernel = false;
612 } 621 }
613 622
614 void SetAddressKey(VAddr key, u32 val) { 623 void SetUserAddressKey(VAddr key, u32 val) {
615 address_key = key; 624 address_key = key;
616 address_key_value = val; 625 address_key_value = val;
626 address_key_is_kernel = false;
627 }
628
629 void SetKernelAddressKey(VAddr key) {
630 address_key = key;
631 address_key_is_kernel = true;
617 } 632 }
618 633
619 void ClearWaitQueue() { 634 void ClearWaitQueue() {
@@ -772,6 +787,7 @@ private:
772 bool debug_attached{}; 787 bool debug_attached{};
773 s8 priority_inheritance_count{}; 788 s8 priority_inheritance_count{};
774 bool resource_limit_release_hint{}; 789 bool resource_limit_release_hint{};
790 bool address_key_is_kernel{};
775 StackParameters stack_parameters{}; 791 StackParameters stack_parameters{};
776 Common::SpinLock context_guard{}; 792 Common::SpinLock context_guard{};
777 793
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1fb25f221..d9eafe261 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1198,28 +1198,35 @@ void KernelCore::Suspend(bool suspended) {
1198 const bool should_suspend{exception_exited || suspended}; 1198 const bool should_suspend{exception_exited || suspended};
1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; 1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
1200 1200
1201 std::vector<KScopedAutoObject<KThread>> process_threads; 1201 //! This refers to the application process, not the current process.
1202 { 1202 KScopedAutoObject<KProcess> process = CurrentProcess();
1203 KScopedSchedulerLock sl{*this}; 1203 if (process.IsNull()) {
1204 return;
1205 }
1204 1206
1205 if (auto* process = CurrentProcess(); process != nullptr) { 1207 // Set the new activity.
1206 process->SetActivity(activity); 1208 process->SetActivity(activity);
1207 1209
1208 if (!should_suspend) { 1210 // Wait for process execution to stop.
1209 // Runnable now; no need to wait. 1211 bool must_wait{should_suspend};
1210 return; 1212
1211 } 1213 // KernelCore::Suspend must be called from locked context, or we
1214 // could race another call to SetActivity, interfering with waiting.
1215 while (must_wait) {
1216 KScopedSchedulerLock sl{*this};
1217
1218 // Assume that all threads have finished running.
1219 must_wait = false;
1212 1220
1213 for (auto* thread : process->GetThreadList()) { 1221 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1214 process_threads.emplace_back(thread); 1222 if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
1223 process.GetPointerUnsafe()) {
1224 // A thread has not finished running yet.
1225 // Continue waiting.
1226 must_wait = true;
1215 } 1227 }
1216 } 1228 }
1217 } 1229 }
1218
1219 // Wait for execution to stop.
1220 for (auto& thread : process_threads) {
1221 thread->WaitUntilSuspended();
1222 }
1223} 1230}
1224 1231
1225void KernelCore::ShutdownCores() { 1232void KernelCore::ShutdownCores() {
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index ecb3e9dc2..d09ff178b 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/param_package.h" 8#include "common/param_package.h"
9#include "common/polyfill_thread.h"
9#include "common/settings_input.h" 10#include "common/settings_input.h"
10#include "common/thread.h" 11#include "common/thread.h"
11#include "input_common/drivers/gc_adapter.h" 12#include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
217 Common::SetCurrentThreadName("ScanGCAdapter"); 218 Common::SetCurrentThreadName("ScanGCAdapter");
218 usb_adapter_handle = nullptr; 219 usb_adapter_handle = nullptr;
219 pads = {}; 220 pads = {};
220 while (!stop_token.stop_requested() && !Setup()) { 221 while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
221 std::this_thread::sleep_for(std::chrono::seconds(2));
222 } 222 }
223} 223}
224 224
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 40cda400d..cedc94e63 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/param_package.h" 6#include "common/param_package.h"
7#include "common/polyfill_ranges.h" 7#include "common/polyfill_ranges.h"
8#include "common/polyfill_thread.h"
8#include "common/settings.h" 9#include "common/settings.h"
9#include "common/thread.h" 10#include "common/thread.h"
10#include "input_common/drivers/joycon.h" 11#include "input_common/drivers/joycon.h"
@@ -67,7 +68,8 @@ void Joycons::Setup() {
67void Joycons::ScanThread(std::stop_token stop_token) { 68void Joycons::ScanThread(std::stop_token stop_token) {
68 constexpr u16 nintendo_vendor_id = 0x057e; 69 constexpr u16 nintendo_vendor_id = 0x057e;
69 Common::SetCurrentThreadName("JoyconScanThread"); 70 Common::SetCurrentThreadName("JoyconScanThread");
70 while (!stop_token.stop_requested()) { 71
72 do {
71 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0); 73 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
72 SDL_hid_device_info* cur_dev = devs; 74 SDL_hid_device_info* cur_dev = devs;
73 75
@@ -81,8 +83,7 @@ void Joycons::ScanThread(std::stop_token stop_token) {
81 } 83 }
82 84
83 SDL_hid_free_enumeration(devs); 85 SDL_hid_free_enumeration(devs);
84 std::this_thread::sleep_for(std::chrono::seconds(5)); 86 } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
85 }
86} 87}
87 88
88bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { 89bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
index f6e7e97d5..d8f040f75 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.cpp
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -13,33 +13,33 @@ CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
13 13
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { 14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
16 std::vector<u8> buffer;
17 DriverResult result{DriverResult::Success}; 16 DriverResult result{DriverResult::Success};
17 JoystickLeftSpiCalibration spi_calibration{};
18 bool has_user_calibration = false;
18 calibration = {}; 19 calibration = {};
19 20
20 result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer);
21
22 if (result == DriverResult::Success) { 21 if (result == DriverResult::Success) {
23 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 22 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
24 if (has_user_calibration) {
25 result = ReadSPI(CalAddr::USER_LEFT_DATA, 9, buffer);
26 } else {
27 result = ReadSPI(CalAddr::FACT_LEFT_DATA, 9, buffer);
28 }
29 } 23 }
30 24
31 if (result == DriverResult::Success) { 25 // Read User defined calibration
32 calibration.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); 26 if (result == DriverResult::Success && has_user_calibration) {
33 calibration.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); 27 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
34 calibration.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]);
35 calibration.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4));
36 calibration.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]);
37 calibration.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4));
38 } 28 }
39 29
40 // Nintendo fix for drifting stick 30 // Read Factory calibration
41 // result = ReadSPI(0x60, 0x86 ,buffer, 16); 31 if (result == DriverResult::Success && !has_user_calibration) {
42 // calibration.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); 32 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
33 }
34
35 if (result == DriverResult::Success) {
36 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
37 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
38 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
39 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
40 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
41 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
42 }
43 43
44 // Set a valid default calibration if data is missing 44 // Set a valid default calibration if data is missing
45 ValidateCalibration(calibration); 45 ValidateCalibration(calibration);
@@ -49,33 +49,33 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration
49 49
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { 50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this); 51 ScopedSetBlocking sb(this);
52 std::vector<u8> buffer;
53 DriverResult result{DriverResult::Success}; 52 DriverResult result{DriverResult::Success};
53 JoystickRightSpiCalibration spi_calibration{};
54 bool has_user_calibration = false;
54 calibration = {}; 55 calibration = {};
55 56
56 result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer);
57
58 if (result == DriverResult::Success) { 57 if (result == DriverResult::Success) {
59 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 58 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
60 if (has_user_calibration) {
61 result = ReadSPI(CalAddr::USER_RIGHT_DATA, 9, buffer);
62 } else {
63 result = ReadSPI(CalAddr::FACT_RIGHT_DATA, 9, buffer);
64 }
65 } 59 }
66 60
67 if (result == DriverResult::Success) { 61 // Read User defined calibration
68 calibration.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); 62 if (result == DriverResult::Success && has_user_calibration) {
69 calibration.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); 63 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
70 calibration.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); 64 }
71 calibration.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); 65
72 calibration.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); 66 // Read Factory calibration
73 calibration.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); 67 if (result == DriverResult::Success && !has_user_calibration) {
68 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
74 } 69 }
75 70
76 // Nintendo fix for drifting stick 71 if (result == DriverResult::Success) {
77 // buffer = ReadSPI(0x60, 0x98 , 16); 72 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
78 // joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); 73 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
74 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
75 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
76 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
77 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
78 }
79 79
80 // Set a valid default calibration if data is missing 80 // Set a valid default calibration if data is missing
81 ValidateCalibration(calibration); 81 ValidateCalibration(calibration);
@@ -85,39 +85,41 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio
85 85
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { 86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this); 87 ScopedSetBlocking sb(this);
88 std::vector<u8> buffer;
89 DriverResult result{DriverResult::Success}; 88 DriverResult result{DriverResult::Success};
89 ImuSpiCalibration spi_calibration{};
90 bool has_user_calibration = false;
90 calibration = {}; 91 calibration = {};
91 92
92 result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer);
93
94 if (result == DriverResult::Success) { 93 if (result == DriverResult::Success) {
95 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 94 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
96 if (has_user_calibration) { 95 }
97 result = ReadSPI(CalAddr::USER_IMU_DATA, sizeof(IMUCalibration), buffer); 96
98 } else { 97 // Read User defined calibration
99 result = ReadSPI(CalAddr::FACT_IMU_DATA, sizeof(IMUCalibration), buffer); 98 if (result == DriverResult::Success && has_user_calibration) {
100 } 99 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
100 }
101
102 // Read Factory calibration
103 if (result == DriverResult::Success && !has_user_calibration) {
104 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
101 } 105 }
102 106
103 if (result == DriverResult::Success) { 107 if (result == DriverResult::Success) {
104 IMUCalibration device_calibration{}; 108 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
105 memcpy(&device_calibration, buffer.data(), sizeof(IMUCalibration)); 109 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
106 calibration.accelerometer[0].offset = device_calibration.accelerometer_offset[0]; 110 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
107 calibration.accelerometer[1].offset = device_calibration.accelerometer_offset[1];
108 calibration.accelerometer[2].offset = device_calibration.accelerometer_offset[2];
109 111
110 calibration.accelerometer[0].scale = device_calibration.accelerometer_scale[0]; 112 calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0];
111 calibration.accelerometer[1].scale = device_calibration.accelerometer_scale[1]; 113 calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1];
112 calibration.accelerometer[2].scale = device_calibration.accelerometer_scale[2]; 114 calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2];
113 115
114 calibration.gyro[0].offset = device_calibration.gyroscope_offset[0]; 116 calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0];
115 calibration.gyro[1].offset = device_calibration.gyroscope_offset[1]; 117 calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1];
116 calibration.gyro[2].offset = device_calibration.gyroscope_offset[2]; 118 calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2];
117 119
118 calibration.gyro[0].scale = device_calibration.gyroscope_scale[0]; 120 calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0];
119 calibration.gyro[1].scale = device_calibration.gyroscope_scale[1]; 121 calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1];
120 calibration.gyro[2].scale = device_calibration.gyroscope_scale[2]; 122 calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2];
121 } 123 }
122 124
123 ValidateCalibration(calibration); 125 ValidateCalibration(calibration);
@@ -127,10 +129,12 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati
127 129
128DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, 130DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
129 s16 current_value) { 131 s16 current_value) {
132 constexpr s16 DefaultRingRange{800};
133
130 // TODO: Get default calibration form ring itself 134 // TODO: Get default calibration form ring itself
131 if (ring_data_max == 0 && ring_data_min == 0) { 135 if (ring_data_max == 0 && ring_data_min == 0) {
132 ring_data_max = current_value + 800; 136 ring_data_max = current_value + DefaultRingRange;
133 ring_data_min = current_value - 800; 137 ring_data_min = current_value - DefaultRingRange;
134 ring_data_default = current_value; 138 ring_data_default = current_value;
135 } 139 }
136 ring_data_max = std::max(ring_data_max, current_value); 140 ring_data_max = std::max(ring_data_max, current_value);
@@ -143,42 +147,72 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio
143 return DriverResult::Success; 147 return DriverResult::Success;
144} 148}
145 149
150DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
151 bool& has_user_calibration) {
152 MagicSpiCalibration spi_magic{};
153 const DriverResult result{ReadSPI(address, spi_magic)};
154 has_user_calibration = false;
155 if (result == DriverResult::Success) {
156 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
157 spi_magic.second == CalibrationMagic::USR_MAGIC_1;
158 }
159 return result;
160}
161
162u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const {
163 return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]);
164}
165
166u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const {
167 return static_cast<u16>((block[2] << 4) | (block[1] >> 4));
168}
169
146void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) { 170void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) {
147 constexpr u16 DefaultStickCenter{2048}; 171 constexpr u16 DefaultStickCenter{0x800};
148 constexpr u16 DefaultStickRange{1740}; 172 constexpr u16 DefaultStickRange{0x6cc};
149 173
150 if (calibration.x.center == 0xFFF || calibration.x.center == 0) { 174 calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter);
151 calibration.x.center = DefaultStickCenter; 175 calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange);
152 } 176 calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange);
153 if (calibration.x.max == 0xFFF || calibration.x.max == 0) { 177
154 calibration.x.max = DefaultStickRange; 178 calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter);
179 calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange);
180 calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange);
181}
182
183void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) {
184 constexpr s16 DefaultAccelerometerScale{0x4000};
185 constexpr s16 DefaultGyroScale{0x3be7};
186 constexpr s16 DefaultOffset{0};
187
188 for (auto& sensor : calibration.accelerometer) {
189 sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale);
190 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
155 } 191 }
156 if (calibration.x.min == 0xFFF || calibration.x.min == 0) { 192 for (auto& sensor : calibration.gyro) {
157 calibration.x.min = DefaultStickRange; 193 sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale);
194 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
158 } 195 }
196}
159 197
160 if (calibration.y.center == 0xFFF || calibration.y.center == 0) { 198u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const {
161 calibration.y.center = DefaultStickCenter; 199 if (value == 0) {
162 } 200 return default_value;
163 if (calibration.y.max == 0xFFF || calibration.y.max == 0) {
164 calibration.y.max = DefaultStickRange;
165 } 201 }
166 if (calibration.y.min == 0xFFF || calibration.y.min == 0) { 202 if (value == 0xFFF) {
167 calibration.y.min = DefaultStickRange; 203 return default_value;
168 } 204 }
205 return value;
169} 206}
170 207
171void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { 208s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const {
172 for (auto& sensor : calibration.accelerometer) { 209 if (value == 0) {
173 if (sensor.scale == 0) { 210 return default_value;
174 sensor.scale = 0x4000;
175 }
176 } 211 }
177 for (auto& sensor : calibration.gyro) { 212 if (value == 0xFFF) {
178 if (sensor.scale == 0) { 213 return default_value;
179 sensor.scale = 0x3be7;
180 }
181 } 214 }
215 return value;
182} 216}
183 217
184} // namespace InputCommon::Joycon 218} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
index afb52a36a..c6fd0f729 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.h
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -53,9 +53,27 @@ public:
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); 53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54 54
55private: 55private:
56 /// Returns true if the specified address corresponds to the magic value of user calibration
57 DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
58
59 /// Converts a raw calibration block to an u16 value containing the x axis value
60 u16 GetXAxisCalibrationValue(std::span<u8> block) const;
61
62 /// Converts a raw calibration block to an u16 value containing the y axis value
63 u16 GetYAxisCalibrationValue(std::span<u8> block) const;
64
65 /// Ensures that all joystick calibration values are set
56 void ValidateCalibration(JoyStickCalibration& calibration); 66 void ValidateCalibration(JoyStickCalibration& calibration);
67
68 /// Ensures that all motion calibration values are set
57 void ValidateCalibration(MotionCalibration& calibration); 69 void ValidateCalibration(MotionCalibration& calibration);
58 70
71 /// Returns the default value if the value is either zero or 0xFFF
72 u16 ValidateValue(u16 value, u16 default_value) const;
73
74 /// Returns the default value if the value is either zero or 0xFFF
75 s16 ValidateValue(s16 value, s16 default_value) const;
76
59 s16 ring_data_max = 0; 77 s16 ring_data_max = 0;
60 s16 ring_data_default = 0; 78 s16 ring_data_default = 0;
61 s16 ring_data_min = 0; 79 s16 ring_data_min = 0;
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 417d0dcc5..0ef240344 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -22,8 +22,8 @@ void JoyconCommonProtocol::SetNonBlocking() {
22} 22}
23 23
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { 24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 std::vector<u8> buffer; 25 std::array<u8, 1> buffer{};
26 const auto result = ReadSPI(CalAddr::DEVICE_TYPE, 1, buffer); 26 const auto result = ReadRawSPI(SpiAddress::DEVICE_TYPE, buffer);
27 controller_type = ControllerType::None; 27 controller_type = ControllerType::None;
28 28
29 if (result == DriverResult::Success) { 29 if (result == DriverResult::Success) {
@@ -148,11 +148,13 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
148 return SendData(local_buffer); 148 return SendData(local_buffer);
149} 149}
150 150
151DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) { 151DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
152 constexpr std::size_t HeaderSize = 20;
152 constexpr std::size_t MaxTries = 10; 153 constexpr std::size_t MaxTries = 10;
154 const auto size = output.size();
153 std::size_t tries = 0; 155 std::size_t tries = 0;
154 std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size}; 156 std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, static_cast<u8>(size)};
155 std::vector<u8> local_buffer(size + 20); 157 std::vector<u8> local_buffer{};
156 158
157 buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); 159 buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF);
158 buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8); 160 buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8);
@@ -167,8 +169,12 @@ DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8
167 } 169 }
168 } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); 170 } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]);
169 171
172 if (local_buffer.size() < size + HeaderSize) {
173 return DriverResult::WrongReply;
174 }
175
170 // Remove header from output 176 // Remove header from output
171 output = std::vector<u8>(local_buffer.begin() + 20, local_buffer.begin() + 20 + size); 177 memcpy(output.data(), local_buffer.data() + HeaderSize, size);
172 return DriverResult::Success; 178 return DriverResult::Success;
173} 179}
174 180
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
index 903bcf402..75d3f20a4 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.h
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -97,10 +97,29 @@ public:
97 /** 97 /**
98 * Reads the SPI memory stored on the joycon 98 * Reads the SPI memory stored on the joycon
99 * @param Initial address location 99 * @param Initial address location
100 * @param size in bytes to be read
101 * @returns output buffer containing the responce 100 * @returns output buffer containing the responce
102 */ 101 */
103 DriverResult ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output); 102 DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
103
104 /**
105 * Reads the SPI memory stored on the joycon
106 * @param Initial address location
107 * @returns output object containing the responce
108 */
109 template <typename Output>
110 requires std::is_trivially_copyable_v<Output> DriverResult ReadSPI(SpiAddress addr,
111 Output& output) {
112 std::array<u8, sizeof(Output)> buffer;
113 output = {};
114
115 const auto result = ReadRawSPI(addr, buffer);
116 if (result != DriverResult::Success) {
117 return result;
118 }
119
120 std::memcpy(&output, buffer.data(), sizeof(Output));
121 return DriverResult::Success;
122 }
104 123
105 /** 124 /**
106 * Enables MCU chip on the joycon 125 * Enables MCU chip on the joycon
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
index 63cfb1369..484c208e6 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -71,8 +71,8 @@ DriverResult GenericProtocol::GetBattery(u32& battery_level) {
71 71
72DriverResult GenericProtocol::GetColor(Color& color) { 72DriverResult GenericProtocol::GetColor(Color& color) {
73 ScopedSetBlocking sb(this); 73 ScopedSetBlocking sb(this);
74 std::vector<u8> buffer; 74 std::array<u8, 12> buffer{};
75 const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); 75 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
76 76
77 color = {}; 77 color = {};
78 if (result == DriverResult::Success) { 78 if (result == DriverResult::Success) {
@@ -87,8 +87,8 @@ DriverResult GenericProtocol::GetColor(Color& color) {
87 87
88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { 88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
89 ScopedSetBlocking sb(this); 89 ScopedSetBlocking sb(this);
90 std::vector<u8> buffer; 90 std::array<u8, 16> buffer{};
91 const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); 91 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
92 92
93 serial_number = {}; 93 serial_number = {};
94 if (result == DriverResult::Success) { 94 if (result == DriverResult::Success) {
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 182d2c15b..14b07bfb5 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -159,13 +159,12 @@ enum class UsbSubCommand : u8 {
159 SEND_UART = 0x92, 159 SEND_UART = 0x92,
160}; 160};
161 161
162enum class CalMagic : u8 { 162enum class CalibrationMagic : u8 {
163 USR_MAGIC_0 = 0xB2, 163 USR_MAGIC_0 = 0xB2,
164 USR_MAGIC_1 = 0xA1, 164 USR_MAGIC_1 = 0xA1,
165 USRR_MAGI_SIZE = 2,
166}; 165};
167 166
168enum class CalAddr { 167enum class SpiAddress {
169 SERIAL_NUMBER = 0X6000, 168 SERIAL_NUMBER = 0X6000,
170 DEVICE_TYPE = 0X6012, 169 DEVICE_TYPE = 0X6012,
171 COLOR_EXIST = 0X601B, 170 COLOR_EXIST = 0X601B,
@@ -396,10 +395,35 @@ struct MotionData {
396 u64 delta_timestamp{}; 395 u64 delta_timestamp{};
397}; 396};
398 397
398// Output from SPI read command containing user calibration magic
399struct MagicSpiCalibration {
400 CalibrationMagic first;
401 CalibrationMagic second;
402};
403static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size");
404
405// Output from SPI read command containing left joystick calibration
406struct JoystickLeftSpiCalibration {
407 std::array<u8, 3> max;
408 std::array<u8, 3> center;
409 std::array<u8, 3> min;
410};
411static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9,
412 "JoystickLeftSpiCalibration is an invalid size");
413
414// Output from SPI read command containing right joystick calibration
415struct JoystickRightSpiCalibration {
416 std::array<u8, 3> center;
417 std::array<u8, 3> min;
418 std::array<u8, 3> max;
419};
420static_assert(sizeof(JoystickRightSpiCalibration) == 0x9,
421 "JoystickRightSpiCalibration is an invalid size");
422
399struct JoyStickAxisCalibration { 423struct JoyStickAxisCalibration {
400 u16 max{1}; 424 u16 max;
401 u16 min{1}; 425 u16 min;
402 u16 center{0}; 426 u16 center;
403}; 427};
404 428
405struct JoyStickCalibration { 429struct JoyStickCalibration {
@@ -407,6 +431,14 @@ struct JoyStickCalibration {
407 JoyStickAxisCalibration y; 431 JoyStickAxisCalibration y;
408}; 432};
409 433
434struct ImuSpiCalibration {
435 std::array<s16, 3> accelerometer_offset;
436 std::array<s16, 3> accelerometer_scale;
437 std::array<s16, 3> gyroscope_offset;
438 std::array<s16, 3> gyroscope_scale;
439};
440static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size");
441
410struct RingCalibration { 442struct RingCalibration {
411 s16 default_value; 443 s16 default_value;
412 s16 max_value; 444 s16 max_value;
@@ -488,14 +520,6 @@ struct InputReportNfcIr {
488static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size"); 520static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
489#pragma pack(pop) 521#pragma pack(pop)
490 522
491struct IMUCalibration {
492 std::array<s16, 3> accelerometer_offset;
493 std::array<s16, 3> accelerometer_scale;
494 std::array<s16, 3> gyroscope_offset;
495 std::array<s16, 3> gyroscope_scale;
496};
497static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size");
498
499struct NFCReadBlock { 523struct NFCReadBlock {
500 u8 start; 524 u8 start;
501 u8 end; 525 u8 end;
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 0cb1e193e..fd4a61a4d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -279,6 +279,8 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
279 header += "OPTION NV_internal;" 279 header += "OPTION NV_internal;"
280 "OPTION NV_shader_storage_buffer;" 280 "OPTION NV_shader_storage_buffer;"
281 "OPTION NV_gpu_program_fp64;"; 281 "OPTION NV_gpu_program_fp64;";
282 // TODO: Enable only when MS is used
283 header += "OPTION NV_texture_multisample;";
282 if (info.uses_int64_bit_atomics) { 284 if (info.uses_int64_bit_atomics) {
283 header += "OPTION NV_shader_atomic_int64;"; 285 header += "OPTION NV_shader_atomic_int64;";
284 } 286 }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
index 5bfdecc09..2fc2a0ac6 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
@@ -43,10 +43,6 @@ void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
43 Alias(inst, value); 43 Alias(inst, value);
44} 44}
45 45
46void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
47 Alias(inst, value);
48}
49
50void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) { 46void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
51 Alias(inst, value); 47 Alias(inst, value);
52} 48}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index e67e80fac..b7bc11416 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -59,7 +59,7 @@ std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
59 } 59 }
60} 60}
61 61
62std::string_view TextureType(IR::TextureInstInfo info) { 62std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) {
63 if (info.is_depth) { 63 if (info.is_depth) {
64 switch (info.type) { 64 switch (info.type) {
65 case TextureType::Color1D: 65 case TextureType::Color1D:
@@ -88,9 +88,9 @@ std::string_view TextureType(IR::TextureInstInfo info) {
88 return "ARRAY1D"; 88 return "ARRAY1D";
89 case TextureType::Color2D: 89 case TextureType::Color2D:
90 case TextureType::Color2DRect: 90 case TextureType::Color2DRect:
91 return "2D"; 91 return is_ms ? "2DMS" : "2D";
92 case TextureType::ColorArray2D: 92 case TextureType::ColorArray2D:
93 return "ARRAY2D"; 93 return is_ms ? "ARRAY2DMS" : "ARRAY2D";
94 case TextureType::Color3D: 94 case TextureType::Color3D:
95 return "3D"; 95 return "3D";
96 case TextureType::ColorCube: 96 case TextureType::ColorCube:
@@ -510,15 +510,16 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
510 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) { 510 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
511 const auto info{inst.Flags<IR::TextureInstInfo>()}; 511 const auto info{inst.Flags<IR::TextureInstInfo>()};
512 const auto sparse_inst{PrepareSparse(inst)}; 512 const auto sparse_inst{PrepareSparse(inst)};
513 const bool is_multisample{ms.type != Type::Void};
513 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""}; 514 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
514 const std::string_view type{TextureType(info)}; 515 const std::string_view type{TextureType(info, is_multisample)};
515 const std::string texture{Texture(ctx, info, index)}; 516 const std::string texture{Texture(ctx, info, index)};
516 const std::string offset_vec{Offset(ctx, offset)}; 517 const std::string offset_vec{Offset(ctx, offset)};
517 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)}; 518 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
518 const Register ret{ctx.reg_alloc.Define(inst)}; 519 const Register ret{ctx.reg_alloc.Define(inst)};
519 if (info.type == TextureType::Buffer) { 520 if (info.type == TextureType::Buffer) {
520 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec); 521 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
521 } else if (ms.type != Type::Void) { 522 } else if (is_multisample) {
522 ctx.Add("MOV.S {}.w,{};" 523 ctx.Add("MOV.S {}.w,{};"
523 "TXFMS.F{} {},{},{},{}{};", 524 "TXFMS.F{} {},{},{},{}{};",
524 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec); 525 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
@@ -531,7 +532,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
531} 532}
532 533
533void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 534void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
534 ScalarS32 lod) { 535 ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) {
535 const auto info{inst.Flags<IR::TextureInstInfo>()}; 536 const auto info{inst.Flags<IR::TextureInstInfo>()};
536 const std::string texture{Texture(ctx, info, index)}; 537 const std::string texture{Texture(ctx, info, index)};
537 const std::string_view type{TextureType(info)}; 538 const std::string_view type{TextureType(info)};
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index eaaf9ba39..1a1ea61d5 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -197,7 +197,6 @@ void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Regist
197void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 197void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
198void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 198void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
199void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 199void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
200void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
201void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 200void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
202void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 201void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
203void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 202void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
@@ -582,7 +581,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
582void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 581void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
583 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms); 582 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
584void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 583void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
585 ScalarS32 lod); 584 ScalarS32 lod, const IR::Value& skip_mips);
586void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord); 585void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
587void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 586void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
588 const IR::Value& coord, const IR::Value& derivatives, 587 const IR::Value& coord, const IR::Value& derivatives,
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
index 8e5e6cf1f..1be4a0f59 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -48,10 +48,6 @@ void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value)
48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); 48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
49} 49}
50 50
51void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
52 ctx.AddF32("{}=ftoi({});", inst, value);
53}
54
55void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { 51void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
56 NotImplemented(); 52 NotImplemented();
57} 53}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index cecdbb9d6..4be2c25ec 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -414,7 +414,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
414 414
415void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 415void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
416 std::string_view coords, std::string_view offset, std::string_view lod, 416 std::string_view coords, std::string_view offset, std::string_view lod,
417 [[maybe_unused]] std::string_view ms) { 417 std::string_view ms) {
418 const auto info{inst.Flags<IR::TextureInstInfo>()}; 418 const auto info{inst.Flags<IR::TextureInstInfo>()};
419 if (info.has_bias) { 419 if (info.has_bias) {
420 throw NotImplementedException("EmitImageFetch Bias texture samples"); 420 throw NotImplementedException("EmitImageFetch Bias texture samples");
@@ -431,19 +431,24 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
431 ctx.AddU1("{}=true;", *sparse_inst); 431 ctx.AddU1("{}=true;", *sparse_inst);
432 } 432 }
433 if (!sparse_inst || !supports_sparse) { 433 if (!sparse_inst || !supports_sparse) {
434 if (!offset.empty()) { 434 const auto int_coords{CoordsCastToInt(coords, info)};
435 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, 435 if (!ms.empty()) {
436 CoordsCastToInt(coords, info), lod, CoordsCastToInt(offset, info)); 436 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, ms);
437 } else if (!offset.empty()) {
438 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, int_coords, lod,
439 CoordsCastToInt(offset, info));
437 } else { 440 } else {
438 if (info.type == TextureType::Buffer) { 441 if (info.type == TextureType::Buffer) {
439 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords); 442 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
440 } else { 443 } else {
441 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, 444 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, lod);
442 CoordsCastToInt(coords, info), lod);
443 } 445 }
444 } 446 }
445 return; 447 return;
446 } 448 }
449 if (!ms.empty()) {
450 throw NotImplementedException("EmitImageFetch Sparse MSAA samples");
451 }
447 if (!offset.empty()) { 452 if (!offset.empty()) {
448 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));", 453 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
449 *sparse_inst, texture, CastToIntVec(coords, info), lod, 454 *sparse_inst, texture, CastToIntVec(coords, info), lod,
@@ -455,27 +460,27 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
455} 460}
456 461
457void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 462void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
458 std::string_view lod) { 463 std::string_view lod, const IR::Value& skip_mips_val) {
459 const auto info{inst.Flags<IR::TextureInstInfo>()}; 464 const auto info{inst.Flags<IR::TextureInstInfo>()};
460 const auto texture{Texture(ctx, info, index)}; 465 const auto texture{Texture(ctx, info, index)};
466 const bool skip_mips{skip_mips_val.U1()};
467 const auto mips{
468 [&] { return skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture); }};
461 switch (info.type) { 469 switch (info.type) {
462 case TextureType::Color1D: 470 case TextureType::Color1D:
463 return ctx.AddU32x4( 471 return ctx.AddU32x4("{}=uvec4(uint(textureSize({},int({}))),0u,0u,{});", inst, texture, lod,
464 "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst, 472 mips());
465 texture, lod, texture);
466 case TextureType::ColorArray1D: 473 case TextureType::ColorArray1D:
467 case TextureType::Color2D: 474 case TextureType::Color2D:
468 case TextureType::ColorCube: 475 case TextureType::ColorCube:
469 case TextureType::Color2DRect: 476 case TextureType::Color2DRect:
470 return ctx.AddU32x4( 477 return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({},int({}))),0u,{});", inst, texture, lod,
471 "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst, 478 mips());
472 texture, lod, texture);
473 case TextureType::ColorArray2D: 479 case TextureType::ColorArray2D:
474 case TextureType::Color3D: 480 case TextureType::Color3D:
475 case TextureType::ColorArrayCube: 481 case TextureType::ColorArrayCube:
476 return ctx.AddU32x4( 482 return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({},int({}))),{});", inst, texture, lod,
477 "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture, 483 mips());
478 lod, texture);
479 case TextureType::Buffer: 484 case TextureType::Buffer:
480 throw NotImplementedException("EmitImageQueryDimensions Texture buffers"); 485 throw NotImplementedException("EmitImageQueryDimensions Texture buffers");
481 } 486 }
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index 4151c89de..8d0a65047 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -231,7 +231,6 @@ void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
231void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); 231void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
232void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 232void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
233void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 233void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
235void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); 234void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
236void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 235void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
237void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 236void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
@@ -655,7 +654,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
655 std::string_view coords, std::string_view offset, std::string_view lod, 654 std::string_view coords, std::string_view offset, std::string_view lod,
656 std::string_view ms); 655 std::string_view ms);
657void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 656void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
658 std::string_view lod); 657 std::string_view lod, const IR::Value& skip_mips);
659void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 658void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
660 std::string_view coords); 659 std::string_view coords);
661void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 660void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 5d01ec0cd..1b006e811 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -61,24 +61,28 @@ std::string OutputDecorator(Stage stage, u32 size) {
61 } 61 }
62} 62}
63 63
64std::string_view SamplerType(TextureType type, bool is_depth) { 64std::string_view DepthSamplerType(TextureType type) {
65 if (is_depth) { 65 switch (type) {
66 switch (type) { 66 case TextureType::Color1D:
67 case TextureType::Color1D: 67 return "sampler1DShadow";
68 return "sampler1DShadow"; 68 case TextureType::ColorArray1D:
69 case TextureType::ColorArray1D: 69 return "sampler1DArrayShadow";
70 return "sampler1DArrayShadow"; 70 case TextureType::Color2D:
71 case TextureType::Color2D: 71 return "sampler2DShadow";
72 return "sampler2DShadow"; 72 case TextureType::ColorArray2D:
73 case TextureType::ColorArray2D: 73 return "sampler2DArrayShadow";
74 return "sampler2DArrayShadow"; 74 case TextureType::ColorCube:
75 case TextureType::ColorCube: 75 return "samplerCubeShadow";
76 return "samplerCubeShadow"; 76 case TextureType::ColorArrayCube:
77 case TextureType::ColorArrayCube: 77 return "samplerCubeArrayShadow";
78 return "samplerCubeArrayShadow"; 78 default:
79 default: 79 throw NotImplementedException("Texture type: {}", type);
80 throw NotImplementedException("Texture type: {}", type); 80 }
81 } 81}
82
83std::string_view ColorSamplerType(TextureType type, bool is_multisample = false) {
84 if (is_multisample) {
85 ASSERT(type == TextureType::Color2D || type == TextureType::ColorArray2D);
82 } 86 }
83 switch (type) { 87 switch (type) {
84 case TextureType::Color1D: 88 case TextureType::Color1D:
@@ -87,9 +91,9 @@ std::string_view SamplerType(TextureType type, bool is_depth) {
87 return "sampler1DArray"; 91 return "sampler1DArray";
88 case TextureType::Color2D: 92 case TextureType::Color2D:
89 case TextureType::Color2DRect: 93 case TextureType::Color2DRect:
90 return "sampler2D"; 94 return is_multisample ? "sampler2DMS" : "sampler2D";
91 case TextureType::ColorArray2D: 95 case TextureType::ColorArray2D:
92 return "sampler2DArray"; 96 return is_multisample ? "sampler2DMSArray" : "sampler2DArray";
93 case TextureType::Color3D: 97 case TextureType::Color3D:
94 return "sampler3D"; 98 return "sampler3D";
95 case TextureType::ColorCube: 99 case TextureType::ColorCube:
@@ -677,7 +681,7 @@ void EmitContext::SetupTextures(Bindings& bindings) {
677 texture_buffers.reserve(info.texture_buffer_descriptors.size()); 681 texture_buffers.reserve(info.texture_buffer_descriptors.size());
678 for (const auto& desc : info.texture_buffer_descriptors) { 682 for (const auto& desc : info.texture_buffer_descriptors) {
679 texture_buffers.push_back({bindings.texture, desc.count}); 683 texture_buffers.push_back({bindings.texture, desc.count});
680 const auto sampler_type{SamplerType(TextureType::Buffer, false)}; 684 const auto sampler_type{ColorSamplerType(TextureType::Buffer)};
681 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 685 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
682 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 686 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
683 sampler_type, bindings.texture, array_decorator); 687 sampler_type, bindings.texture, array_decorator);
@@ -686,7 +690,8 @@ void EmitContext::SetupTextures(Bindings& bindings) {
686 textures.reserve(info.texture_descriptors.size()); 690 textures.reserve(info.texture_descriptors.size());
687 for (const auto& desc : info.texture_descriptors) { 691 for (const auto& desc : info.texture_descriptors) {
688 textures.push_back({bindings.texture, desc.count}); 692 textures.push_back({bindings.texture, desc.count});
689 const auto sampler_type{SamplerType(desc.type, desc.is_depth)}; 693 const auto sampler_type{desc.is_depth ? DepthSamplerType(desc.type)
694 : ColorSamplerType(desc.type, desc.is_multisample)};
690 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 695 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
691 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 696 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
692 sampler_type, bindings.texture, array_decorator); 697 sampler_type, bindings.texture, array_decorator);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
index 50daacd95..c4ca28d11 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -18,10 +18,6 @@ void EmitBitCastU64F64(EmitContext&) {
18 throw NotImplementedException("SPIR-V Instruction"); 18 throw NotImplementedException("SPIR-V Instruction");
19} 19}
20 20
21void EmitBitCastS32F32(EmitContext&) {
22 throw NotImplementedException("SPIR-V Instruction");
23}
24
25void EmitBitCastF16U16(EmitContext&) { 21void EmitBitCastF16U16(EmitContext&) {
26 throw NotImplementedException("SPIR-V Instruction"); 22 throw NotImplementedException("SPIR-V Instruction");
27} 23}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index c898ce12f..3b969d915 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -445,11 +445,13 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
445 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 445 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
446} 446}
447 447
448Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { 448Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
449 const IR::Value& skip_mips_val) {
449 const auto info{inst->Flags<IR::TextureInstInfo>()}; 450 const auto info{inst->Flags<IR::TextureInstInfo>()};
450 const Id image{TextureImage(ctx, info, index)}; 451 const Id image{TextureImage(ctx, info, index)};
451 const Id zero{ctx.u32_zero_value}; 452 const Id zero{ctx.u32_zero_value};
452 const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; 453 const bool skip_mips{skip_mips_val.U1()};
454 const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
453 switch (info.type) { 455 switch (info.type) {
454 case TextureType::Color1D: 456 case TextureType::Color1D:
455 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), 457 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod),
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index e31cdc5e8..a440b557d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -179,7 +179,6 @@ Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
179void EmitBitCastU16F16(EmitContext& ctx); 179void EmitBitCastU16F16(EmitContext& ctx);
180Id EmitBitCastU32F32(EmitContext& ctx, Id value); 180Id EmitBitCastU32F32(EmitContext& ctx, Id value);
181void EmitBitCastU64F64(EmitContext& ctx); 181void EmitBitCastU64F64(EmitContext& ctx);
182void EmitBitCastS32F32(EmitContext& ctx);
183void EmitBitCastF16U16(EmitContext&); 182void EmitBitCastF16U16(EmitContext&);
184Id EmitBitCastF32U32(EmitContext& ctx, Id value); 183Id EmitBitCastF32U32(EmitContext& ctx, Id value);
185void EmitBitCastF64U64(EmitContext& ctx); 184void EmitBitCastF64U64(EmitContext& ctx);
@@ -540,7 +539,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
540 const IR::Value& offset, const IR::Value& offset2, Id dref); 539 const IR::Value& offset, const IR::Value& offset2, Id dref);
541Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, 540Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
542 Id lod, Id ms); 541 Id lod, Id ms);
543Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod); 542Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id derivates, Id offset, Id lod_clamp); 546 Id derivates, Id offset, Id lod_clamp);
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index eb2e49a68..b7caa4246 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -704,11 +704,6 @@ IR::U32 IREmitter::BitCast<IR::U32, IR::F32>(const IR::F32& value) {
704} 704}
705 705
706template <> 706template <>
707IR::S32 IREmitter::BitCast<IR::S32, IR::F32>(const IR::F32& value) {
708 return Inst<IR::S32>(Opcode::BitCastS32F32, value);
709}
710
711template <>
712IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) { 707IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) {
713 return Inst<IR::F32>(Opcode::BitCastF32U32, value); 708 return Inst<IR::F32>(Opcode::BitCastF32U32, value);
714} 709}
@@ -1851,15 +1846,16 @@ Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Valu
1851 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling); 1846 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling);
1852} 1847}
1853 1848
1854Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) { 1849Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1850 const IR::U1& skip_mips) {
1855 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions 1851 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions
1856 : Opcode::BindlessImageQueryDimensions}; 1852 : Opcode::BindlessImageQueryDimensions};
1857 return Inst(op, handle, lod); 1853 return Inst(op, handle, lod, skip_mips);
1858} 1854}
1859 1855
1860Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, 1856Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1861 TextureInstInfo info) { 1857 const IR::U1& skip_mips, TextureInstInfo info) {
1862 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod); 1858 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips);
1863} 1859}
1864 1860
1865Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { 1861Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 7aaaa4ab0..df158c928 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -320,9 +320,10 @@ public:
320 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, 320 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
321 const F32& dref, const F32& lod, 321 const F32& dref, const F32& lod,
322 const Value& offset, TextureInstInfo info); 322 const Value& offset, TextureInstInfo info);
323 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
324 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, 323 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
325 TextureInstInfo info); 324 const IR::U1& skip_mips);
325 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
326 const IR::U1& skip_mips, TextureInstInfo info);
326 327
327 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, 328 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
328 TextureInstInfo info); 329 TextureInstInfo info);
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index d155afd0f..e300714f3 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -38,7 +38,6 @@ constexpr Type U8{Type::U8};
38constexpr Type U16{Type::U16}; 38constexpr Type U16{Type::U16};
39constexpr Type U32{Type::U32}; 39constexpr Type U32{Type::U32};
40constexpr Type U64{Type::U64}; 40constexpr Type U64{Type::U64};
41constexpr Type S32{Type::S32};
42constexpr Type F16{Type::F16}; 41constexpr Type F16{Type::F16};
43constexpr Type F32{Type::F32}; 42constexpr Type F32{Type::F32};
44constexpr Type F64{Type::F64}; 43constexpr Type F64{Type::F64};
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 1fe3749cc..4447d67b0 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -175,7 +175,6 @@ OPCODE(SelectF64, F64, U1,
175OPCODE(BitCastU16F16, U16, F16, ) 175OPCODE(BitCastU16F16, U16, F16, )
176OPCODE(BitCastU32F32, U32, F32, ) 176OPCODE(BitCastU32F32, U32, F32, )
177OPCODE(BitCastU64F64, U64, F64, ) 177OPCODE(BitCastU64F64, U64, F64, )
178OPCODE(BitCastS32F32, S32, F32, )
179OPCODE(BitCastF16U16, F16, U16, ) 178OPCODE(BitCastF16U16, F16, U16, )
180OPCODE(BitCastF32U32, F32, U32, ) 179OPCODE(BitCastF32U32, F32, U32, )
181OPCODE(BitCastF64U64, F64, U64, ) 180OPCODE(BitCastF64U64, F64, U64, )
@@ -483,7 +482,7 @@ OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32,
483OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 482OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
484OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 483OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
485OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 484OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
486OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, ) 485OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, U1, )
487OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, ) 486OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, )
488OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 487OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
489OPCODE(BindlessImageRead, U32x4, U32, Opaque, ) 488OPCODE(BindlessImageRead, U32x4, U32, Opaque, )
@@ -496,7 +495,7 @@ OPCODE(BoundImageSampleDrefExplicitLod, F32, U32,
496OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 495OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
497OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 496OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
498OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 497OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
499OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, ) 498OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, U1, )
500OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, ) 499OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, )
501OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 500OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
502OPCODE(BoundImageRead, U32x4, U32, Opaque, ) 501OPCODE(BoundImageRead, U32x4, U32, Opaque, )
@@ -509,7 +508,7 @@ OPCODE(ImageSampleDrefExplicitLod, F32, Opaq
509OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, ) 508OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
510OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, ) 509OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
511OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) 510OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
512OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, ) 511OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, )
513OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) 512OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
514OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) 513OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
515OPCODE(ImageRead, U32x4, Opaque, Opaque, ) 514OPCODE(ImageRead, U32x4, Opaque, Opaque, )
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h
index 5a7c706ad..04c8c4ddb 100644
--- a/src/shader_recompiler/frontend/ir/type.h
+++ b/src/shader_recompiler/frontend/ir/type.h
@@ -24,22 +24,21 @@ enum class Type {
24 U16 = 1 << 7, 24 U16 = 1 << 7,
25 U32 = 1 << 8, 25 U32 = 1 << 8,
26 U64 = 1 << 9, 26 U64 = 1 << 9,
27 S32 = 1 << 10, 27 F16 = 1 << 10,
28 F16 = 1 << 11, 28 F32 = 1 << 11,
29 F32 = 1 << 12, 29 F64 = 1 << 12,
30 F64 = 1 << 13, 30 U32x2 = 1 << 13,
31 U32x2 = 1 << 14, 31 U32x3 = 1 << 14,
32 U32x3 = 1 << 15, 32 U32x4 = 1 << 15,
33 U32x4 = 1 << 16, 33 F16x2 = 1 << 16,
34 F16x2 = 1 << 17, 34 F16x3 = 1 << 17,
35 F16x3 = 1 << 18, 35 F16x4 = 1 << 18,
36 F16x4 = 1 << 19, 36 F32x2 = 1 << 19,
37 F32x2 = 1 << 20, 37 F32x3 = 1 << 20,
38 F32x3 = 1 << 21, 38 F32x4 = 1 << 21,
39 F32x4 = 1 << 22, 39 F64x2 = 1 << 22,
40 F64x2 = 1 << 23, 40 F64x3 = 1 << 23,
41 F64x3 = 1 << 24, 41 F64x4 = 1 << 24,
42 F64x4 = 1 << 25,
43}; 42};
44DECLARE_ENUM_FLAG_OPERATORS(Type) 43DECLARE_ENUM_FLAG_OPERATORS(Type)
45 44
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
index 30ba12316..346169328 100644
--- a/src/shader_recompiler/frontend/ir/value.cpp
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -23,8 +23,6 @@ Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {}
23 23
24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} 24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
25 25
26Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {}
27
28Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} 26Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {}
29 27
30Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} 28Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
@@ -71,7 +69,6 @@ bool Value::operator==(const Value& other) const {
71 return imm_u16 == other.imm_u16; 69 return imm_u16 == other.imm_u16;
72 case Type::U32: 70 case Type::U32:
73 case Type::F32: 71 case Type::F32:
74 case Type::S32:
75 return imm_u32 == other.imm_u32; 72 return imm_u32 == other.imm_u32;
76 case Type::U64: 73 case Type::U64:
77 case Type::F64: 74 case Type::F64:
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 8b34356fd..883dfa24e 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -268,7 +268,6 @@ using U8 = TypedValue<Type::U8>;
268using U16 = TypedValue<Type::U16>; 268using U16 = TypedValue<Type::U16>;
269using U32 = TypedValue<Type::U32>; 269using U32 = TypedValue<Type::U32>;
270using U64 = TypedValue<Type::U64>; 270using U64 = TypedValue<Type::U64>;
271using S32 = TypedValue<Type::S32>;
272using F16 = TypedValue<Type::F16>; 271using F16 = TypedValue<Type::F16>;
273using F32 = TypedValue<Type::F32>; 272using F32 = TypedValue<Type::F32>;
274using F64 = TypedValue<Type::F64>; 273using F64 = TypedValue<Type::F64>;
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
index f8cfd4ab6..39af62559 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
@@ -15,11 +15,13 @@ enum class Mode : u64 {
15 SamplePos = 5, 15 SamplePos = 5,
16}; 16};
17 17
18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg) { 18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg, u64 mask) {
19 switch (mode) { 19 switch (mode) {
20 case Mode::Dimension: { 20 case Mode::Dimension: {
21 const bool needs_num_mips{((mask >> 3) & 1) != 0};
22 const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)};
21 const IR::U32 lod{v.X(src_reg)}; 23 const IR::U32 lod{v.X(src_reg)};
22 return v.ir.ImageQueryDimension(handle, lod); 24 return v.ir.ImageQueryDimension(handle, lod, skip_mips);
23 } 25 }
24 case Mode::TextureType: 26 case Mode::TextureType:
25 case Mode::SamplePos: 27 case Mode::SamplePos:
@@ -46,7 +48,7 @@ void Impl(TranslatorVisitor& v, u64 insn, std::optional<u32> cbuf_offset) {
46 handle = v.X(src_reg); 48 handle = v.X(src_reg);
47 ++src_reg; 49 ++src_reg;
48 } 50 }
49 const IR::Value query{Query(v, handle, txq.mode, src_reg)}; 51 const IR::Value query{Query(v, handle, txq.mode, src_reg, txq.mask)};
50 IR::Reg dest_reg{txq.dest_reg}; 52 IR::Reg dest_reg{txq.dest_reg};
51 for (int element = 0; element < 4; ++element) { 53 for (int element = 0; element < 4; ++element) {
52 if (((txq.mask >> element) & 1) == 0) { 54 if (((txq.mask >> element) & 1) == 0) {
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 14496b911..d374c976a 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -355,21 +355,21 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
355 }; 355 };
356} 356}
357 357
358TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { 358u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; 360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left}; 361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left};
362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset) 362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)
363 << cbuf.secondary_shift_left}; 363 << cbuf.secondary_shift_left};
364 return env.ReadTextureType(lhs_raw | rhs_raw); 364 return lhs_raw | rhs_raw;
365}
366
367TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
368 return env.ReadTextureType(GetTextureHandle(env, cbuf));
365} 369}
366 370
367TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) { 371TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
368 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
369 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
370 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
371 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
372 return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
373} 373}
374 374
375class Descriptors { 375class Descriptors {
@@ -459,7 +459,8 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
459 const IR::Value coord(inst.Arg(1)); 459 const IR::Value coord(inst.Arg(1));
460 const IR::Value handle(ir.Imm32(0)); 460 const IR::Value handle(ir.Imm32(0));
461 const IR::U32 lod{ir.Imm32(0)}; 461 const IR::U32 lod{ir.Imm32(0)};
462 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, info); 462 const IR::U1 skip_mips{ir.Imm1(true)};
463 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, skip_mips, info);
463 inst.SetArg( 464 inst.SetArg(
464 1, ir.CompositeConstruct( 465 1, ir.CompositeConstruct(
465 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)), 466 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)),
@@ -493,10 +494,10 @@ void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_
493 const IR::F32 w(ir.CompositeExtract(new_inst, 3)); 494 const IR::F32 w(ir.CompositeExtract(new_inst, 3));
494 const IR::F16F32F64 max_value(ir.Imm32(get_max_value())); 495 const IR::F16F32F64 max_value(ir.Imm32(get_max_value()));
495 const IR::Value converted = 496 const IR::Value converted =
496 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value), 497 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(x)), max_value),
497 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value), 498 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(y)), max_value),
498 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value), 499 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(z)), max_value),
499 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value)); 500 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(w)), max_value));
500 inst.ReplaceUsesWith(converted); 501 inst.ReplaceUsesWith(converted);
501} 502}
502} // Anonymous namespace 503} // Anonymous namespace
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index b474eb363..4742bcbe9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -52,6 +52,8 @@ add_library(video_core STATIC
52 engines/puller.cpp 52 engines/puller.cpp
53 engines/puller.h 53 engines/puller.h
54 framebuffer_config.h 54 framebuffer_config.h
55 fsr.cpp
56 fsr.h
55 host1x/codecs/codec.cpp 57 host1x/codecs/codec.cpp
56 host1x/codecs/codec.h 58 host1x/codecs/codec.h
57 host1x/codecs/h264.cpp 59 host1x/codecs/h264.cpp
@@ -110,6 +112,8 @@ add_library(video_core STATIC
110 renderer_opengl/gl_device.h 112 renderer_opengl/gl_device.h
111 renderer_opengl/gl_fence_manager.cpp 113 renderer_opengl/gl_fence_manager.cpp
112 renderer_opengl/gl_fence_manager.h 114 renderer_opengl/gl_fence_manager.h
115 renderer_opengl/gl_fsr.cpp
116 renderer_opengl/gl_fsr.h
113 renderer_opengl/gl_graphics_pipeline.cpp 117 renderer_opengl/gl_graphics_pipeline.cpp
114 renderer_opengl/gl_graphics_pipeline.h 118 renderer_opengl/gl_graphics_pipeline.h
115 renderer_opengl/gl_rasterizer.cpp 119 renderer_opengl/gl_rasterizer.cpp
diff --git a/src/video_core/fsr.cpp b/src/video_core/fsr.cpp
new file mode 100644
index 000000000..5653c64fc
--- /dev/null
+++ b/src/video_core/fsr.cpp
@@ -0,0 +1,148 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cmath>
5#include "video_core/fsr.h"
6
7namespace FSR {
8namespace {
9// Reimplementations of the constant generating functions in ffx_fsr1.h
10// GCC generated a lot of warnings when using the official header.
11u32 AU1_AH1_AF1(f32 f) {
12 static constexpr u32 base[512]{
13 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
14 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
15 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
16 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
17 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
18 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
19 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
20 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
21 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
22 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
23 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
24 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
25 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
26 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
27 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
28 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
29 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
30 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
31 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
32 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
33 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
34 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
35 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
36 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
37 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
38 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
39 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
40 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
41 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
42 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
43 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
44 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
45 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
46 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
47 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
48 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
49 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
50 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
51 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
52 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
53 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
54 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
55 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
56 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
57 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
58 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
59 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
60 };
61 static constexpr s8 shift[512]{
62 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
63 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
64 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
65 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
66 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
67 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
68 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
69 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
70 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
71 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
72 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
73 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
74 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
75 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
76 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
77 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
78 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
79 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
80 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
81 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
82 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
83 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
84 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
85 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
86 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
87 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
88 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
89 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
90 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
91 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
92 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
93 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
94 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
95 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
96 0x18, 0x18,
97 };
98 const u32 u = Common::BitCast<u32>(f);
99 const u32 i = u >> 23;
100 return base[i] + ((u & 0x7fffff) >> shift[i]);
101}
102
103u32 AU1_AH2_AF2(f32 a[2]) {
104 return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
105}
106
107void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
108 f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
109 f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
110 con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
111 con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
112 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
113 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
114 con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
115 con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
116 con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
117 con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
118 con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
119 con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
120 con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
121 con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
122 con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
123 con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
124 con3[2] = con3[3] = 0;
125}
126} // Anonymous namespace
127
128void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
129 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
130 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
131 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
132 FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
133 inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
134 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
135 inputOffsetInPixelsX);
136 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
137 inputOffsetInPixelsY);
138}
139
140void FsrRcasCon(u32* con, f32 sharpness) {
141 sharpness = std::exp2f(-sharpness);
142 f32 hSharp[2]{sharpness, sharpness};
143 con[0] = Common::BitCast<u32>(sharpness);
144 con[1] = AU1_AH2_AF2(hSharp);
145 con[2] = 0;
146 con[3] = 0;
147}
148} // namespace FSR
diff --git a/src/video_core/fsr.h b/src/video_core/fsr.h
new file mode 100644
index 000000000..db0d4ec6f
--- /dev/null
+++ b/src/video_core/fsr.h
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_cast.h"
7#include "common/common_types.h"
8
9namespace FSR {
10// Reimplementations of the constant generating functions in ffx_fsr1.h
11// GCC generated a lot of warnings when using the official header.
12void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
13 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
14 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
15 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY);
16
17void FsrRcasCon(u32* con, f32 sharpness);
18
19} // namespace FSR
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index e968ae220..dad7b07d4 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -3,12 +3,16 @@
3 3
4set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr) 4set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr)
5 5
6set(GLSL_INCLUDES 6set(FIDELITYFX_FILES
7 fidelityfx_fsr.comp
8 ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h 7 ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h
9 ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h 8 ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h
10) 9)
11 10
11set(GLSL_INCLUDES
12 fidelityfx_fsr.comp
13 ${FIDELITYFX_FILES}
14)
15
12set(SHADER_FILES 16set(SHADER_FILES
13 astc_decoder.comp 17 astc_decoder.comp
14 blit_color_float.frag 18 blit_color_float.frag
@@ -24,6 +28,9 @@ set(SHADER_FILES
24 fxaa.vert 28 fxaa.vert
25 opengl_convert_s8d24.comp 29 opengl_convert_s8d24.comp
26 opengl_copy_bc4.comp 30 opengl_copy_bc4.comp
31 opengl_fidelityfx_fsr.frag
32 opengl_fidelityfx_fsr_easu.frag
33 opengl_fidelityfx_fsr_rcas.frag
27 opengl_present.frag 34 opengl_present.frag
28 opengl_present.vert 35 opengl_present.vert
29 opengl_present_scaleforce.frag 36 opengl_present_scaleforce.frag
@@ -118,6 +125,25 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
118 endif() 125 endif()
119endforeach() 126endforeach()
120 127
128foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
129 get_filename_component(FILENAME ${FILEPATH} NAME)
130 string(REPLACE "." "_" HEADER_NAME ${FILENAME})
131 set(SOURCE_FILE ${FILEPATH})
132 set(SOURCE_HEADER_FILE ${SHADER_DIR}/${HEADER_NAME}.h)
133 add_custom_command(
134 OUTPUT
135 ${SOURCE_HEADER_FILE}
136 COMMAND
137 ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${SOURCE_HEADER_FILE} ${INPUT_FILE}
138 MAIN_DEPENDENCY
139 ${SOURCE_FILE}
140 DEPENDS
141 ${INPUT_FILE}
142 # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
143 )
144 set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
145endforeach()
146
121set(SHADER_SOURCES ${SHADER_FILES}) 147set(SHADER_SOURCES ${SHADER_FILES})
122list(APPEND SHADER_SOURCES ${GLSL_INCLUDES}) 148list(APPEND SHADER_SOURCES ${GLSL_INCLUDES})
123 149
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
new file mode 100644
index 000000000..16d22f58e
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
@@ -0,0 +1,108 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//!#version 460 core
5#extension GL_ARB_separate_shader_objects : enable
6#extension GL_ARB_shading_language_420pack : enable
7
8#extension GL_AMD_gpu_shader_half_float : enable
9#extension GL_NV_gpu_shader5 : enable
10
11// FidelityFX Super Resolution Sample
12//
13// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
14// Permission is hereby granted, free of charge, to any person obtaining a copy
15// of this software and associated documentation files(the "Software"), to deal
16// in the Software without restriction, including without limitation the rights
17// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
18// copies of the Software, and to permit persons to whom the Software is
19// furnished to do so, subject to the following conditions :
20// The above copyright notice and this permission notice shall be included in
21// all copies or substantial portions of the Software.
22// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28// THE SOFTWARE.
29
30layout (location = 0) uniform uvec4 constants[4];
31
32#define A_GPU 1
33#define A_GLSL 1
34
35#ifdef YUZU_USE_FP16
36 #define A_HALF
37#endif
38#include "ffx_a.h"
39
40#ifndef YUZU_USE_FP16
41 layout (binding=0) uniform sampler2D InputTexture;
42 #if USE_EASU
43 #define FSR_EASU_F 1
44 AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(InputTexture, p, 0); return res; }
45 AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(InputTexture, p, 1); return res; }
46 AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(InputTexture, p, 2); return res; }
47 #endif
48 #if USE_RCAS
49 #define FSR_RCAS_F
50 AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(InputTexture, ASU2(p), 0); }
51 void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
52 #endif
53#else
54 layout (binding=0) uniform sampler2D InputTexture;
55 #if USE_EASU
56 #define FSR_EASU_H 1
57 AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 0)); return res; }
58 AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 1)); return res; }
59 AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 2)); return res; }
60 #endif
61 #if USE_RCAS
62 #define FSR_RCAS_H
63 AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(InputTexture, ASU2(p), 0)); }
64 void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){}
65 #endif
66#endif
67
68#include "ffx_fsr1.h"
69
70#if USE_RCAS
71 layout(location = 0) in vec2 frag_texcoord;
72#endif
73layout (location = 0) out vec4 frag_color;
74
75void CurrFilter(AU2 pos)
76{
77#if USE_EASU
78 #ifndef YUZU_USE_FP16
79 AF3 c;
80 FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
81 frag_color = AF4(c, 1.0);
82 #else
83 AH3 c;
84 FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
85 frag_color = AH4(c, 1.0);
86 #endif
87#endif
88#if USE_RCAS
89 #ifndef YUZU_USE_FP16
90 AF3 c;
91 FsrRcasF(c.r, c.g, c.b, pos, constants[0]);
92 frag_color = AF4(c, 1.0);
93 #else
94 AH3 c;
95 FsrRcasH(c.r, c.g, c.b, pos, constants[0]);
96 frag_color = AH4(c, 1.0);
97 #endif
98#endif
99}
100
101void main()
102{
103#if USE_RCAS
104 CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0))));
105#else
106 CurrFilter(AU2(gl_FragCoord.xy));
107#endif
108}
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag
new file mode 100644
index 000000000..d39f80ac1
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5#extension GL_GOOGLE_include_directive : enable
6
7#define USE_EASU 1
8
9#include "opengl_fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag
new file mode 100644
index 000000000..cfa78ddc7
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5#extension GL_GOOGLE_include_directive : enable
6
7#define USE_RCAS 1
8
9#include "opengl_fidelityfx_fsr.frag"
diff --git a/src/video_core/renderer_opengl/gl_fsr.cpp b/src/video_core/renderer_opengl/gl_fsr.cpp
new file mode 100644
index 000000000..77262dcf1
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fsr.cpp
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/fsr.h"
6#include "video_core/renderer_opengl/gl_fsr.h"
7#include "video_core/renderer_opengl/gl_shader_manager.h"
8#include "video_core/renderer_opengl/gl_shader_util.h"
9
10namespace OpenGL {
11using namespace FSR;
12
13using FsrConstants = std::array<u32, 4 * 4>;
14
15FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
16 std::string_view fsr_rcas_source)
17 : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)},
18 fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)},
19 fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} {
20 glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f);
21 glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f);
22}
23
24FSR::~FSR() = default;
25
26void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
27 u32 input_image_width, u32 input_image_height,
28 const Common::Rectangle<int>& crop_rect) {
29
30 const auto output_image_width = screen.GetWidth();
31 const auto output_image_height = screen.GetHeight();
32
33 if (fsr_intermediate_tex.handle) {
34 GLint fsr_tex_width, fsr_tex_height;
35 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH,
36 &fsr_tex_width);
37 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT,
38 &fsr_tex_height);
39 if (static_cast<u32>(fsr_tex_width) != output_image_width ||
40 static_cast<u32>(fsr_tex_height) != output_image_height) {
41 fsr_intermediate_tex.Release();
42 }
43 }
44 if (!fsr_intermediate_tex.handle) {
45 fsr_intermediate_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width,
47 output_image_height);
48 glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0,
49 fsr_intermediate_tex.handle, 0);
50 }
51
52 GLint old_draw_fb;
53 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
54
55 glFrontFace(GL_CW);
56 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle);
57 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width),
58 static_cast<GLfloat>(output_image_height));
59
60 FsrConstants constants;
61 FsrEasuConOffset(
62 constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12,
63
64 static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()),
65 static_cast<f32>(input_image_width), static_cast<f32>(input_image_height),
66 static_cast<f32>(output_image_width), static_cast<f32>(output_image_height),
67 static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
68
69 glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants));
70
71 program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle);
72 glDrawArrays(GL_TRIANGLES, 0, 3);
73
74 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
75 glBindTextureUnit(0, fsr_intermediate_tex.handle);
76
77 const float sharpening =
78 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
79
80 FsrRcasCon(constants.data(), sharpening);
81 glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants));
82}
83
84void FSR::InitBuffers() {
85 fsr_framebuffer.Create();
86}
87
88void FSR::ReleaseBuffers() {
89 fsr_framebuffer.Release();
90 fsr_intermediate_tex.Release();
91}
92
93const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept {
94 return fsr_rcas_frag;
95}
96
97bool FSR::AreBuffersInitialized() const noexcept {
98 return fsr_framebuffer.handle;
99}
100
101} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fsr.h b/src/video_core/renderer_opengl/gl_fsr.h
new file mode 100644
index 000000000..1f6ae3115
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fsr.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string_view>
7
8#include "common/common_types.h"
9#include "common/math_util.h"
10#include "video_core/fsr.h"
11#include "video_core/renderer_opengl/gl_resource_manager.h"
12
13namespace OpenGL {
14
15class ProgramManager;
16
17class FSR {
18public:
19 explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
20 std::string_view fsr_rcas_source);
21 ~FSR();
22
23 void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
24 u32 input_image_width, u32 input_image_height,
25 const Common::Rectangle<int>& crop_rect);
26
27 void InitBuffers();
28
29 void ReleaseBuffers();
30
31 [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept;
32
33 [[nodiscard]] bool AreBuffersInitialized() const noexcept;
34
35private:
36 OGLFramebuffer fsr_framebuffer;
37 OGLProgram fsr_vertex;
38 OGLProgram fsr_easu_frag;
39 OGLProgram fsr_rcas_frag;
40 OGLTexture fsr_intermediate_tex;
41};
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index de95f2634..2a74c1d05 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -17,8 +17,14 @@
17#include "core/frontend/emu_window.h" 17#include "core/frontend/emu_window.h"
18#include "core/memory.h" 18#include "core/memory.h"
19#include "core/telemetry_session.h" 19#include "core/telemetry_session.h"
20#include "video_core/host_shaders/ffx_a_h.h"
21#include "video_core/host_shaders/ffx_fsr1_h.h"
22#include "video_core/host_shaders/full_screen_triangle_vert.h"
20#include "video_core/host_shaders/fxaa_frag.h" 23#include "video_core/host_shaders/fxaa_frag.h"
21#include "video_core/host_shaders/fxaa_vert.h" 24#include "video_core/host_shaders/fxaa_vert.h"
25#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
26#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
27#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
22#include "video_core/host_shaders/opengl_present_frag.h" 28#include "video_core/host_shaders/opengl_present_frag.h"
23#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" 29#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
24#include "video_core/host_shaders/opengl_present_vert.h" 30#include "video_core/host_shaders/opengl_present_vert.h"
@@ -31,6 +37,7 @@
31#include "video_core/host_shaders/smaa_edge_detection_vert.h" 37#include "video_core/host_shaders/smaa_edge_detection_vert.h"
32#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" 38#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
33#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" 39#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
40#include "video_core/renderer_opengl/gl_fsr.h"
34#include "video_core/renderer_opengl/gl_rasterizer.h" 41#include "video_core/renderer_opengl/gl_rasterizer.h"
35#include "video_core/renderer_opengl/gl_shader_manager.h" 42#include "video_core/renderer_opengl/gl_shader_manager.h"
36#include "video_core/renderer_opengl/gl_shader_util.h" 43#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -268,12 +275,17 @@ void RendererOpenGL::InitOpenGLObjects() {
268 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); 275 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
269 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); 276 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
270 277
271 const auto SmaaShader = [](std::string_view specialized_source, GLenum stage) { 278 const auto replace_include = [](std::string& shader_source, std::string_view include_name,
272 std::string shader_source{specialized_source}; 279 std::string_view include_content) {
273 constexpr std::string_view include_string = "#include \"opengl_smaa.glsl\""; 280 const std::string include_string = fmt::format("#include \"{}\"", include_name);
274 const std::size_t pos = shader_source.find(include_string); 281 const std::size_t pos = shader_source.find(include_string);
275 ASSERT(pos != std::string::npos); 282 ASSERT(pos != std::string::npos);
276 shader_source.replace(pos, include_string.size(), HostShaders::OPENGL_SMAA_GLSL); 283 shader_source.replace(pos, include_string.size(), include_content);
284 };
285
286 const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
287 std::string shader_source{specialized_source};
288 replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
277 return CreateProgram(shader_source, stage); 289 return CreateProgram(shader_source, stage);
278 }; 290 };
279 291
@@ -298,14 +310,32 @@ void RendererOpenGL::InitOpenGLObjects() {
298 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), 310 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
299 GL_FRAGMENT_SHADER); 311 GL_FRAGMENT_SHADER);
300 312
313 std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
314 replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
315 replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
316
317 std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
318 std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
319 replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
320 replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
321
322 fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source,
323 fsr_rcas_frag_source);
324
301 // Generate presentation sampler 325 // Generate presentation sampler
302 present_sampler.Create(); 326 present_sampler.Create();
303 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 327 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
304 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 328 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
329 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
330 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
331 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
305 332
306 present_sampler_nn.Create(); 333 present_sampler_nn.Create();
307 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 334 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
308 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 335 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
336 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
337 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
338 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
309 339
310 // Generate VBO handle for drawing 340 // Generate VBO handle for drawing
311 vertex_buffer.Create(); 341 vertex_buffer.Create();
@@ -525,6 +555,31 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
525 555
526 glBindTextureUnit(0, aa_texture.handle); 556 glBindTextureUnit(0, aa_texture.handle);
527 } 557 }
558 glDisablei(GL_SCISSOR_TEST, 0);
559
560 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
561 if (!fsr->AreBuffersInitialized()) {
562 fsr->InitBuffers();
563 }
564
565 auto crop_rect = framebuffer_crop_rect;
566 if (crop_rect.GetWidth() == 0) {
567 crop_rect.right = framebuffer_width;
568 }
569 if (crop_rect.GetHeight() == 0) {
570 crop_rect.bottom = framebuffer_height;
571 }
572 crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
573 const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width);
574 const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height);
575 glBindSampler(0, present_sampler.handle);
576 fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect);
577 } else {
578 if (fsr->AreBuffersInitialized()) {
579 fsr->ReleaseBuffers();
580 }
581 }
582
528 const std::array ortho_matrix = 583 const std::array ortho_matrix =
529 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); 584 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
530 585
@@ -540,10 +595,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
540 case Settings::ScalingFilter::ScaleForce: 595 case Settings::ScalingFilter::ScaleForce:
541 return present_scaleforce_fragment.handle; 596 return present_scaleforce_fragment.handle;
542 case Settings::ScalingFilter::Fsr: 597 case Settings::ScalingFilter::Fsr:
543 LOG_WARNING( 598 return fsr->GetPresentFragmentProgram().handle;
544 Render_OpenGL,
545 "FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
546 return present_scaleforce_fragment.handle;
547 default: 599 default:
548 return present_bilinear_fragment.handle; 600 return present_bilinear_fragment.handle;
549 } 601 }
@@ -578,15 +630,18 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
578 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); 630 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
579 f32 scale_v = 631 f32 scale_v =
580 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); 632 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
581 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 633
582 // (e.g. handheld mode) on a 1920x1080 framebuffer. 634 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) {
583 if (framebuffer_crop_rect.GetWidth() > 0) { 635 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
584 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / 636 // (e.g. handheld mode) on a 1920x1080 framebuffer.
585 static_cast<f32>(screen_info.texture.width); 637 if (framebuffer_crop_rect.GetWidth() > 0) {
586 } 638 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
587 if (framebuffer_crop_rect.GetHeight() > 0) { 639 static_cast<f32>(screen_info.texture.width);
588 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / 640 }
589 static_cast<f32>(screen_info.texture.height); 641 if (framebuffer_crop_rect.GetHeight() > 0) {
642 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
643 static_cast<f32>(screen_info.texture.height);
644 }
590 } 645 }
591 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa && 646 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa &&
592 !screen_info.was_accelerated) { 647 !screen_info.was_accelerated) {
@@ -612,7 +667,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
612 } else { 667 } else {
613 glDisable(GL_FRAMEBUFFER_SRGB); 668 glDisable(GL_FRAMEBUFFER_SRGB);
614 } 669 }
615 glDisablei(GL_SCISSOR_TEST, 0);
616 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), 670 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
617 static_cast<GLfloat>(layout.height)); 671 static_cast<GLfloat>(layout.height));
618 672
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index cc97d7b26..f1d5fd954 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -10,6 +10,7 @@
10 10
11#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_device.h" 12#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_fsr.h"
13#include "video_core/renderer_opengl/gl_rasterizer.h" 14#include "video_core/renderer_opengl/gl_rasterizer.h"
14#include "video_core/renderer_opengl/gl_resource_manager.h" 15#include "video_core/renderer_opengl/gl_resource_manager.h"
15#include "video_core/renderer_opengl/gl_shader_manager.h" 16#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -141,6 +142,8 @@ private:
141 OGLTexture smaa_edges_tex; 142 OGLTexture smaa_edges_tex;
142 OGLTexture smaa_blend_tex; 143 OGLTexture smaa_blend_tex;
143 144
145 std::unique_ptr<FSR> fsr;
146
144 /// OpenGL framebuffer data 147 /// OpenGL framebuffer data
145 std::vector<u8> gl_framebuffer_data; 148 std::vector<u8> gl_framebuffer_data;
146 149
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
index 33daa8c1c..df972cd54 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ b/src/video_core/renderer_vulkan/vk_fsr.cpp
@@ -1,12 +1,11 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cmath>
5#include "common/bit_cast.h"
6#include "common/common_types.h" 4#include "common/common_types.h"
7#include "common/div_ceil.h" 5#include "common/div_ceil.h"
8#include "common/settings.h" 6#include "common/settings.h"
9 7
8#include "video_core/fsr.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" 9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" 10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
12#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h" 11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h"
@@ -17,146 +16,7 @@
17#include "video_core/vulkan_common/vulkan_device.h" 16#include "video_core/vulkan_common/vulkan_device.h"
18 17
19namespace Vulkan { 18namespace Vulkan {
20namespace { 19using namespace FSR;
21// Reimplementations of the constant generating functions in ffx_fsr1.h
22// GCC generated a lot of warnings when using the official header.
23u32 AU1_AH1_AF1(f32 f) {
24 static constexpr u32 base[512]{
25 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
26 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
27 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
28 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
29 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
30 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
31 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
32 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
33 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
34 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
35 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
36 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
37 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
38 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
39 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
40 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
41 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
42 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
43 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
44 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
45 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
46 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
47 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
48 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
49 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
50 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
51 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
52 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
53 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
54 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
55 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
56 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
57 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
58 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
59 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
60 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
61 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
62 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
63 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
64 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
65 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
66 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
67 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
68 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
69 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
70 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
71 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
72 };
73 static constexpr s8 shift[512]{
74 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
75 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
76 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
77 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
78 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
79 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
80 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
81 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
82 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
83 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
84 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
85 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
86 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
87 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
88 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
89 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
90 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
91 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
92 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
93 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
94 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
95 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
96 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
97 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
98 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
99 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
100 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
101 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
102 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
103 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
104 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
105 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
106 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
107 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
108 0x18, 0x18,
109 };
110 const u32 u = Common::BitCast<u32>(f);
111 const u32 i = u >> 23;
112 return base[i] + ((u & 0x7fffff) >> shift[i]);
113}
114
115u32 AU1_AH2_AF2(f32 a[2]) {
116 return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
117}
118
119void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
120 f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
121 f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
122 con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
123 con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
124 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
125 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
126 con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
127 con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
128 con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
129 con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
130 con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
131 con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
132 con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
133 con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
134 con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
135 con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
136 con3[2] = con3[3] = 0;
137}
138
139void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
140 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
141 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
142 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
143 FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
144 inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
145 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
146 inputOffsetInPixelsX);
147 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
148 inputOffsetInPixelsY);
149}
150
151void FsrRcasCon(u32* con, f32 sharpness) {
152 sharpness = std::exp2f(-sharpness);
153 f32 hSharp[2]{sharpness, sharpness};
154 con[0] = Common::BitCast<u32>(sharpness);
155 con[1] = AU1_AH2_AF2(hSharp);
156 con[2] = 0;
157 con[3] = 0;
158}
159} // Anonymous namespace
160 20
161FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_, 21FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_,
162 VkExtent2D output_size_) 22 VkExtent2D output_size_)
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 852ec2519..e9100091e 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -100,6 +100,10 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
101 break; 101 break;
102 } 102 }
103 if (num_samples > 1) {
104 size.width *= NumSamplesX(config.msaa_mode);
105 size.height *= NumSamplesY(config.msaa_mode);
106 }
103 if (type != ImageType::Linear) { 107 if (type != ImageType::Linear) {
104 // FIXME: Call this without passing *this 108 // FIXME: Call this without passing *this
105 layer_stride = CalculateLayerStride(*this); 109 layer_stride = CalculateLayerStride(*this);
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index d552bccf0..203ac1b11 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -51,4 +51,48 @@ namespace VideoCommon {
51 return 1; 51 return 1;
52} 52}
53 53
54[[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) {
55 using Tegra::Texture::MsaaMode;
56 switch (msaa_mode) {
57 case MsaaMode::Msaa1x1:
58 return 1;
59 case MsaaMode::Msaa2x1:
60 case MsaaMode::Msaa2x1_D3D:
61 case MsaaMode::Msaa2x2:
62 case MsaaMode::Msaa2x2_VC4:
63 case MsaaMode::Msaa2x2_VC12:
64 return 2;
65 case MsaaMode::Msaa4x2:
66 case MsaaMode::Msaa4x2_D3D:
67 case MsaaMode::Msaa4x2_VC8:
68 case MsaaMode::Msaa4x2_VC24:
69 case MsaaMode::Msaa4x4:
70 return 4;
71 }
72 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
73 return 1;
74}
75
76[[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) {
77 using Tegra::Texture::MsaaMode;
78 switch (msaa_mode) {
79 case MsaaMode::Msaa1x1:
80 case MsaaMode::Msaa2x1:
81 case MsaaMode::Msaa2x1_D3D:
82 return 1;
83 case MsaaMode::Msaa2x2:
84 case MsaaMode::Msaa2x2_VC4:
85 case MsaaMode::Msaa2x2_VC12:
86 case MsaaMode::Msaa4x2:
87 case MsaaMode::Msaa4x2_D3D:
88 case MsaaMode::Msaa4x2_VC8:
89 case MsaaMode::Msaa4x2_VC24:
90 return 2;
91 case MsaaMode::Msaa4x4:
92 return 4;
93 }
94 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
95 return 1;
96}
97
54} // namespace VideoCommon 98} // namespace VideoCommon
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index bb9910a53..a45ec69ec 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -460,7 +460,7 @@
460 </item> 460 </item>
461 <item> 461 <item>
462 <property name="text"> 462 <property name="text">
463 <string>AMD FidelityFX™️ Super Resolution (Vulkan Only)</string> 463 <string>AMD FidelityFX™️ Super Resolution</string>
464 </property> 464 </property>
465 </item> 465 </item>
466 </widget> 466 </widget>
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 9bb69cab1..41ef4250a 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -58,13 +58,16 @@ std::vector<std::string> InputProfiles::GetInputProfileNames() {
58 std::vector<std::string> profile_names; 58 std::vector<std::string> profile_names;
59 profile_names.reserve(map_profiles.size()); 59 profile_names.reserve(map_profiles.size());
60 60
61 for (const auto& [profile_name, config] : map_profiles) { 61 auto it = map_profiles.cbegin();
62 while (it != map_profiles.cend()) {
63 const auto& [profile_name, config] = *it;
62 if (!ProfileExistsInFilesystem(profile_name)) { 64 if (!ProfileExistsInFilesystem(profile_name)) {
63 DeleteProfile(profile_name); 65 it = map_profiles.erase(it);
64 continue; 66 continue;
65 } 67 }
66 68
67 profile_names.push_back(profile_name); 69 profile_names.push_back(profile_name);
70 ++it;
68 } 71 }
69 72
70 std::stable_sort(profile_names.begin(), profile_names.end()); 73 std::stable_sort(profile_names.begin(), profile_names.end());
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 62aaf41bf..42b7b64c8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -983,11 +983,6 @@ void GMainWindow::InitializeWidgets() {
983 filter_status_button->setFocusPolicy(Qt::NoFocus); 983 filter_status_button->setFocusPolicy(Qt::NoFocus);
984 connect(filter_status_button, &QPushButton::clicked, this, 984 connect(filter_status_button, &QPushButton::clicked, this,
985 &GMainWindow::OnToggleAdaptingFilter); 985 &GMainWindow::OnToggleAdaptingFilter);
986 auto filter = Settings::values.scaling_filter.GetValue();
987 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
988 filter == Settings::ScalingFilter::Fsr) {
989 Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
990 }
991 UpdateFilterText(); 986 UpdateFilterText();
992 filter_status_button->setCheckable(true); 987 filter_status_button->setCheckable(true);
993 filter_status_button->setChecked(true); 988 filter_status_button->setChecked(true);
@@ -3468,10 +3463,6 @@ void GMainWindow::OnToggleAdaptingFilter() {
3468 } else { 3463 } else {
3469 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); 3464 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
3470 } 3465 }
3471 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
3472 filter == Settings::ScalingFilter::Fsr) {
3473 filter = Settings::ScalingFilter::NearestNeighbor;
3474 }
3475 Settings::values.scaling_filter.SetValue(filter); 3466 Settings::values.scaling_filter.SetValue(filter);
3476 filter_status_button->setChecked(true); 3467 filter_status_button->setChecked(true);
3477 UpdateFilterText(); 3468 UpdateFilterText();
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index cbd52da85..d71cc23a7 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -81,20 +81,13 @@ void DirectConnectWindow::Connect() {
81 } 81 }
82 } 82 }
83 } 83 }
84 switch (static_cast<ConnectionType>(ui->connection_type->currentIndex())) { 84 if (!ui->ip->hasAcceptableInput()) {
85 case ConnectionType::TraversalServer: 85 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
86 break; 86 return;
87 case ConnectionType::IP: 87 }
88 if (!ui->ip->hasAcceptableInput()) { 88 if (!ui->port->hasAcceptableInput()) {
89 NetworkMessage::ErrorManager::ShowError( 89 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
90 NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID); 90 return;
91 return;
92 }
93 if (!ui->port->hasAcceptableInput()) {
94 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
95 return;
96 }
97 break;
98 } 91 }
99 92
100 // Store settings 93 // Store settings
diff --git a/src/yuzu/multiplayer/direct_connect.ui b/src/yuzu/multiplayer/direct_connect.ui
index 57d6ec25a..0dd4e6829 100644
--- a/src/yuzu/multiplayer/direct_connect.ui
+++ b/src/yuzu/multiplayer/direct_connect.ui
@@ -27,19 +27,10 @@
27 <number>0</number> 27 <number>0</number>
28 </property> 28 </property>
29 <item> 29 <item>
30 <widget class="QComboBox" name="connection_type">
31 <item>
32 <property name="text">
33 <string>IP Address</string>
34 </property>
35 </item>
36 </widget>
37 </item>
38 <item>
39 <widget class="QWidget" name="ip_container" native="true"> 30 <widget class="QWidget" name="ip_container" native="true">
40 <layout class="QHBoxLayout" name="ip_layout"> 31 <layout class="QHBoxLayout" name="ip_layout">
41 <property name="leftMargin"> 32 <property name="leftMargin">
42 <number>5</number> 33 <number>0</number>
43 </property> 34 </property>
44 <property name="topMargin"> 35 <property name="topMargin">
45 <number>0</number> 36 <number>0</number>
@@ -53,17 +44,17 @@
53 <item> 44 <item>
54 <widget class="QLabel" name="label_2"> 45 <widget class="QLabel" name="label_2">
55 <property name="text"> 46 <property name="text">
56 <string>IP</string> 47 <string>Server Address</string>
57 </property> 48 </property>
58 </widget> 49 </widget>
59 </item> 50 </item>
60 <item> 51 <item>
61 <widget class="QLineEdit" name="ip"> 52 <widget class="QLineEdit" name="ip">
62 <property name="toolTip"> 53 <property name="toolTip">
63 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;IPv4 address of the host&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 54 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Server address of the host&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
64 </property> 55 </property>
65 <property name="maxLength"> 56 <property name="maxLength">
66 <number>16</number> 57 <number>253</number>
67 </property> 58 </property>
68 </widget> 59 </widget>
69 </item> 60 </item>
@@ -85,6 +76,12 @@
85 <property name="placeholderText"> 76 <property name="placeholderText">
86 <string notr="true" extracomment="placeholder string that tells user default port">24872</string> 77 <string notr="true" extracomment="placeholder string that tells user default port">24872</string>
87 </property> 78 </property>
79 <property name="maximumSize">
80 <size>
81 <width>65</width>
82 <height>50</height>
83 </size>
84 </property>
88 </widget> 85 </widget>
89 </item> 86 </item>
90 </layout> 87 </layout>
diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h
index dd25af280..cbbe6757b 100644
--- a/src/yuzu/multiplayer/validation.h
+++ b/src/yuzu/multiplayer/validation.h
@@ -38,11 +38,28 @@ private:
38 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}")); 38 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
39 QRegularExpressionValidator nickname; 39 QRegularExpressionValidator nickname;
40 40
41 /// ipv4 address only 41 /// ipv4 / ipv6 / hostnames
42 // TODO remove this when we support hostnames in direct connect
43 QRegularExpression ip_regex = QRegularExpression(QStringLiteral( 42 QRegularExpression ip_regex = QRegularExpression(QStringLiteral(
44 "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|" 43 // IPv4 regex
45 "2[0-4][0-9]|25[0-5])")); 44 "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|"
45 // IPv6 regex
46 "^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|"
47 "(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-"
48 "5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
49 "(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)"
50 "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
51 "(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]"
52 "\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
53 "(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2["
54 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
55 "(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2["
56 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
57 "(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2["
58 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
59 "(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?"
60 "\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$|"
61 // Hostname regex
62 "^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$"));
46 QRegularExpressionValidator ip; 63 QRegularExpressionValidator ip;
47 64
48 /// port must be between 0 and 65535 65 /// port must be between 0 and 65535