summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hid/emulated_controller.cpp12
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp12
-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_page_table.cpp544
-rw-r--r--src/core/hle/kernel/k_page_table.h86
-rw-r--r--src/core/hle/kernel/k_process.cpp40
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp6
-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/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/input_common/helpers/joycon_driver.cpp3
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp11
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.h6
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h1
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp9
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp5
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp8
-rw-r--r--src/shader_recompiler/shader_info.h1
-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/memory_manager.cpp40
-rw-r--r--src/video_core/memory_manager.h3
-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/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui2
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp1
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_tas.cpp1
-rw-r--r--src/yuzu/debugger/controller.cpp5
-rw-r--r--src/yuzu/debugger/profiler.cpp5
-rw-r--r--src/yuzu/install_dialog.cpp1
-rw-r--r--src/yuzu/main.cpp17
-rw-r--r--src/yuzu/util/limitable_input_dialog.cpp2
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp1
-rw-r--r--src/yuzu_cmd/default_ini.h10
49 files changed, 1151 insertions, 496 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 6e9812e6e..0e06468da 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -11,7 +11,6 @@
11 11
12namespace Core::HID { 12namespace Core::HID {
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = 0x7ffe;
15constexpr s32 HID_TRIGGER_MAX = 0x7fff; 14constexpr s32 HID_TRIGGER_MAX = 0x7fff;
16// Use a common UUID for TAS and Virtual Gamepad 15// Use a common UUID for TAS and Virtual Gamepad
17constexpr Common::UUID TAS_UUID = 16constexpr Common::UUID TAS_UUID =
@@ -864,16 +863,9 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
864 return; 863 return;
865 } 864 }
866 865
867 const auto FloatToShort = [](float a) {
868 if (a > 0) {
869 return static_cast<s32>(a * HID_JOYSTICK_MAX);
870 }
871 return static_cast<s32>(a * HID_JOYSTICK_MIN);
872 };
873
874 const AnalogStickState stick{ 866 const AnalogStickState stick{
875 .x = FloatToShort(controller.stick_values[index].x.value), 867 .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
876 .y = FloatToShort(controller.stick_values[index].y.value), 868 .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
877 }; 869 };
878 870
879 switch (index) { 871 switch (index) {
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index d9da1e600..884eba001 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -74,7 +74,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
74 R_UNLESS(!m_is_mapped, ResultInvalidState); 74 R_UNLESS(!m_is_mapped, ResultInvalidState);
75 75
76 // Map the memory. 76 // Map the memory.
77 R_TRY(kernel.CurrentProcess()->PageTable().MapPages( 77 R_TRY(kernel.CurrentProcess()->PageTable().MapPageGroup(
78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); 78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
79 79
80 // Mark ourselves as mapped. 80 // Mark ourselves as mapped.
@@ -91,8 +91,8 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {
91 KScopedLightLock lk(m_lock); 91 KScopedLightLock lk(m_lock);
92 92
93 // Unmap the memory. 93 // Unmap the memory.
94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group, 94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPageGroup(address, *m_page_group,
95 KMemoryState::CodeOut)); 95 KMemoryState::CodeOut));
96 96
97 // Mark ourselves as unmapped. 97 // Mark ourselves as unmapped.
98 m_is_mapped = false; 98 m_is_mapped = false;
@@ -125,8 +125,8 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
125 } 125 }
126 126
127 // Map the memory. 127 // Map the memory.
128 R_TRY( 128 R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode,
129 m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm)); 129 k_perm));
130 130
131 // Mark ourselves as mapped. 131 // Mark ourselves as mapped.
132 m_is_owner_mapped = true; 132 m_is_owner_mapped = true;
@@ -142,7 +142,7 @@ Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
142 KScopedLightLock lk(m_lock); 142 KScopedLightLock lk(m_lock);
143 143
144 // Unmap the memory. 144 // Unmap the memory.
145 R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode)); 145 R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode));
146 146
147 // Mark ourselves as unmapped. 147 // Mark ourselves as unmapped.
148 m_is_owner_mapped = false; 148 m_is_owner_mapped = false;
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_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 9c7ac22dc..2e13d5d0d 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -435,6 +435,9 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
435 KPageGroup pg{m_kernel, m_block_info_manager}; 435 KPageGroup pg{m_kernel, m_block_info_manager};
436 AddRegionToPages(src_address, num_pages, pg); 436 AddRegionToPages(src_address, num_pages, pg);
437 437
438 // We're going to perform an update, so create a helper.
439 KScopedPageTableUpdater updater(this);
440
438 // Reprotect the source as kernel-read/not mapped. 441 // Reprotect the source as kernel-read/not mapped.
439 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | 442 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
440 KMemoryPermission::NotMapped); 443 KMemoryPermission::NotMapped);
@@ -447,7 +450,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
447 }); 450 });
448 451
449 // Map the alias pages. 452 // Map the alias pages.
450 R_TRY(MapPages(dst_address, pg, new_perm)); 453 const KPageProperties dst_properties = {new_perm, false, false,
454 DisableMergeAttribute::DisableHead};
455 R_TRY(
456 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
451 457
452 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on 458 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
453 // failure. 459 // failure.
@@ -1881,7 +1887,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1881 R_SUCCEED(); 1887 R_SUCCEED();
1882} 1888}
1883 1889
1884Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1890Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1891 size_t size) {
1885 // Lock the table. 1892 // Lock the table.
1886 KScopedLightLock lk(m_general_lock); 1893 KScopedLightLock lk(m_general_lock);
1887 1894
@@ -1902,53 +1909,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)
1902 KMemoryAttribute::None)); 1909 KMemoryAttribute::None));
1903 1910
1904 // Create an update allocator for the source. 1911 // Create an update allocator for the source.
1905 Result src_allocator_result{ResultSuccess}; 1912 Result src_allocator_result;
1906 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 1913 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1907 m_memory_block_slab_manager, 1914 m_memory_block_slab_manager,
1908 num_src_allocator_blocks); 1915 num_src_allocator_blocks);
1909 R_TRY(src_allocator_result); 1916 R_TRY(src_allocator_result);
1910 1917
1911 // Create an update allocator for the destination. 1918 // Create an update allocator for the destination.
1912 Result dst_allocator_result{ResultSuccess}; 1919 Result dst_allocator_result;
1913 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 1920 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1914 m_memory_block_slab_manager, 1921 m_memory_block_slab_manager,
1915 num_dst_allocator_blocks); 1922 num_dst_allocator_blocks);
1916 R_TRY(dst_allocator_result); 1923 R_TRY(dst_allocator_result);
1917 1924
1918 // Map the memory. 1925 // Map the memory.
1919 KPageGroup page_linked_list{m_kernel, m_block_info_manager};
1920 const size_t num_pages{size / PageSize};
1921 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1922 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1923 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1924
1925 AddRegionToPages(src_address, num_pages, page_linked_list);
1926 { 1926 {
1927 // Determine the number of pages being operated on.
1928 const size_t num_pages = size / PageSize;
1929
1930 // Create page groups for the memory being unmapped.
1931 KPageGroup pg{m_kernel, m_block_info_manager};
1932
1933 // Create the page group representing the source.
1934 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1935
1936 // We're going to perform an update, so create a helper.
1937 KScopedPageTableUpdater updater(this);
1938
1927 // Reprotect the source as kernel-read/not mapped. 1939 // Reprotect the source as kernel-read/not mapped.
1928 auto block_guard = detail::ScopeExit([&] { 1940 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1929 Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 1941 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1930 OperationType::ChangePermissions); 1942 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1931 }); 1943 const KPageProperties src_properties = {new_src_perm, false, false,
1932 R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); 1944 DisableMergeAttribute::DisableHeadBodyTail};
1933 R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); 1945 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
1946 OperationType::ChangePermissions));
1934 1947
1935 block_guard.Cancel(); 1948 // Ensure that we unprotect the source pages on failure.
1936 } 1949 ON_RESULT_FAILURE {
1950 const KPageProperties unprotect_properties = {
1951 KMemoryPermission::UserReadWrite, false, false,
1952 DisableMergeAttribute::EnableHeadBodyTail};
1953 ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm,
1954 OperationType::ChangePermissions) == ResultSuccess);
1955 };
1937 1956
1938 // Apply the memory block updates. 1957 // Map the alias pages.
1939 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 1958 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
1940 new_src_perm, new_src_attr, 1959 DisableMergeAttribute::DisableHead};
1941 KMemoryBlockDisableMergeAttribute::Locked, 1960 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
1942 KMemoryBlockDisableMergeAttribute::None); 1961 false));
1943 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 1962
1944 KMemoryState::Stack, KMemoryPermission::UserReadWrite, 1963 // Apply the memory block updates.
1945 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, 1964 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1946 KMemoryBlockDisableMergeAttribute::None); 1965 src_state, new_src_perm, new_src_attr,
1966 KMemoryBlockDisableMergeAttribute::Locked,
1967 KMemoryBlockDisableMergeAttribute::None);
1968 m_memory_block_manager.Update(
1969 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
1970 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1971 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
1972 }
1947 1973
1948 R_SUCCEED(); 1974 R_SUCCEED();
1949} 1975}
1950 1976
1951Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1977Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1978 size_t size) {
1952 // Lock the table. 1979 // Lock the table.
1953 KScopedLightLock lk(m_general_lock); 1980 KScopedLightLock lk(m_general_lock);
1954 1981
@@ -1970,108 +1997,208 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size
1970 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); 1997 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
1971 1998
1972 // Create an update allocator for the source. 1999 // Create an update allocator for the source.
1973 Result src_allocator_result{ResultSuccess}; 2000 Result src_allocator_result;
1974 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 2001 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1975 m_memory_block_slab_manager, 2002 m_memory_block_slab_manager,
1976 num_src_allocator_blocks); 2003 num_src_allocator_blocks);
1977 R_TRY(src_allocator_result); 2004 R_TRY(src_allocator_result);
1978 2005
1979 // Create an update allocator for the destination. 2006 // Create an update allocator for the destination.
1980 Result dst_allocator_result{ResultSuccess}; 2007 Result dst_allocator_result;
1981 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 2008 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1982 m_memory_block_slab_manager, 2009 m_memory_block_slab_manager,
1983 num_dst_allocator_blocks); 2010 num_dst_allocator_blocks);
1984 R_TRY(dst_allocator_result); 2011 R_TRY(dst_allocator_result);
1985 2012
1986 KPageGroup src_pages{m_kernel, m_block_info_manager}; 2013 // Unmap the memory.
1987 KPageGroup dst_pages{m_kernel, m_block_info_manager}; 2014 {
1988 const size_t num_pages{size / PageSize}; 2015 // Determine the number of pages being operated on.
2016 const size_t num_pages = size / PageSize;
1989 2017
1990 AddRegionToPages(src_address, num_pages, src_pages); 2018 // Create page groups for the memory being unmapped.
1991 AddRegionToPages(dst_address, num_pages, dst_pages); 2019 KPageGroup pg{m_kernel, m_block_info_manager};
1992 2020
1993 R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion); 2021 // Create the page group representing the destination.
2022 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
1994 2023
1995 { 2024 // Ensure the page group is the valid for the source.
1996 auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); 2025 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
1997 2026
1998 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2027 // We're going to perform an update, so create a helper.
1999 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 2028 KScopedPageTableUpdater updater(this);
2000 OperationType::ChangePermissions));
2001 2029
2002 block_guard.Cancel(); 2030 // Unmap the aliased copy of the pages.
2003 } 2031 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
2032 DisableMergeAttribute::None};
2033 R_TRY(
2034 this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap));
2035
2036 // Ensure that we re-map the aliased pages on failure.
2037 ON_RESULT_FAILURE {
2038 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
2039 };
2004 2040
2005 // Apply the memory block updates. 2041 // Try to set the permissions for the source pages back to what they should be.
2006 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 2042 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
2007 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, 2043 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
2008 KMemoryBlockDisableMergeAttribute::None, 2044 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
2009 KMemoryBlockDisableMergeAttribute::Locked); 2045 OperationType::ChangePermissions));
2010 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 2046
2011 KMemoryState::None, KMemoryPermission::None, 2047 // Apply the memory block updates.
2012 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, 2048 m_memory_block_manager.Update(
2013 KMemoryBlockDisableMergeAttribute::Normal); 2049 std::addressof(src_allocator), src_address, num_pages, src_state,
2050 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2051 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
2052 m_memory_block_manager.Update(
2053 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
2054 KMemoryPermission::None, KMemoryAttribute::None,
2055 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
2056 }
2014 2057
2015 R_SUCCEED(); 2058 R_SUCCEED();
2016} 2059}
2017 2060
2018Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, 2061Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
2019 KMemoryPermission perm) { 2062 size_t num_pages, KMemoryPermission perm) {
2020 ASSERT(this->IsLockedByCurrentThread()); 2063 ASSERT(this->IsLockedByCurrentThread());
2021 2064
2022 VAddr cur_addr{addr}; 2065 // Create a page group to hold the pages we allocate.
2066 KPageGroup pg{m_kernel, m_block_info_manager};
2023 2067
2024 for (const auto& node : page_linked_list) { 2068 // Allocate the pages.
2025 if (const auto result{ 2069 R_TRY(
2026 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 2070 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
2027 result.IsError()) {
2028 const size_t num_pages{(addr - cur_addr) / PageSize};
2029 2071
2030 ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) 2072 // Ensure that the page group is closed when we're done working with it.
2031 .IsSuccess()); 2073 SCOPE_EXIT({ pg.Close(); });
2032 2074
2033 R_RETURN(result); 2075 // Clear all pages.
2076 for (const auto& it : pg) {
2077 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2078 it.GetSize());
2079 }
2080
2081 // Map the pages.
2082 R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup));
2083}
2084
2085Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
2086 const KPageGroup& pg, const KPageProperties properties,
2087 bool reuse_ll) {
2088 ASSERT(this->IsLockedByCurrentThread());
2089
2090 // Note the current address, so that we can iterate.
2091 const KProcessAddress start_address = address;
2092 KProcessAddress cur_address = address;
2093
2094 // Ensure that we clean up on failure.
2095 ON_RESULT_FAILURE {
2096 ASSERT(!reuse_ll);
2097 if (cur_address != start_address) {
2098 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2099 DisableMergeAttribute::None};
2100 ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize,
2101 unmap_properties.perm, OperationType::Unmap) == ResultSuccess);
2034 } 2102 }
2103 };
2035 2104
2036 cur_addr += node.GetNumPages() * PageSize; 2105 // Iterate, mapping all pages in the group.
2106 for (const auto& block : pg) {
2107 // Map and advance.
2108 const KPageProperties cur_properties =
2109 (cur_address == start_address)
2110 ? properties
2111 : KPageProperties{properties.perm, properties.io, properties.uncached,
2112 DisableMergeAttribute::None};
2113 this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map,
2114 block.GetAddress());
2115 cur_address += block.GetSize();
2037 } 2116 }
2038 2117
2118 // We succeeded!
2039 R_SUCCEED(); 2119 R_SUCCEED();
2040} 2120}
2041 2121
2042Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, 2122void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
2043 KMemoryPermission perm) { 2123 const KPageGroup& pg) {
2044 // Check that the map is in range. 2124 ASSERT(this->IsLockedByCurrentThread());
2045 const size_t num_pages{page_linked_list.GetNumPages()};
2046 const size_t size{num_pages * PageSize};
2047 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2048 2125
2049 // Lock the table. 2126 // Note the current address, so that we can iterate.
2050 KScopedLightLock lk(m_general_lock); 2127 const KProcessAddress start_address = address;
2128 const KProcessAddress last_address = start_address + size - 1;
2129 const KProcessAddress end_address = last_address + 1;
2051 2130
2052 // Check the memory state. 2131 // Iterate over the memory.
2053 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 2132 auto pg_it = pg.begin();
2054 KMemoryPermission::None, KMemoryPermission::None, 2133 ASSERT(pg_it != pg.end());
2055 KMemoryAttribute::None, KMemoryAttribute::None));
2056 2134
2057 // Create an update allocator. 2135 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
2058 Result allocator_result{ResultSuccess}; 2136 size_t pg_pages = pg_it->GetNumPages();
2059 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2060 m_memory_block_slab_manager);
2061 2137
2062 // Map the pages. 2138 auto it = m_memory_block_manager.FindIterator(start_address);
2063 R_TRY(MapPages(address, page_linked_list, perm)); 2139 while (true) {
2140 // Check that the iterator is valid.
2141 ASSERT(it != m_memory_block_manager.end());
2064 2142
2065 // Update the blocks. 2143 // Get the memory info.
2066 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, 2144 const KMemoryInfo info = it->GetMemoryInfo();
2067 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2068 KMemoryBlockDisableMergeAttribute::None);
2069 2145
2070 R_SUCCEED(); 2146 // Determine the range to map.
2147 KProcessAddress map_address = std::max<VAddr>(info.GetAddress(), start_address);
2148 const KProcessAddress map_end_address = std::min<VAddr>(info.GetEndAddress(), end_address);
2149 ASSERT(map_end_address != map_address);
2150
2151 // Determine if we should disable head merge.
2152 const bool disable_head_merge =
2153 info.GetAddress() >= start_address &&
2154 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
2155 const KPageProperties map_properties = {
2156 info.GetPermission(), false, false,
2157 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
2158
2159 // While we have pages to map, map them.
2160 size_t map_pages = (map_end_address - map_address) / PageSize;
2161 while (map_pages > 0) {
2162 // Check if we're at the end of the physical block.
2163 if (pg_pages == 0) {
2164 // Ensure there are more pages to map.
2165 ASSERT(pg_it != pg.end());
2166
2167 // Advance our physical block.
2168 ++pg_it;
2169 pg_phys_addr = pg_it->GetAddress();
2170 pg_pages = pg_it->GetNumPages();
2171 }
2172
2173 // Map whatever we can.
2174 const size_t cur_pages = std::min(pg_pages, map_pages);
2175 ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map,
2176 pg_phys_addr) == ResultSuccess);
2177
2178 // Advance.
2179 map_address += cur_pages * PageSize;
2180 map_pages -= cur_pages;
2181
2182 pg_phys_addr += cur_pages * PageSize;
2183 pg_pages -= cur_pages;
2184 }
2185
2186 // Check if we're done.
2187 if (last_address <= info.GetLastAddress()) {
2188 break;
2189 }
2190
2191 // Advance.
2192 ++it;
2193 }
2194
2195 // Check that we re-mapped precisely the page group.
2196 ASSERT((++pg_it) == pg.end());
2071} 2197}
2072 2198
2073Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 2199Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2074 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 2200 KPhysicalAddress phys_addr, bool is_pa_valid,
2201 KProcessAddress region_start, size_t region_num_pages,
2075 KMemoryState state, KMemoryPermission perm) { 2202 KMemoryState state, KMemoryPermission perm) {
2076 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); 2203 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2077 2204
@@ -2084,26 +2211,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2084 KScopedLightLock lk(m_general_lock); 2211 KScopedLightLock lk(m_general_lock);
2085 2212
2086 // Find a random address to map at. 2213 // Find a random address to map at.
2087 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, 2214 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2088 this->GetNumGuardPages()); 2215 0, this->GetNumGuardPages());
2089 R_UNLESS(addr != 0, ResultOutOfMemory); 2216 R_UNLESS(addr != 0, ResultOutOfMemory);
2090 ASSERT(Common::IsAligned(addr, alignment)); 2217 ASSERT(Common::IsAligned(addr, alignment));
2091 ASSERT(this->CanContain(addr, num_pages * PageSize, state)); 2218 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2092 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, 2219 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2093 KMemoryPermission::None, KMemoryPermission::None, 2220 KMemoryPermission::None, KMemoryPermission::None,
2094 KMemoryAttribute::None, KMemoryAttribute::None) 2221 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2095 .IsSuccess());
2096 2222
2097 // Create an update allocator. 2223 // Create an update allocator.
2098 Result allocator_result{ResultSuccess}; 2224 Result allocator_result;
2099 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2225 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2100 m_memory_block_slab_manager); 2226 m_memory_block_slab_manager);
2227 R_TRY(allocator_result);
2228
2229 // We're going to perform an update, so create a helper.
2230 KScopedPageTableUpdater updater(this);
2101 2231
2102 // Perform mapping operation. 2232 // Perform mapping operation.
2103 if (is_pa_valid) { 2233 if (is_pa_valid) {
2104 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); 2234 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2235 R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));
2105 } else { 2236 } else {
2106 UNIMPLEMENTED(); 2237 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2107 } 2238 }
2108 2239
2109 // Update the blocks. 2240 // Update the blocks.
@@ -2116,28 +2247,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2116 R_SUCCEED(); 2247 R_SUCCEED();
2117} 2248}
2118 2249
2119Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { 2250Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2120 ASSERT(this->IsLockedByCurrentThread()); 2251 KMemoryPermission perm) {
2252 // Check that the map is in range.
2253 const size_t size = num_pages * PageSize;
2254 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2121 2255
2122 VAddr cur_addr{addr}; 2256 // Lock the table.
2257 KScopedLightLock lk(m_general_lock);
2123 2258
2124 for (const auto& node : page_linked_list) { 2259 // Check the memory state.
2125 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, 2260 size_t num_allocator_blocks;
2126 OperationType::Unmap)}; 2261 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2127 result.IsError()) { 2262 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2128 R_RETURN(result); 2263 KMemoryPermission::None, KMemoryAttribute::None,
2129 } 2264 KMemoryAttribute::None));
2130 2265
2131 cur_addr += node.GetNumPages() * PageSize; 2266 // Create an update allocator.
2132 } 2267 Result allocator_result;
2268 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2269 m_memory_block_slab_manager, num_allocator_blocks);
2270 R_TRY(allocator_result);
2271
2272 // We're going to perform an update, so create a helper.
2273 KScopedPageTableUpdater updater(this);
2274
2275 // Map the pages.
2276 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2277
2278 // Update the blocks.
2279 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2280 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2281 KMemoryBlockDisableMergeAttribute::None);
2133 2282
2134 R_SUCCEED(); 2283 R_SUCCEED();
2135} 2284}
2136 2285
2137Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { 2286Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2138 // Check that the unmap is in range. 2287 // Check that the unmap is in range.
2139 const size_t num_pages{page_linked_list.GetNumPages()}; 2288 const size_t size = num_pages * PageSize;
2140 const size_t size{num_pages * PageSize};
2141 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2289 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2142 2290
2143 // Lock the table. 2291 // Lock the table.
@@ -2151,13 +2299,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2151 KMemoryAttribute::None)); 2299 KMemoryAttribute::None));
2152 2300
2153 // Create an update allocator. 2301 // Create an update allocator.
2154 Result allocator_result{ResultSuccess}; 2302 Result allocator_result;
2155 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2303 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2156 m_memory_block_slab_manager, num_allocator_blocks); 2304 m_memory_block_slab_manager, num_allocator_blocks);
2157 R_TRY(allocator_result); 2305 R_TRY(allocator_result);
2158 2306
2307 // We're going to perform an update, so create a helper.
2308 KScopedPageTableUpdater updater(this);
2309
2159 // Perform the unmap. 2310 // Perform the unmap.
2160 R_TRY(UnmapPages(address, page_linked_list)); 2311 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2312 DisableMergeAttribute::None};
2313 R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));
2161 2314
2162 // Update the blocks. 2315 // Update the blocks.
2163 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2316 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2168,29 +2321,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2168 R_SUCCEED(); 2321 R_SUCCEED();
2169} 2322}
2170 2323
2171Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { 2324Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2172 // Check that the unmap is in range. 2325 KProcessAddress region_start, size_t region_num_pages,
2326 KMemoryState state, KMemoryPermission perm) {
2327 ASSERT(!this->IsLockedByCurrentThread());
2328
2329 // Ensure this is a valid map request.
2330 const size_t num_pages = pg.GetNumPages();
2331 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2332 ResultInvalidCurrentMemory);
2333 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2334
2335 // Lock the table.
2336 KScopedLightLock lk(m_general_lock);
2337
2338 // Find a random address to map at.
2339 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2340 0, this->GetNumGuardPages());
2341 R_UNLESS(addr != 0, ResultOutOfMemory);
2342 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2343 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2344 KMemoryPermission::None, KMemoryPermission::None,
2345 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2346
2347 // Create an update allocator.
2348 Result allocator_result;
2349 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2350 m_memory_block_slab_manager);
2351 R_TRY(allocator_result);
2352
2353 // We're going to perform an update, so create a helper.
2354 KScopedPageTableUpdater updater(this);
2355
2356 // Perform mapping operation.
2357 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2358 DisableMergeAttribute::DisableHead};
2359 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2360
2361 // Update the blocks.
2362 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2363 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2364 KMemoryBlockDisableMergeAttribute::None);
2365
2366 // We successfully mapped the pages.
2367 *out_addr = addr;
2368 R_SUCCEED();
2369}
2370
2371Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2372 KMemoryPermission perm) {
2373 ASSERT(!this->IsLockedByCurrentThread());
2374
2375 // Ensure this is a valid map request.
2376 const size_t num_pages = pg.GetNumPages();
2173 const size_t size = num_pages * PageSize; 2377 const size_t size = num_pages * PageSize;
2174 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2378 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2175 2379
2176 // Lock the table. 2380 // Lock the table.
2177 KScopedLightLock lk(m_general_lock); 2381 KScopedLightLock lk(m_general_lock);
2178 2382
2179 // Check the memory state. 2383 // Check if state allows us to map.
2180 size_t num_allocator_blocks{}; 2384 size_t num_allocator_blocks;
2385 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2386 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2387 KMemoryPermission::None, KMemoryAttribute::None,
2388 KMemoryAttribute::None));
2389
2390 // Create an update allocator.
2391 Result allocator_result;
2392 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2393 m_memory_block_slab_manager, num_allocator_blocks);
2394 R_TRY(allocator_result);
2395
2396 // We're going to perform an update, so create a helper.
2397 KScopedPageTableUpdater updater(this);
2398
2399 // Perform mapping operation.
2400 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2401 DisableMergeAttribute::DisableHead};
2402 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2403
2404 // Update the blocks.
2405 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2406 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2407 KMemoryBlockDisableMergeAttribute::None);
2408
2409 // We successfully mapped the pages.
2410 R_SUCCEED();
2411}
2412
2413Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2414 KMemoryState state) {
2415 ASSERT(!this->IsLockedByCurrentThread());
2416
2417 // Ensure this is a valid unmap request.
2418 const size_t num_pages = pg.GetNumPages();
2419 const size_t size = num_pages * PageSize;
2420 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2421
2422 // Lock the table.
2423 KScopedLightLock lk(m_general_lock);
2424
2425 // Check if state allows us to unmap.
2426 size_t num_allocator_blocks;
2181 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, 2427 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2182 KMemoryState::All, state, KMemoryPermission::None, 2428 KMemoryState::All, state, KMemoryPermission::None,
2183 KMemoryPermission::None, KMemoryAttribute::All, 2429 KMemoryPermission::None, KMemoryAttribute::All,
2184 KMemoryAttribute::None)); 2430 KMemoryAttribute::None));
2185 2431
2432 // Check that the page group is valid.
2433 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2434
2186 // Create an update allocator. 2435 // Create an update allocator.
2187 Result allocator_result{ResultSuccess}; 2436 Result allocator_result;
2188 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2437 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2189 m_memory_block_slab_manager, num_allocator_blocks); 2438 m_memory_block_slab_manager, num_allocator_blocks);
2190 R_TRY(allocator_result); 2439 R_TRY(allocator_result);
2191 2440
2192 // Perform the unmap. 2441 // We're going to perform an update, so create a helper.
2193 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2442 KScopedPageTableUpdater updater(this);
2443
2444 // Perform unmapping operation.
2445 const KPageProperties properties = {KMemoryPermission::None, false, false,
2446 DisableMergeAttribute::None};
2447 R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));
2194 2448
2195 // Update the blocks. 2449 // Update the blocks.
2196 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2450 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2550,54 +2804,6 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2550 } 2804 }
2551} 2805}
2552 2806
2553ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align,
2554 bool is_map_only, VAddr region_start,
2555 size_t region_num_pages, KMemoryState state,
2556 KMemoryPermission perm, PAddr map_addr) {
2557 KScopedLightLock lk(m_general_lock);
2558
2559 R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state),
2560 ResultInvalidCurrentMemory);
2561 R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory);
2562 const VAddr addr{
2563 AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
2564 R_UNLESS(addr, ResultOutOfMemory);
2565
2566 // Create an update allocator.
2567 Result allocator_result{ResultSuccess};
2568 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2569 m_memory_block_slab_manager);
2570
2571 if (is_map_only) {
2572 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
2573 } else {
2574 // Create a page group tohold the pages we allocate.
2575 KPageGroup pg{m_kernel, m_block_info_manager};
2576
2577 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2578 &pg, needed_num_pages,
2579 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2580
2581 // Ensure that the page group is closed when we're done working with it.
2582 SCOPE_EXIT({ pg.Close(); });
2583
2584 // Clear all pages.
2585 for (const auto& it : pg) {
2586 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()),
2587 m_heap_fill_value, it.GetSize());
2588 }
2589
2590 R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup));
2591 }
2592
2593 // Update the blocks.
2594 m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm,
2595 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2596 KMemoryBlockDisableMergeAttribute::None);
2597
2598 return addr;
2599}
2600
2601Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 2807Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
2602 KMemoryPermission perm, bool is_aligned, 2808 KMemoryPermission perm, bool is_aligned,
2603 bool check_heap) { 2809 bool check_heap) {
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 0a454b05b..367dab613 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -24,12 +24,36 @@ class System;
24 24
25namespace Kernel { 25namespace Kernel {
26 26
27enum class DisableMergeAttribute : u8 {
28 None = (0U << 0),
29 DisableHead = (1U << 0),
30 DisableHeadAndBody = (1U << 1),
31 EnableHeadAndBody = (1U << 2),
32 DisableTail = (1U << 3),
33 EnableTail = (1U << 4),
34 EnableAndMergeHeadBodyTail = (1U << 5),
35 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
36 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
37};
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
27class KBlockInfoManager; 48class KBlockInfoManager;
28class KMemoryBlockManager; 49class KMemoryBlockManager;
29class KResourceLimit; 50class KResourceLimit;
30class KSystemResource; 51class KSystemResource;
31 52
32class KPageTable final { 53class KPageTable final {
54protected:
55 struct PageLinkedList;
56
33public: 57public:
34 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; 58 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
35 59
@@ -57,27 +81,12 @@ public:
57 Result UnmapPhysicalMemory(VAddr addr, size_t size); 81 Result UnmapPhysicalMemory(VAddr addr, size_t size);
58 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 82 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
59 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 83 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
60 Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
61 KMemoryPermission perm);
62 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
63 KMemoryState state, KMemoryPermission perm) {
64 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
65 this->GetRegionAddress(state),
66 this->GetRegionSize(state) / PageSize, state, perm));
67 }
68 Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
69 Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
70 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); 84 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
71 KMemoryInfo QueryInfo(VAddr addr); 85 KMemoryInfo QueryInfo(VAddr addr);
72 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); 86 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
73 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); 87 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
74 Result SetMaxHeapSize(size_t size); 88 Result SetMaxHeapSize(size_t size);
75 Result SetHeapSize(VAddr* out, size_t size); 89 Result SetHeapSize(VAddr* out, size_t size);
76 ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
77 VAddr region_start, size_t region_num_pages,
78 KMemoryState state, KMemoryPermission perm,
79 PAddr map_addr = 0);
80
81 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 90 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
82 KMemoryPermission perm, bool is_aligned, bool check_heap); 91 KMemoryPermission perm, bool is_aligned, bool check_heap);
83 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); 92 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
@@ -113,6 +122,40 @@ public:
113 122
114 bool CanContain(VAddr addr, size_t size, KMemoryState state) const; 123 bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
115 124
125 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
126 KPhysicalAddress phys_addr, KProcessAddress region_start,
127 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
128 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
129 region_num_pages, state, perm));
130 }
131
132 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
133 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
134 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
135 this->GetRegionAddress(state),
136 this->GetRegionSize(state) / PageSize, state, perm));
137 }
138
139 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
140 KMemoryPermission perm) {
141 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
142 this->GetRegionAddress(state),
143 this->GetRegionSize(state) / PageSize, state, perm));
144 }
145
146 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
147 KMemoryPermission perm);
148 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
149
150 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
151 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
152 KMemoryPermission perm);
153 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
154 KMemoryPermission perm);
155 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
156 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
157 const KPageGroup& pg);
158
116protected: 159protected:
117 struct PageLinkedList { 160 struct PageLinkedList {
118 private: 161 private:
@@ -166,11 +209,9 @@ private:
166 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = 209 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
167 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; 210 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
168 211
169 Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); 212 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
170 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 213 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
171 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 214 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
172 KMemoryState state, KMemoryPermission perm);
173 Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
174 bool IsRegionContiguous(VAddr addr, u64 size) const; 215 bool IsRegionContiguous(VAddr addr, u64 size) const;
175 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); 216 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
176 KMemoryInfo QueryInfoImpl(VAddr addr); 217 KMemoryInfo QueryInfoImpl(VAddr addr);
@@ -265,6 +306,11 @@ private:
265 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, 306 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
266 size_t size, KMemoryPermission prot_perm); 307 size_t size, KMemoryPermission prot_perm);
267 308
309 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
310 size_t num_pages, KMemoryPermission perm);
311 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
312 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
313
268 mutable KLightLock m_general_lock; 314 mutable KLightLock m_general_lock;
269 mutable KLightLock m_map_physical_memory_lock; 315 mutable KLightLock m_map_physical_memory_lock;
270 316
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index a1abf5d68..e201bb0cd 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -417,9 +417,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
417} 417}
418 418
419void KProcess::Run(s32 main_thread_priority, u64 stack_size) { 419void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
420 AllocateMainThreadStack(stack_size); 420 ASSERT(AllocateMainThreadStack(stack_size) == ResultSuccess);
421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); 421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
422 resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
423 422
424 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; 423 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
425 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); 424 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -675,20 +674,31 @@ void KProcess::ChangeState(State new_state) {
675} 674}
676 675
677Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { 676Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
678 ASSERT(stack_size); 677 // Ensure that we haven't already allocated stack.
679 678 ASSERT(main_thread_stack_size == 0);
680 // The kernel always ensures that the given stack size is page aligned. 679
681 main_thread_stack_size = Common::AlignUp(stack_size, PageSize); 680 // Ensure that we're allocating a valid stack.
682 681 stack_size = Common::AlignUp(stack_size, PageSize);
683 const VAddr start{page_table.GetStackRegionStart()}; 682 // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory);
684 const std::size_t size{page_table.GetStackRegionEnd() - start}; 683 R_UNLESS(stack_size + image_size >= image_size, ResultOutOfMemory);
685 684
686 CASCADE_RESULT(main_thread_stack_top, 685 // Place a tentative reservation of memory for our new stack.
687 page_table.AllocateAndMapMemory( 686 KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax,
688 main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, 687 stack_size);
689 KMemoryState::Stack, KMemoryPermission::UserReadWrite)); 688 R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached);
689
690 // Allocate and map our stack.
691 if (stack_size) {
692 KProcessAddress stack_bottom;
693 R_TRY(page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize,
694 KMemoryState::Stack, KMemoryPermission::UserReadWrite));
695
696 main_thread_stack_top = stack_bottom + stack_size;
697 main_thread_stack_size = stack_size;
698 }
690 699
691 main_thread_stack_top += main_thread_stack_size; 700 // We succeeded! Commit our memory reservation.
701 mem_reservation.Commit();
692 702
693 R_SUCCEED(); 703 R_SUCCEED();
694} 704}
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 3cf2b5d91..df505edfe 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -94,15 +94,15 @@ Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t m
94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); 94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);
95 } 95 }
96 96
97 return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared, 97 return target_process.PageTable().MapPageGroup(address, *page_group, KMemoryState::Shared,
98 ConvertToKMemoryPermission(map_perm)); 98 ConvertToKMemoryPermission(map_perm));
99} 99}
100 100
101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { 101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
102 // Validate the size. 102 // Validate the size.
103 R_UNLESS(size == unmap_size, ResultInvalidSize); 103 R_UNLESS(size == unmap_size, ResultInvalidSize);
104 104
105 return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared); 105 return target_process.PageTable().UnmapPageGroup(address, *page_group, KMemoryState::Shared);
106} 106}
107 107
108} // namespace Kernel 108} // namespace Kernel
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/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index aca442196..67fa5d71c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1492,8 +1492,8 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p
1492 KMemoryAttribute::All, KMemoryAttribute::None)); 1492 KMemoryAttribute::All, KMemoryAttribute::None));
1493 1493
1494 // Map the group. 1494 // Map the group.
1495 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1495 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
1496 KMemoryPermission::UserReadWrite)); 1496 KMemoryPermission::UserReadWrite));
1497 1497
1498 return ResultSuccess; 1498 return ResultSuccess;
1499} 1499}
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4e605fae4..af9660b55 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -440,7 +440,7 @@ struct Memory::Impl {
440 } 440 }
441 441
442 if (Settings::IsFastmemEnabled()) { 442 if (Settings::IsFastmemEnabled()) {
443 const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; 443 const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
444 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 444 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
445 } 445 }
446 446
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 4159e5717..3775e2d35 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -86,6 +86,7 @@ DriverResult JoyconDriver::InitializeDevice() {
86 86
87 // Get fixed joycon info 87 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version); 88 generic_protocol->GetVersionNumber(version);
89 generic_protocol->SetLowPowerMode(false);
89 generic_protocol->GetColor(color); 90 generic_protocol->GetColor(color);
90 if (handle_device_type == ControllerType::Pro) { 91 if (handle_device_type == ControllerType::Pro) {
91 // Some 3rd party controllers aren't pro controllers 92 // Some 3rd party controllers aren't pro controllers
@@ -324,6 +325,8 @@ DriverResult JoyconDriver::SetPollingMode() {
324 if (result != DriverResult::Success) { 325 if (result != DriverResult::Success) {
325 LOG_ERROR(Input, "Error enabling active mode"); 326 LOG_ERROR(Input, "Error enabling active mode");
326 } 327 }
328 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed();
327 330
328 disable_input_thread = false; 331 disable_input_thread = false;
329 return result; 332 return result;
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
index 52bb8b61a..63cfb1369 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -19,6 +19,17 @@ DriverResult GenericProtocol::EnableActiveMode() {
19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ); 19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
20} 20}
21 21
22DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
23 ScopedSetBlocking sb(this);
24 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
25 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
26}
27
28DriverResult GenericProtocol::TriggersElapsed() {
29 ScopedSetBlocking sb(this);
30 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
31}
32
22DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { 33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
23 ScopedSetBlocking sb(this); 34 ScopedSetBlocking sb(this);
24 std::vector<u8> output; 35 std::vector<u8> output;
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h
index 239bb7dbf..424831e81 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.h
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.h
@@ -25,6 +25,12 @@ public:
25 /// Enables active mode. This mode will return the current status every 5-15ms 25 /// Enables active mode. This mode will return the current status every 5-15ms
26 DriverResult EnableActiveMode(); 26 DriverResult EnableActiveMode();
27 27
28 /// Enables or disables the low power mode
29 DriverResult SetLowPowerMode(bool enable);
30
31 /// Unknown function used by the switch
32 DriverResult TriggersElapsed();
33
28 /** 34 /**
29 * Sends a request to obtain the joycon firmware and mac from handle 35 * Sends a request to obtain the joycon firmware and mac from handle
30 * @returns controller device info 36 * @returns controller device info
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index e2d47349f..182d2c15b 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -129,6 +129,7 @@ enum class SubCommand : u8 {
129 LOW_POWER_MODE = 0x08, 129 LOW_POWER_MODE = 0x08,
130 SPI_FLASH_READ = 0x10, 130 SPI_FLASH_READ = 0x10,
131 SPI_FLASH_WRITE = 0x11, 131 SPI_FLASH_WRITE = 0x11,
132 SPI_SECTOR_ERASE = 0x12,
132 RESET_MCU = 0x20, 133 RESET_MCU = 0x20,
133 SET_MCU_CONFIG = 0x21, 134 SET_MCU_CONFIG = 0x21,
134 SET_MCU_STATE = 0x22, 135 SET_MCU_STATE = 0x22,
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index f3a0b3419..096c23b07 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -11,6 +11,11 @@ namespace InputCommon {
11 11
12class Stick final : public Common::Input::InputDevice { 12class Stick final : public Common::Input::InputDevice {
13public: 13public:
14 // Some games such as EARTH DEFENSE FORCE: WORLD BROTHERS
15 // do not play nicely with the theoretical maximum range.
16 // Using a value one lower from the maximum emulates real stick behavior.
17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
18
14 using Button = std::unique_ptr<Common::Input::InputDevice>; 19 using Button = std::unique_ptr<Common::Input::InputDevice>;
15 20
16 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_, 21 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_,
@@ -196,7 +201,7 @@ public:
196 } 201 }
197 202
198 void UpdateStatus() { 203 void UpdateStatus() {
199 const float coef = modifier_status.value ? modifier_scale : 1.0f; 204 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
200 205
201 bool r = right_status; 206 bool r = right_status;
202 bool l = left_status; 207 bool l = left_status;
@@ -290,7 +295,7 @@ public:
290 if (down_status) { 295 if (down_status) {
291 --y; 296 --y;
292 } 297 }
293 const float coef = modifier_status.value ? modifier_scale : 1.0f; 298 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
294 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); 299 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
295 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); 300 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
296 return status; 301 return status;
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index fb5799c42..c898ce12f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -436,6 +436,10 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
436 if (info.type == TextureType::Buffer) { 436 if (info.type == TextureType::Buffer) {
437 lod = Id{}; 437 lod = Id{};
438 } 438 }
439 if (Sirit::ValidId(ms)) {
440 // This image is multisampled, lod must be implicit
441 lod = Id{};
442 }
439 const ImageOperands operands(offset, lod, ms); 443 const ImageOperands operands(offset, lod, ms);
440 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], 444 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
441 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 445 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index a0c155fdb..3b97721e1 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -35,6 +35,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
35 const spv::ImageFormat format{spv::ImageFormat::Unknown}; 35 const spv::ImageFormat format{spv::ImageFormat::Unknown};
36 const Id type{ctx.F32[1]}; 36 const Id type{ctx.F32[1]};
37 const bool depth{desc.is_depth}; 37 const bool depth{desc.is_depth};
38 const bool ms{desc.is_multisample};
38 switch (desc.type) { 39 switch (desc.type) {
39 case TextureType::Color1D: 40 case TextureType::Color1D:
40 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); 41 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
@@ -42,9 +43,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
42 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); 43 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
43 case TextureType::Color2D: 44 case TextureType::Color2D:
44 case TextureType::Color2DRect: 45 case TextureType::Color2DRect:
45 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); 46 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
46 case TextureType::ColorArray2D: 47 case TextureType::ColorArray2D:
47 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); 48 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
48 case TextureType::Color3D: 49 case TextureType::Color3D:
49 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); 50 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
50 case TextureType::ColorCube: 51 case TextureType::ColorCube:
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index f5c86fcb1..9718c6921 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -524,6 +524,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
524 524
525 const auto& cbuf{texture_inst.cbuf}; 525 const auto& cbuf{texture_inst.cbuf};
526 auto flags{inst->Flags<IR::TextureInstInfo>()}; 526 auto flags{inst->Flags<IR::TextureInstInfo>()};
527 bool is_multisample{false};
527 switch (inst->GetOpcode()) { 528 switch (inst->GetOpcode()) {
528 case IR::Opcode::ImageQueryDimensions: 529 case IR::Opcode::ImageQueryDimensions:
529 flags.type.Assign(ReadTextureType(env, cbuf)); 530 flags.type.Assign(ReadTextureType(env, cbuf));
@@ -538,6 +539,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
538 } 539 }
539 break; 540 break;
540 case IR::Opcode::ImageFetch: 541 case IR::Opcode::ImageFetch:
542 if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect ||
543 flags.type == TextureType::ColorArray2D) {
544 is_multisample = !inst->Arg(4).IsEmpty();
545 } else {
546 inst->SetArg(4, IR::U32{});
547 }
541 if (flags.type != TextureType::Color1D) { 548 if (flags.type != TextureType::Color1D) {
542 break; 549 break;
543 } 550 }
@@ -613,6 +620,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
613 index = descriptors.Add(TextureDescriptor{ 620 index = descriptors.Add(TextureDescriptor{
614 .type = flags.type, 621 .type = flags.type,
615 .is_depth = flags.is_depth != 0, 622 .is_depth = flags.is_depth != 0,
623 .is_multisample = is_multisample,
616 .has_secondary = cbuf.has_secondary, 624 .has_secondary = cbuf.has_secondary,
617 .cbuf_index = cbuf.index, 625 .cbuf_index = cbuf.index,
618 .cbuf_offset = cbuf.offset, 626 .cbuf_offset = cbuf.offset,
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index f93181e1e..d308db942 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -109,6 +109,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip
109struct TextureDescriptor { 109struct TextureDescriptor {
110 TextureType type; 110 TextureType type;
111 bool is_depth; 111 bool is_depth;
112 bool is_multisample;
112 bool has_secondary; 113 bool has_secondary;
113 u32 cbuf_index; 114 u32 cbuf_index;
114 u32 cbuf_offset; 115 u32 cbuf_offset;
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/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 3bcae3503..83924475b 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -6,7 +6,6 @@
6#include "common/alignment.h" 6#include "common/alignment.h"
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/device_memory.h" 10#include "core/device_memory.h"
12#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
@@ -46,11 +45,6 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
46 big_page_table_cpu.resize(big_page_table_size); 45 big_page_table_cpu.resize(big_page_table_size);
47 big_page_continous.resize(big_page_table_size / continous_bits, 0); 46 big_page_continous.resize(big_page_table_size / continous_bits, 0);
48 entries.resize(page_table_size / 32, 0); 47 entries.resize(page_table_size / 32, 0);
49 if (!Settings::IsGPULevelExtreme() && Settings::IsFastmemEnabled()) {
50 fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
51 } else {
52 fastmem_arena = nullptr;
53 }
54} 48}
55 49
56MemoryManager::~MemoryManager() = default; 50MemoryManager::~MemoryManager() = default;
@@ -360,7 +354,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
360 } 354 }
361} 355}
362 356
363template <bool is_safe, bool use_fastmem> 357template <bool is_safe>
364void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 358void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
365 [[maybe_unused]] VideoCommon::CacheType which) const { 359 [[maybe_unused]] VideoCommon::CacheType which) const {
366 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, 360 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
@@ -374,12 +368,8 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
374 if constexpr (is_safe) { 368 if constexpr (is_safe) {
375 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); 369 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
376 } 370 }
377 if constexpr (use_fastmem) { 371 u8* physical = memory.GetPointer(cpu_addr_base);
378 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); 372 std::memcpy(dest_buffer, physical, copy_amount);
379 } else {
380 u8* physical = memory.GetPointer(cpu_addr_base);
381 std::memcpy(dest_buffer, physical, copy_amount);
382 }
383 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 373 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
384 }; 374 };
385 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 375 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
@@ -388,15 +378,11 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
388 if constexpr (is_safe) { 378 if constexpr (is_safe) {
389 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); 379 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
390 } 380 }
391 if constexpr (use_fastmem) { 381 if (!IsBigPageContinous(page_index)) [[unlikely]] {
392 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); 382 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
393 } else { 383 } else {
394 if (!IsBigPageContinous(page_index)) [[unlikely]] { 384 u8* physical = memory.GetPointer(cpu_addr_base);
395 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); 385 std::memcpy(dest_buffer, physical, copy_amount);
396 } else {
397 u8* physical = memory.GetPointer(cpu_addr_base);
398 std::memcpy(dest_buffer, physical, copy_amount);
399 }
400 } 386 }
401 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 387 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
402 }; 388 };
@@ -410,20 +396,12 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
410 396
411void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 397void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
412 VideoCommon::CacheType which) const { 398 VideoCommon::CacheType which) const {
413 if (fastmem_arena) [[likely]] { 399 ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
414 ReadBlockImpl<true, true>(gpu_src_addr, dest_buffer, size, which);
415 return;
416 }
417 ReadBlockImpl<true, false>(gpu_src_addr, dest_buffer, size, which);
418} 400}
419 401
420void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, 402void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
421 const std::size_t size) const { 403 const std::size_t size) const {
422 if (fastmem_arena) [[likely]] { 404 ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
423 ReadBlockImpl<false, true>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
424 return;
425 }
426 ReadBlockImpl<false, false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
427} 405}
428 406
429template <bool is_safe> 407template <bool is_safe>
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 2936364f0..9ebfb6179 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -141,7 +141,7 @@ private:
141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, 141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; 142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
143 143
144 template <bool is_safe, bool use_fastmem> 144 template <bool is_safe>
145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
146 VideoCommon::CacheType which) const; 146 VideoCommon::CacheType which) const;
147 147
@@ -215,7 +215,6 @@ private:
215 215
216 std::vector<u64> big_page_continous; 216 std::vector<u64> big_page_continous;
217 std::vector<std::pair<VAddr, std::size_t>> page_stash{}; 217 std::vector<std::pair<VAddr, std::size_t>> page_stash{};
218 u8* fastmem_arena{};
219 218
220 constexpr static size_t continous_bits = 64; 219 constexpr static size_t continous_bits = 64;
221 220
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/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4301313cf..2aaefcc05 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -66,7 +66,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
66 66
67 web_tab->SetWebServiceConfigEnabled(enable_web_config); 67 web_tab->SetWebServiceConfigEnabled(enable_web_config);
68 hotkeys_tab->Populate(registry); 68 hotkeys_tab->Populate(registry);
69 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
70 69
71 input_tab->Initialize(input_subsystem); 70 input_tab->Initialize(input_subsystem);
72 71
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/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index d1b870c72..fb1292f07 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -89,7 +89,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " 89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); 90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
91 91
92 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
93 SetConfiguration(); 92 SetConfiguration();
94 UpdateUiDisplay(); 93 UpdateUiDisplay();
95 ConnectEvents(); 94 ConnectEvents();
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 93db47cfd..7e757eafd 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -66,8 +66,6 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
66 66
67 setFocusPolicy(Qt::ClickFocus); 67 setFocusPolicy(Qt::ClickFocus);
68 setWindowTitle(tr("Properties")); 68 setWindowTitle(tr("Properties"));
69 // remove Help question mark button from the title bar
70 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
71 69
72 addons_tab->SetTitleId(title_id); 70 addons_tab->SetTitleId(title_id);
73 71
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 1edc5f1f3..5a545aa70 100644
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/src/yuzu/configuration/configure_tas.cpp
@@ -17,7 +17,6 @@ ConfigureTasDialog::ConfigureTasDialog(QWidget* parent)
17 17
18 setFocusPolicy(Qt::ClickFocus); 18 setFocusPolicy(Qt::ClickFocus);
19 setWindowTitle(tr("TAS Configuration")); 19 setWindowTitle(tr("TAS Configuration"));
20 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
21 20
22 connect(ui->tas_path_button, &QToolButton::pressed, this, 21 connect(ui->tas_path_button, &QToolButton::pressed, this,
23 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); 22 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); });
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 19f3775a3..e2f55ebae 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -20,9 +20,8 @@ ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
20 setWindowTitle(tr("Controller P1")); 20 setWindowTitle(tr("Controller P1"));
21 resize(500, 350); 21 resize(500, 350);
22 setMinimumSize(500, 350); 22 setMinimumSize(500, 350);
23 // Remove the "?" button from the titlebar and enable the maximize button 23 // Enable the maximize button
24 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 24 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
25 Qt::WindowMaximizeButtonHint);
26 25
27 widget = new PlayerControlPreview(this); 26 widget = new PlayerControlPreview(this);
28 refreshConfiguration(); 27 refreshConfiguration();
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index d3e2d3c12..493ee0b17 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -49,9 +49,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Di
49 setObjectName(QStringLiteral("MicroProfile")); 49 setObjectName(QStringLiteral("MicroProfile"));
50 setWindowTitle(tr("&MicroProfile")); 50 setWindowTitle(tr("&MicroProfile"));
51 resize(1000, 600); 51 resize(1000, 600);
52 // Remove the "?" button from the titlebar and enable the maximize button 52 // Enable the maximize button
53 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 53 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
54 Qt::WindowMaximizeButtonHint);
55 54
56#if MICROPROFILE_ENABLED 55#if MICROPROFILE_ENABLED
57 56
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp
index 84ec4fe13..673bbaa83 100644
--- a/src/yuzu/install_dialog.cpp
+++ b/src/yuzu/install_dialog.cpp
@@ -46,7 +46,6 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
46 vbox_layout->addLayout(hbox_layout); 46 vbox_layout->addLayout(hbox_layout);
47 47
48 setLayout(vbox_layout); 48 setLayout(vbox_layout);
49 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
50 setWindowTitle(tr("Install Files to NAND")); 49 setWindowTitle(tr("Install Files to NAND"));
51} 50}
52 51
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 571eacf9f..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);
@@ -2758,8 +2753,7 @@ void GMainWindow::OnMenuInstallToNAND() {
2758 ui->action_Install_File_NAND->setEnabled(false); 2753 ui->action_Install_File_NAND->setEnabled(false);
2759 2754
2760 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); 2755 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);
2761 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & 2756 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
2762 ~Qt::WindowMaximizeButtonHint);
2763 install_progress->setAttribute(Qt::WA_DeleteOnClose, true); 2757 install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
2764 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); 2758 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
2765 install_progress->show(); 2759 install_progress->show();
@@ -3469,10 +3463,6 @@ void GMainWindow::OnToggleAdaptingFilter() {
3469 } else { 3463 } else {
3470 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); 3464 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
3471 } 3465 }
3472 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
3473 filter == Settings::ScalingFilter::Fsr) {
3474 filter = Settings::ScalingFilter::NearestNeighbor;
3475 }
3476 Settings::values.scaling_filter.SetValue(filter); 3466 Settings::values.scaling_filter.SetValue(filter);
3477 filter_status_button->setChecked(true); 3467 filter_status_button->setChecked(true);
3478 UpdateFilterText(); 3468 UpdateFilterText();
@@ -4456,6 +4446,11 @@ int main(int argc, char* argv[]) {
4456 } 4446 }
4457#endif 4447#endif
4458 4448
4449#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
4450 // Disables the "?" button on all dialogs. Disabled by default on Qt6.
4451 QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
4452#endif
4453
4459 // Enables the core to make the qt created contexts current on std::threads 4454 // Enables the core to make the qt created contexts current on std::threads
4460 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4455 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4461 QApplication app(argc, argv); 4456 QApplication app(argc, argv);
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp
index bbb370595..5f6a9c193 100644
--- a/src/yuzu/util/limitable_input_dialog.cpp
+++ b/src/yuzu/util/limitable_input_dialog.cpp
@@ -16,8 +16,6 @@ LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
16LimitableInputDialog::~LimitableInputDialog() = default; 16LimitableInputDialog::~LimitableInputDialog() = default;
17 17
18void LimitableInputDialog::CreateUI() { 18void LimitableInputDialog::CreateUI() {
19 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
20
21 text_label = new QLabel(this); 19 text_label = new QLabel(this);
22 text_entry = new QLineEdit(this); 20 text_entry = new QLineEdit(this);
23 text_label_invalid = new QLabel(this); 21 text_label_invalid = new QLabel(this);
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
index 4b10fa517..1670aa596 100644
--- a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
@@ -8,7 +8,6 @@
8 8
9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { 9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
10 setWindowTitle(tr("Enter a hotkey")); 10 setWindowTitle(tr("Enter a hotkey"));
11 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
12 11
13 key_sequence = new QKeySequenceEdit; 12 key_sequence = new QKeySequenceEdit;
14 13
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 6fcf04e1b..67d230462 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -5,8 +5,8 @@
5 5
6namespace DefaultINI { 6namespace DefaultINI {
7 7
8const char* sdl2_config_file = R"( 8const char* sdl2_config_file =
9 9 R"(
10[ControlsP0] 10[ControlsP0]
11# The input devices and parameters for each Switch native input 11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... 12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
@@ -143,6 +143,8 @@ mouse_enabled =
143# 0 (default): Disabled, 1: Enabled 143# 0 (default): Disabled, 1: Enabled
144keyboard_enabled = 144keyboard_enabled =
145 145
146)"
147 R"(
146[Core] 148[Core]
147# Whether to use multi-core for CPU emulation 149# Whether to use multi-core for CPU emulation
148# 0: Disabled, 1 (default): Enabled 150# 0: Disabled, 1 (default): Enabled
@@ -242,6 +244,8 @@ cpuopt_unsafe_fastmem_check =
242# 0: Disabled, 1 (default): Enabled 244# 0: Disabled, 1 (default): Enabled
243cpuopt_unsafe_ignore_global_monitor = 245cpuopt_unsafe_ignore_global_monitor =
244 246
247)"
248 R"(
245[Renderer] 249[Renderer]
246# Which backend API to use. 250# Which backend API to use.
247# 0: OpenGL, 1 (default): Vulkan 251# 0: OpenGL, 1 (default): Vulkan
@@ -360,6 +364,8 @@ bg_red =
360bg_blue = 364bg_blue =
361bg_green = 365bg_green =
362 366
367)"
368 R"(
363[Audio] 369[Audio]
364# Which audio output engine to use. 370# Which audio output engine to use.
365# auto (default): Auto-select 371# auto (default): Auto-select