summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/audio_core/renderer/system.cpp5
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp11
-rw-r--r--src/common/address_space.inc4
-rw-r--r--src/common/input.h2
-rw-r--r--src/common/intrusive_list.h631
-rw-r--r--src/common/settings.cpp10
-rw-r--r--src/common/settings.h16
-rw-r--r--src/common/vector_math.h14
-rw-r--r--src/core/CMakeLists.txt21
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/core.h2
-rw-r--r--src/core/file_sys/vfs_layered.cpp9
-rw-r--r--src/core/file_sys/vfs_vector.cpp19
-rw-r--r--src/core/file_sys/vfs_vector.h4
-rw-r--r--src/core/frontend/applets/cabinet.cpp2
-rw-r--r--src/core/frontend/applets/cabinet.h10
-rw-r--r--src/core/hid/emulated_controller.cpp45
-rw-r--r--src/core/hid/emulated_controller.h9
-rw-r--r--src/core/hid/input_converter.cpp10
-rw-r--r--src/core/hid/motion_input.cpp57
-rw-r--r--src/core/hid/motion_input.h13
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp7
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/k_event_info.h5
-rw-r--r--src/core/hle/kernel/k_object_name.h8
-rw-r--r--src/core/hle/kernel/k_server_port.h4
-rw-r--r--src/core/hle/kernel/k_server_session.h7
-rw-r--r--src/core/hle/kernel/k_session_request.h4
-rw-r--r--src/core/hle/kernel/k_shared_memory_info.h4
-rw-r--r--src/core/hle/kernel/k_thread.h13
-rw-r--r--src/core/hle/kernel/kernel.cpp31
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp24
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.h6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/ipc_helpers.h1
-rw-r--r--src/core/hle/service/kernel_helpers.cpp3
-rw-r--r--src/core/hle/service/mutex.cpp3
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp (renamed from src/core/hle/service/nfp/amiibo_crypto.cpp)4
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.h (renamed from src/core/hle/service/nfp/amiibo_crypto.h)4
-rw-r--r--src/core/hle/service/nfc/common/device.cpp (renamed from src/core/hle/service/nfp/nfp_device.cpp)672
-rw-r--r--src/core/hle/service/nfc/common/device.h138
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp695
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h100
-rw-r--r--src/core/hle/service/nfc/mifare_result.h17
-rw-r--r--src/core/hle/service/nfc/mifare_types.h63
-rw-r--r--src/core/hle/service/nfc/mifare_user.cpp400
-rw-r--r--src/core/hle/service/nfc/mifare_user.h52
-rw-r--r--src/core/hle/service/nfc/nfc.cpp159
-rw-r--r--src/core/hle/service/nfc/nfc_device.cpp288
-rw-r--r--src/core/hle/service/nfc/nfc_device.h78
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp382
-rw-r--r--src/core/hle/service/nfc/nfc_interface.h (renamed from src/core/hle/service/nfc/nfc_user.h)33
-rw-r--r--src/core/hle/service/nfc/nfc_result.h33
-rw-r--r--src/core/hle/service/nfc/nfc_types.h90
-rw-r--r--src/core/hle/service/nfc/nfc_user.cpp365
-rw-r--r--src/core/hle/service/nfp/nfp.cpp36
-rw-r--r--src/core/hle/service/nfp/nfp_device.h120
-rw-r--r--src/core/hle/service/nfp/nfp_interface.cpp839
-rw-r--r--src/core/hle/service/nfp/nfp_interface.h37
-rw-r--r--src/core/hle/service/nfp/nfp_result.h29
-rw-r--r--src/core/hle/service/nfp/nfp_types.h129
-rw-r--r--src/core/hle/service/nifm/nifm.cpp6
-rw-r--r--src/core/hle/service/server_manager.cpp6
-rw-r--r--src/core/hle/service/service.h20
-rw-r--r--src/core/hle/service/sm/sm.cpp3
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp3
-rw-r--r--src/core/internal_network/network.cpp2
-rw-r--r--src/core/internal_network/network_interface.cpp2
-rw-r--r--src/core/memory.cpp25
-rw-r--r--src/core/telemetry_session.cpp17
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp7
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h2
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h7
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp24
-rw-r--r--src/input_common/input_mapping.cpp3
-rw-r--r--src/input_common/input_poller.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp3
-rw-r--r--src/tests/CMakeLists.txt2
-rw-r--r--src/tests/video_core/buffer_base.cpp549
-rw-r--r--src/tests/video_core/memory_tracker.cpp549
-rw-r--r--src/video_core/CMakeLists.txt7
-rw-r--r--src/video_core/buffer_cache/buffer_base.h527
-rw-r--r--src/video_core/buffer_cache/buffer_cache.cpp4
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h1007
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h579
-rw-r--r--src/video_core/buffer_cache/memory_tracker_base.h299
-rw-r--r--src/video_core/buffer_cache/word_manager.h485
-rw-r--r--src/video_core/compatible_formats.cpp20
-rw-r--r--src/video_core/engines/maxwell_dma.cpp8
-rw-r--r--src/video_core/fence_manager.h148
-rw-r--r--src/video_core/gpu.cpp19
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/memory_manager.cpp13
-rw-r--r--src/video_core/memory_manager.h4
-rw-r--r--src/video_core/query_cache.h139
-rw-r--r--src/video_core/rasterizer_download_area.h16
-rw-r--r--src/video_core/rasterizer_interface.h3
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h1
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache_base.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h12
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.h6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h1
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h1
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h3
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp3
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp53
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp224
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h34
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache_base.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp457
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.h83
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp28
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp124
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h37
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.h10
-rw-r--r--src/video_core/shader_cache.cpp4
-rw-r--r--src/video_core/shader_environment.cpp16
-rw-r--r--src/video_core/shader_environment.h6
-rw-r--r--src/video_core/surface.cpp5
-rw-r--r--src/video_core/surface.h12
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp6
-rw-r--r--src/video_core/texture_cache/formatter.cpp22
-rw-r--r--src/video_core/texture_cache/formatter.h8
-rw-r--r--src/video_core/texture_cache/image_info.cpp12
-rw-r--r--src/video_core/texture_cache/image_info.h2
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp12
-rw-r--r--src/video_core/texture_cache/image_view_base.h7
-rw-r--r--src/video_core/texture_cache/texture_cache.h53
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h4
-rw-r--r--src/video_core/texture_cache/util.cpp10
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h3
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.cpp6
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.h9
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.cpp12
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.h14
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp1
-rw-r--r--src/yuzu/bootmanager.cpp90
-rw-r--r--src/yuzu/bootmanager.h25
-rw-r--r--src/yuzu/configuration/config.cpp20
-rw-r--r--src/yuzu/configuration/configure_general.cpp9
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp168
-rw-r--r--src/yuzu/configuration/configure_graphics.h23
-rw-r--r--src/yuzu/configuration/configure_graphics.ui40
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp27
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h3
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui35
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp14
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp91
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h5
-rw-r--r--src/yuzu/configuration/configure_system.cpp10
-rw-r--r--src/yuzu/configuration/configure_system.h1
-rw-r--r--src/yuzu/configuration/configure_system.ui7
-rw-r--r--src/yuzu/main.cpp39
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu/qt_common.cpp55
-rw-r--r--src/yuzu/qt_common.h15
-rw-r--r--src/yuzu_cmd/config.cpp19
-rw-r--r--src/yuzu_cmd/default_ini.h26
183 files changed, 7505 insertions, 5037 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 312a49f42..5e3a74c0f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -113,6 +113,9 @@ else()
113 113
114 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init> 114 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
115 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field> 115 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
116 $<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local>
117 $<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough>
118 $<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits>
116 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init> 119 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
117 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field> 120 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
118 ) 121 )
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index ad869facb..53b258c4f 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -436,10 +436,7 @@ void System::Stop() {
436 } 436 }
437 437
438 if (execution_mode == ExecutionMode::Auto) { 438 if (execution_mode == ExecutionMode::Auto) {
439 // Should wait for the system to terminate here, but core timing (should have) already 439 terminate_event.Wait();
440 // stopped, so this isn't needed. Find a way to make this definite.
441
442 // terminate_event.Wait();
443 } 440 }
444} 441}
445 442
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index ee1a0652f..c1529d1f9 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -3,6 +3,7 @@
3 3
4#include <span> 4#include <span>
5#include <vector> 5#include <vector>
6#include <SDL.h>
6 7
7#include "audio_core/common/common.h" 8#include "audio_core/common/common.h"
8#include "audio_core/sink/sdl2_sink.h" 9#include "audio_core/sink/sdl2_sink.h"
@@ -10,16 +11,6 @@
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "core/core.h" 12#include "core/core.h"
12 13
13// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
14#ifdef __clang__
15#pragma clang diagnostic push
16#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
17#endif
18#include <SDL.h>
19#ifdef __clang__
20#pragma clang diagnostic pop
21#endif
22
23namespace AudioCore::Sink { 14namespace AudioCore::Sink {
24/** 15/**
25 * SDL sink stream, responsible for sinking samples to hardware. 16 * SDL sink stream, responsible for sinking samples to hardware.
diff --git a/src/common/address_space.inc b/src/common/address_space.inc
index 2195dabd5..1ee82df53 100644
--- a/src/common/address_space.inc
+++ b/src/common/address_space.inc
@@ -72,7 +72,7 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf
72 } 72 }
73 }()}; 73 }()};
74 74
75 if (block_end_predecessor->virt >= virt) { 75 if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) {
76 // If this block's start would be overlapped by the map then reuse it as a tail 76 // If this block's start would be overlapped by the map then reuse it as a tail
77 // block 77 // block
78 block_end_predecessor->virt = virt_end; 78 block_end_predecessor->virt = virt_end;
@@ -336,7 +336,7 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) {
336 ASSERT_MSG(false, "Unexpected allocator state!"); 336 ASSERT_MSG(false, "Unexpected allocator state!");
337 } 337 }
338 338
339 auto search_predecessor{this->blocks.begin()}; 339 auto search_predecessor{std::next(this->blocks.begin())};
340 auto search_successor{std::next(search_predecessor)}; 340 auto search_successor{std::next(search_predecessor)};
341 341
342 while (search_successor != this->blocks.end() && 342 while (search_successor != this->blocks.end() &&
diff --git a/src/common/input.h b/src/common/input.h
index 51b277c1f..66fb15f0a 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -111,6 +111,8 @@ struct AnalogProperties {
111 float offset{}; 111 float offset{};
112 // Invert direction of the sensor data 112 // Invert direction of the sensor data
113 bool inverted{}; 113 bool inverted{};
114 // Invert the state if it's converted to a button
115 bool inverted_button{};
114 // Press once to activate, press again to release 116 // Press once to activate, press again to release
115 bool toggle{}; 117 bool toggle{};
116}; 118};
diff --git a/src/common/intrusive_list.h b/src/common/intrusive_list.h
new file mode 100644
index 000000000..d330dc1c2
--- /dev/null
+++ b/src/common/intrusive_list.h
@@ -0,0 +1,631 @@
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/common_funcs.h"
7#include "common/parent_of_member.h"
8
9namespace Common {
10
11// Forward declare implementation class for Node.
12namespace impl {
13
14class IntrusiveListImpl;
15
16}
17
18class IntrusiveListNode {
19 YUZU_NON_COPYABLE(IntrusiveListNode);
20
21private:
22 friend class impl::IntrusiveListImpl;
23
24 IntrusiveListNode* m_prev;
25 IntrusiveListNode* m_next;
26
27public:
28 constexpr IntrusiveListNode() : m_prev(this), m_next(this) {}
29
30 constexpr bool IsLinked() const {
31 return m_next != this;
32 }
33
34private:
35 constexpr void LinkPrev(IntrusiveListNode* node) {
36 // We can't link an already linked node.
37 ASSERT(!node->IsLinked());
38 this->SplicePrev(node, node);
39 }
40
41 constexpr void SplicePrev(IntrusiveListNode* first, IntrusiveListNode* last) {
42 // Splice a range into the list.
43 auto last_prev = last->m_prev;
44 first->m_prev = m_prev;
45 last_prev->m_next = this;
46 m_prev->m_next = first;
47 m_prev = last_prev;
48 }
49
50 constexpr void LinkNext(IntrusiveListNode* node) {
51 // We can't link an already linked node.
52 ASSERT(!node->IsLinked());
53 return this->SpliceNext(node, node);
54 }
55
56 constexpr void SpliceNext(IntrusiveListNode* first, IntrusiveListNode* last) {
57 // Splice a range into the list.
58 auto last_prev = last->m_prev;
59 first->m_prev = this;
60 last_prev->m_next = m_next;
61 m_next->m_prev = last_prev;
62 m_next = first;
63 }
64
65 constexpr void Unlink() {
66 this->Unlink(m_next);
67 }
68
69 constexpr void Unlink(IntrusiveListNode* last) {
70 // Unlink a node from a next node.
71 auto last_prev = last->m_prev;
72 m_prev->m_next = last;
73 last->m_prev = m_prev;
74 last_prev->m_next = this;
75 m_prev = last_prev;
76 }
77
78 constexpr IntrusiveListNode* GetPrev() {
79 return m_prev;
80 }
81
82 constexpr const IntrusiveListNode* GetPrev() const {
83 return m_prev;
84 }
85
86 constexpr IntrusiveListNode* GetNext() {
87 return m_next;
88 }
89
90 constexpr const IntrusiveListNode* GetNext() const {
91 return m_next;
92 }
93};
94// DEPRECATED: static_assert(std::is_literal_type<IntrusiveListNode>::value);
95
96namespace impl {
97
98class IntrusiveListImpl {
99 YUZU_NON_COPYABLE(IntrusiveListImpl);
100
101private:
102 IntrusiveListNode m_root_node;
103
104public:
105 template <bool Const>
106 class Iterator;
107
108 using value_type = IntrusiveListNode;
109 using size_type = size_t;
110 using difference_type = ptrdiff_t;
111 using pointer = value_type*;
112 using const_pointer = const value_type*;
113 using reference = value_type&;
114 using const_reference = const value_type&;
115 using iterator = Iterator<false>;
116 using const_iterator = Iterator<true>;
117 using reverse_iterator = std::reverse_iterator<iterator>;
118 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
119
120 template <bool Const>
121 class Iterator {
122 public:
123 using iterator_category = std::bidirectional_iterator_tag;
124 using value_type = typename IntrusiveListImpl::value_type;
125 using difference_type = typename IntrusiveListImpl::difference_type;
126 using pointer =
127 std::conditional_t<Const, IntrusiveListImpl::const_pointer, IntrusiveListImpl::pointer>;
128 using reference = std::conditional_t<Const, IntrusiveListImpl::const_reference,
129 IntrusiveListImpl::reference>;
130
131 private:
132 pointer m_node;
133
134 public:
135 constexpr explicit Iterator(pointer n) : m_node(n) {}
136
137 constexpr bool operator==(const Iterator& rhs) const {
138 return m_node == rhs.m_node;
139 }
140
141 constexpr pointer operator->() const {
142 return m_node;
143 }
144
145 constexpr reference operator*() const {
146 return *m_node;
147 }
148
149 constexpr Iterator& operator++() {
150 m_node = m_node->m_next;
151 return *this;
152 }
153
154 constexpr Iterator& operator--() {
155 m_node = m_node->m_prev;
156 return *this;
157 }
158
159 constexpr Iterator operator++(int) {
160 const Iterator it{*this};
161 ++(*this);
162 return it;
163 }
164
165 constexpr Iterator operator--(int) {
166 const Iterator it{*this};
167 --(*this);
168 return it;
169 }
170
171 constexpr operator Iterator<true>() const {
172 return Iterator<true>(m_node);
173 }
174
175 constexpr Iterator<false> GetNonConstIterator() const {
176 return Iterator<false>(const_cast<IntrusiveListImpl::pointer>(m_node));
177 }
178 };
179
180public:
181 constexpr IntrusiveListImpl() : m_root_node() {}
182
183 // Iterator accessors.
184 constexpr iterator begin() {
185 return iterator(m_root_node.GetNext());
186 }
187
188 constexpr const_iterator begin() const {
189 return const_iterator(m_root_node.GetNext());
190 }
191
192 constexpr iterator end() {
193 return iterator(std::addressof(m_root_node));
194 }
195
196 constexpr const_iterator end() const {
197 return const_iterator(std::addressof(m_root_node));
198 }
199
200 constexpr iterator iterator_to(reference v) {
201 // Only allow iterator_to for values in lists.
202 ASSERT(v.IsLinked());
203 return iterator(std::addressof(v));
204 }
205
206 constexpr const_iterator iterator_to(const_reference v) const {
207 // Only allow iterator_to for values in lists.
208 ASSERT(v.IsLinked());
209 return const_iterator(std::addressof(v));
210 }
211
212 // Content management.
213 constexpr bool empty() const {
214 return !m_root_node.IsLinked();
215 }
216
217 constexpr size_type size() const {
218 return static_cast<size_type>(std::distance(this->begin(), this->end()));
219 }
220
221 constexpr reference back() {
222 return *m_root_node.GetPrev();
223 }
224
225 constexpr const_reference back() const {
226 return *m_root_node.GetPrev();
227 }
228
229 constexpr reference front() {
230 return *m_root_node.GetNext();
231 }
232
233 constexpr const_reference front() const {
234 return *m_root_node.GetNext();
235 }
236
237 constexpr void push_back(reference node) {
238 m_root_node.LinkPrev(std::addressof(node));
239 }
240
241 constexpr void push_front(reference node) {
242 m_root_node.LinkNext(std::addressof(node));
243 }
244
245 constexpr void pop_back() {
246 m_root_node.GetPrev()->Unlink();
247 }
248
249 constexpr void pop_front() {
250 m_root_node.GetNext()->Unlink();
251 }
252
253 constexpr iterator insert(const_iterator pos, reference node) {
254 pos.GetNonConstIterator()->LinkPrev(std::addressof(node));
255 return iterator(std::addressof(node));
256 }
257
258 constexpr void splice(const_iterator pos, IntrusiveListImpl& o) {
259 splice_impl(pos, o.begin(), o.end());
260 }
261
262 constexpr void splice(const_iterator pos, IntrusiveListImpl& o, const_iterator first) {
263 const_iterator last(first);
264 std::advance(last, 1);
265 splice_impl(pos, first, last);
266 }
267
268 constexpr void splice(const_iterator pos, IntrusiveListImpl& o, const_iterator first,
269 const_iterator last) {
270 splice_impl(pos, first, last);
271 }
272
273 constexpr iterator erase(const_iterator pos) {
274 if (pos == this->end()) {
275 return this->end();
276 }
277 iterator it(pos.GetNonConstIterator());
278 (it++)->Unlink();
279 return it;
280 }
281
282 constexpr void clear() {
283 while (!this->empty()) {
284 this->pop_front();
285 }
286 }
287
288private:
289 constexpr void splice_impl(const_iterator _pos, const_iterator _first, const_iterator _last) {
290 if (_first == _last) {
291 return;
292 }
293 iterator pos(_pos.GetNonConstIterator());
294 iterator first(_first.GetNonConstIterator());
295 iterator last(_last.GetNonConstIterator());
296 first->Unlink(std::addressof(*last));
297 pos->SplicePrev(std::addressof(*first), std::addressof(*first));
298 }
299};
300
301} // namespace impl
302
303template <class T, class Traits>
304class IntrusiveList {
305 YUZU_NON_COPYABLE(IntrusiveList);
306
307private:
308 impl::IntrusiveListImpl m_impl;
309
310public:
311 template <bool Const>
312 class Iterator;
313
314 using value_type = T;
315 using size_type = size_t;
316 using difference_type = ptrdiff_t;
317 using pointer = value_type*;
318 using const_pointer = const value_type*;
319 using reference = value_type&;
320 using const_reference = const value_type&;
321 using iterator = Iterator<false>;
322 using const_iterator = Iterator<true>;
323 using reverse_iterator = std::reverse_iterator<iterator>;
324 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
325
326 template <bool Const>
327 class Iterator {
328 public:
329 friend class Common::IntrusiveList<T, Traits>;
330
331 using ImplIterator =
332 std::conditional_t<Const, Common::impl::IntrusiveListImpl::const_iterator,
333 Common::impl::IntrusiveListImpl::iterator>;
334
335 using iterator_category = std::bidirectional_iterator_tag;
336 using value_type = typename IntrusiveList::value_type;
337 using difference_type = typename IntrusiveList::difference_type;
338 using pointer =
339 std::conditional_t<Const, IntrusiveList::const_pointer, IntrusiveList::pointer>;
340 using reference =
341 std::conditional_t<Const, IntrusiveList::const_reference, IntrusiveList::reference>;
342
343 private:
344 ImplIterator m_iterator;
345
346 private:
347 constexpr explicit Iterator(ImplIterator it) : m_iterator(it) {}
348
349 constexpr ImplIterator GetImplIterator() const {
350 return m_iterator;
351 }
352
353 public:
354 constexpr bool operator==(const Iterator& rhs) const {
355 return m_iterator == rhs.m_iterator;
356 }
357
358 constexpr pointer operator->() const {
359 return std::addressof(Traits::GetParent(*m_iterator));
360 }
361
362 constexpr reference operator*() const {
363 return Traits::GetParent(*m_iterator);
364 }
365
366 constexpr Iterator& operator++() {
367 ++m_iterator;
368 return *this;
369 }
370
371 constexpr Iterator& operator--() {
372 --m_iterator;
373 return *this;
374 }
375
376 constexpr Iterator operator++(int) {
377 const Iterator it{*this};
378 ++m_iterator;
379 return it;
380 }
381
382 constexpr Iterator operator--(int) {
383 const Iterator it{*this};
384 --m_iterator;
385 return it;
386 }
387
388 constexpr operator Iterator<true>() const {
389 return Iterator<true>(m_iterator);
390 }
391 };
392
393private:
394 static constexpr IntrusiveListNode& GetNode(reference ref) {
395 return Traits::GetNode(ref);
396 }
397
398 static constexpr IntrusiveListNode const& GetNode(const_reference ref) {
399 return Traits::GetNode(ref);
400 }
401
402 static constexpr reference GetParent(IntrusiveListNode& node) {
403 return Traits::GetParent(node);
404 }
405
406 static constexpr const_reference GetParent(IntrusiveListNode const& node) {
407 return Traits::GetParent(node);
408 }
409
410public:
411 constexpr IntrusiveList() : m_impl() {}
412
413 // Iterator accessors.
414 constexpr iterator begin() {
415 return iterator(m_impl.begin());
416 }
417
418 constexpr const_iterator begin() const {
419 return const_iterator(m_impl.begin());
420 }
421
422 constexpr iterator end() {
423 return iterator(m_impl.end());
424 }
425
426 constexpr const_iterator end() const {
427 return const_iterator(m_impl.end());
428 }
429
430 constexpr const_iterator cbegin() const {
431 return this->begin();
432 }
433
434 constexpr const_iterator cend() const {
435 return this->end();
436 }
437
438 constexpr reverse_iterator rbegin() {
439 return reverse_iterator(this->end());
440 }
441
442 constexpr const_reverse_iterator rbegin() const {
443 return const_reverse_iterator(this->end());
444 }
445
446 constexpr reverse_iterator rend() {
447 return reverse_iterator(this->begin());
448 }
449
450 constexpr const_reverse_iterator rend() const {
451 return const_reverse_iterator(this->begin());
452 }
453
454 constexpr const_reverse_iterator crbegin() const {
455 return this->rbegin();
456 }
457
458 constexpr const_reverse_iterator crend() const {
459 return this->rend();
460 }
461
462 constexpr iterator iterator_to(reference v) {
463 return iterator(m_impl.iterator_to(GetNode(v)));
464 }
465
466 constexpr const_iterator iterator_to(const_reference v) const {
467 return const_iterator(m_impl.iterator_to(GetNode(v)));
468 }
469
470 // Content management.
471 constexpr bool empty() const {
472 return m_impl.empty();
473 }
474
475 constexpr size_type size() const {
476 return m_impl.size();
477 }
478
479 constexpr reference back() {
480 return GetParent(m_impl.back());
481 }
482
483 constexpr const_reference back() const {
484 return GetParent(m_impl.back());
485 }
486
487 constexpr reference front() {
488 return GetParent(m_impl.front());
489 }
490
491 constexpr const_reference front() const {
492 return GetParent(m_impl.front());
493 }
494
495 constexpr void push_back(reference ref) {
496 m_impl.push_back(GetNode(ref));
497 }
498
499 constexpr void push_front(reference ref) {
500 m_impl.push_front(GetNode(ref));
501 }
502
503 constexpr void pop_back() {
504 m_impl.pop_back();
505 }
506
507 constexpr void pop_front() {
508 m_impl.pop_front();
509 }
510
511 constexpr iterator insert(const_iterator pos, reference ref) {
512 return iterator(m_impl.insert(pos.GetImplIterator(), GetNode(ref)));
513 }
514
515 constexpr void splice(const_iterator pos, IntrusiveList& o) {
516 m_impl.splice(pos.GetImplIterator(), o.m_impl);
517 }
518
519 constexpr void splice(const_iterator pos, IntrusiveList& o, const_iterator first) {
520 m_impl.splice(pos.GetImplIterator(), o.m_impl, first.GetImplIterator());
521 }
522
523 constexpr void splice(const_iterator pos, IntrusiveList& o, const_iterator first,
524 const_iterator last) {
525 m_impl.splice(pos.GetImplIterator(), o.m_impl, first.GetImplIterator(),
526 last.GetImplIterator());
527 }
528
529 constexpr iterator erase(const_iterator pos) {
530 return iterator(m_impl.erase(pos.GetImplIterator()));
531 }
532
533 constexpr void clear() {
534 m_impl.clear();
535 }
536};
537
538template <auto T, class Derived = Common::impl::GetParentType<T>>
539class IntrusiveListMemberTraits;
540
541template <class Parent, IntrusiveListNode Parent::*Member, class Derived>
542class IntrusiveListMemberTraits<Member, Derived> {
543public:
544 using ListType = IntrusiveList<Derived, IntrusiveListMemberTraits>;
545
546private:
547 friend class IntrusiveList<Derived, IntrusiveListMemberTraits>;
548
549 static constexpr IntrusiveListNode& GetNode(Derived& parent) {
550 return parent.*Member;
551 }
552
553 static constexpr IntrusiveListNode const& GetNode(Derived const& parent) {
554 return parent.*Member;
555 }
556
557 static Derived& GetParent(IntrusiveListNode& node) {
558 return Common::GetParentReference<Member, Derived>(std::addressof(node));
559 }
560
561 static Derived const& GetParent(IntrusiveListNode const& node) {
562 return Common::GetParentReference<Member, Derived>(std::addressof(node));
563 }
564};
565
566template <auto T, class Derived = Common::impl::GetParentType<T>>
567class IntrusiveListMemberTraitsByNonConstexprOffsetOf;
568
569template <class Parent, IntrusiveListNode Parent::*Member, class Derived>
570class IntrusiveListMemberTraitsByNonConstexprOffsetOf<Member, Derived> {
571public:
572 using ListType = IntrusiveList<Derived, IntrusiveListMemberTraitsByNonConstexprOffsetOf>;
573
574private:
575 friend class IntrusiveList<Derived, IntrusiveListMemberTraitsByNonConstexprOffsetOf>;
576
577 static constexpr IntrusiveListNode& GetNode(Derived& parent) {
578 return parent.*Member;
579 }
580
581 static constexpr IntrusiveListNode const& GetNode(Derived const& parent) {
582 return parent.*Member;
583 }
584
585 static Derived& GetParent(IntrusiveListNode& node) {
586 return *reinterpret_cast<Derived*>(reinterpret_cast<char*>(std::addressof(node)) -
587 GetOffset());
588 }
589
590 static Derived const& GetParent(IntrusiveListNode const& node) {
591 return *reinterpret_cast<const Derived*>(
592 reinterpret_cast<const char*>(std::addressof(node)) - GetOffset());
593 }
594
595 static uintptr_t GetOffset() {
596 return reinterpret_cast<uintptr_t>(std::addressof(reinterpret_cast<Derived*>(0)->*Member));
597 }
598};
599
600template <class Derived>
601class IntrusiveListBaseNode : public IntrusiveListNode {};
602
603template <class Derived>
604class IntrusiveListBaseTraits {
605public:
606 using ListType = IntrusiveList<Derived, IntrusiveListBaseTraits>;
607
608private:
609 friend class IntrusiveList<Derived, IntrusiveListBaseTraits>;
610
611 static constexpr IntrusiveListNode& GetNode(Derived& parent) {
612 return static_cast<IntrusiveListNode&>(
613 static_cast<IntrusiveListBaseNode<Derived>&>(parent));
614 }
615
616 static constexpr IntrusiveListNode const& GetNode(Derived const& parent) {
617 return static_cast<const IntrusiveListNode&>(
618 static_cast<const IntrusiveListBaseNode<Derived>&>(parent));
619 }
620
621 static constexpr Derived& GetParent(IntrusiveListNode& node) {
622 return static_cast<Derived&>(static_cast<IntrusiveListBaseNode<Derived>&>(node));
623 }
624
625 static constexpr Derived const& GetParent(IntrusiveListNode const& node) {
626 return static_cast<const Derived&>(
627 static_cast<const IntrusiveListBaseNode<Derived>&>(node));
628 }
629};
630
631} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 84955030b..db1774c71 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -45,6 +45,7 @@ void LogSettings() {
45 log_setting("System_LanguageIndex", values.language_index.GetValue()); 45 log_setting("System_LanguageIndex", values.language_index.GetValue());
46 log_setting("System_RegionIndex", values.region_index.GetValue()); 46 log_setting("System_RegionIndex", values.region_index.GetValue());
47 log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); 47 log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
48 log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue());
48 log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); 49 log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
49 log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); 50 log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
50 log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); 51 log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
@@ -60,7 +61,8 @@ void LogSettings() {
60 log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue()); 61 log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
61 log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue()); 62 log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
62 log_setting("Renderer_AsyncASTC", values.async_astc.GetValue()); 63 log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
63 log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); 64 log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
65 log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
64 log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); 66 log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
65 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); 67 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
66 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 68 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
@@ -191,7 +193,7 @@ void RestoreGlobalState(bool is_powered_on) {
191 193
192 // Core 194 // Core
193 values.use_multi_core.SetGlobal(true); 195 values.use_multi_core.SetGlobal(true);
194 values.use_extended_memory_layout.SetGlobal(true); 196 values.use_unsafe_extended_memory_layout.SetGlobal(true);
195 197
196 // CPU 198 // CPU
197 values.cpu_accuracy.SetGlobal(true); 199 values.cpu_accuracy.SetGlobal(true);
@@ -205,6 +207,7 @@ void RestoreGlobalState(bool is_powered_on) {
205 // Renderer 207 // Renderer
206 values.fsr_sharpening_slider.SetGlobal(true); 208 values.fsr_sharpening_slider.SetGlobal(true);
207 values.renderer_backend.SetGlobal(true); 209 values.renderer_backend.SetGlobal(true);
210 values.async_presentation.SetGlobal(true);
208 values.renderer_force_max_clock.SetGlobal(true); 211 values.renderer_force_max_clock.SetGlobal(true);
209 values.vulkan_device.SetGlobal(true); 212 values.vulkan_device.SetGlobal(true);
210 values.fullscreen_mode.SetGlobal(true); 213 values.fullscreen_mode.SetGlobal(true);
@@ -221,11 +224,10 @@ void RestoreGlobalState(bool is_powered_on) {
221 values.nvdec_emulation.SetGlobal(true); 224 values.nvdec_emulation.SetGlobal(true);
222 values.accelerate_astc.SetGlobal(true); 225 values.accelerate_astc.SetGlobal(true);
223 values.async_astc.SetGlobal(true); 226 values.async_astc.SetGlobal(true);
224 values.use_vsync.SetGlobal(true); 227 values.use_reactive_flushing.SetGlobal(true);
225 values.shader_backend.SetGlobal(true); 228 values.shader_backend.SetGlobal(true);
226 values.use_asynchronous_shaders.SetGlobal(true); 229 values.use_asynchronous_shaders.SetGlobal(true);
227 values.use_fast_gpu_time.SetGlobal(true); 230 values.use_fast_gpu_time.SetGlobal(true);
228 values.use_pessimistic_flushes.SetGlobal(true);
229 values.use_vulkan_driver_pipeline_cache.SetGlobal(true); 231 values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
230 values.bg_red.SetGlobal(true); 232 values.bg_red.SetGlobal(true);
231 values.bg_green.SetGlobal(true); 233 values.bg_green.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index b77a1580a..f4eb4e3cd 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -16,6 +16,13 @@
16 16
17namespace Settings { 17namespace Settings {
18 18
19enum class VSyncMode : u32 {
20 Immediate = 0,
21 Mailbox = 1,
22 FIFO = 2,
23 FIFORelaxed = 3,
24};
25
19enum class RendererBackend : u32 { 26enum class RendererBackend : u32 {
20 OpenGL = 0, 27 OpenGL = 0,
21 Vulkan = 1, 28 Vulkan = 1,
@@ -388,7 +395,8 @@ struct Values {
388 395
389 // Core 396 // Core
390 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; 397 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
391 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; 398 SwitchableSetting<bool> use_unsafe_extended_memory_layout{false,
399 "use_unsafe_extended_memory_layout"};
392 400
393 // Cpu 401 // Cpu
394 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 402 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
@@ -422,6 +430,7 @@ struct Values {
422 // Renderer 430 // Renderer
423 SwitchableSetting<RendererBackend, true> renderer_backend{ 431 SwitchableSetting<RendererBackend, true> renderer_backend{
424 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; 432 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
433 SwitchableSetting<bool> async_presentation{false, "async_presentation"};
425 SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"}; 434 SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"};
426 Setting<bool> renderer_debug{false, "debug"}; 435 Setting<bool> renderer_debug{false, "debug"};
427 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 436 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
@@ -454,12 +463,13 @@ struct Values {
454 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 463 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
455 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; 464 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
456 SwitchableSetting<bool> async_astc{false, "async_astc"}; 465 SwitchableSetting<bool> async_astc{false, "async_astc"};
457 SwitchableSetting<bool> use_vsync{true, "use_vsync"}; 466 Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate,
467 VSyncMode::FIFORelaxed, "use_vsync"};
468 SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"};
458 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL, 469 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
459 ShaderBackend::SPIRV, "shader_backend"}; 470 ShaderBackend::SPIRV, "shader_backend"};
460 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 471 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
461 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 472 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
462 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
463 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true, 473 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
464 "use_vulkan_driver_pipeline_cache"}; 474 "use_vulkan_driver_pipeline_cache"};
465 475
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 0e2095c45..b4885835d 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -259,6 +259,20 @@ public:
259 return *this; 259 return *this;
260 } 260 }
261 261
262 void RotateFromOrigin(float roll, float pitch, float yaw) {
263 float temp = y;
264 y = std::cos(roll) * y - std::sin(roll) * z;
265 z = std::sin(roll) * temp + std::cos(roll) * z;
266
267 temp = x;
268 x = std::cos(pitch) * x + std::sin(pitch) * z;
269 z = -std::sin(pitch) * temp + std::cos(pitch) * z;
270
271 temp = x;
272 x = std::cos(yaw) * x - std::sin(yaw) * y;
273 y = std::sin(yaw) * temp + std::cos(yaw) * y;
274 }
275
262 [[nodiscard]] constexpr T Length2() const { 276 [[nodiscard]] constexpr T Length2() const {
263 return x * x + y * y + z * z; 277 return x * x + y * y + z * z;
264 } 278 }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8817a99c9..45328158f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -555,21 +555,22 @@ add_library(core STATIC
555 hle/service/mnpp/mnpp_app.h 555 hle/service/mnpp/mnpp_app.h
556 hle/service/ncm/ncm.cpp 556 hle/service/ncm/ncm.cpp
557 hle/service/ncm/ncm.h 557 hle/service/ncm/ncm.h
558 hle/service/nfc/mifare_user.cpp 558 hle/service/nfc/common/amiibo_crypto.cpp
559 hle/service/nfc/mifare_user.h 559 hle/service/nfc/common/amiibo_crypto.h
560 hle/service/nfc/common/device.cpp
561 hle/service/nfc/common/device.h
562 hle/service/nfc/common/device_manager.cpp
563 hle/service/nfc/common/device_manager.h
564 hle/service/nfc/mifare_result.h
565 hle/service/nfc/mifare_types.h
560 hle/service/nfc/nfc.cpp 566 hle/service/nfc/nfc.cpp
561 hle/service/nfc/nfc.h 567 hle/service/nfc/nfc.h
562 hle/service/nfc/nfc_device.cpp 568 hle/service/nfc/nfc_interface.cpp
563 hle/service/nfc/nfc_device.h 569 hle/service/nfc/nfc_interface.h
564 hle/service/nfc/nfc_result.h 570 hle/service/nfc/nfc_result.h
565 hle/service/nfc/nfc_user.cpp 571 hle/service/nfc/nfc_types.h
566 hle/service/nfc/nfc_user.h
567 hle/service/nfp/amiibo_crypto.cpp
568 hle/service/nfp/amiibo_crypto.h
569 hle/service/nfp/nfp.cpp 572 hle/service/nfp/nfp.cpp
570 hle/service/nfp/nfp.h 573 hle/service/nfp/nfp.h
571 hle/service/nfp/nfp_device.cpp
572 hle/service/nfp/nfp_device.h
573 hle/service/nfp/nfp_interface.cpp 574 hle/service/nfp/nfp_interface.cpp
574 hle/service/nfp/nfp_interface.h 575 hle/service/nfp/nfp_interface.h
575 hle/service/nfp/nfp_result.h 576 hle/service/nfp/nfp_result.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index caa6a77be..b5f62690e 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -137,7 +137,7 @@ struct System::Impl {
137 device_memory = std::make_unique<Core::DeviceMemory>(); 137 device_memory = std::make_unique<Core::DeviceMemory>();
138 138
139 is_multicore = Settings::values.use_multi_core.GetValue(); 139 is_multicore = Settings::values.use_multi_core.GetValue();
140 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); 140 extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
141 141
142 core_timing.SetMulticore(is_multicore); 142 core_timing.SetMulticore(is_multicore);
143 core_timing.Initialize([&system]() { system.RegisterHostThread(); }); 143 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -169,7 +169,7 @@ struct System::Impl {
169 void ReinitializeIfNecessary(System& system) { 169 void ReinitializeIfNecessary(System& system) {
170 const bool must_reinitialize = 170 const bool must_reinitialize =
171 is_multicore != Settings::values.use_multi_core.GetValue() || 171 is_multicore != Settings::values.use_multi_core.GetValue() ||
172 extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue(); 172 extended_memory_layout != Settings::values.use_unsafe_extended_memory_layout.GetValue();
173 173
174 if (!must_reinitialize) { 174 if (!must_reinitialize) {
175 return; 175 return;
@@ -178,7 +178,7 @@ struct System::Impl {
178 LOG_DEBUG(Kernel, "Re-initializing"); 178 LOG_DEBUG(Kernel, "Re-initializing");
179 179
180 is_multicore = Settings::values.use_multi_core.GetValue(); 180 is_multicore = Settings::values.use_multi_core.GetValue();
181 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); 181 extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
182 182
183 Initialize(system); 183 Initialize(system);
184 } 184 }
@@ -293,6 +293,7 @@ struct System::Impl {
293 ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", 293 ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
294 Kernel::KProcess::ProcessType::Userland, resource_limit) 294 Kernel::KProcess::ProcessType::Userland, resource_limit)
295 .IsSuccess()); 295 .IsSuccess());
296 Kernel::KProcess::Register(system.Kernel(), main_process);
296 kernel.MakeApplicationProcess(main_process); 297 kernel.MakeApplicationProcess(main_process);
297 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); 298 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
298 if (load_result != Loader::ResultStatus::Success) { 299 if (load_result != Loader::ResultStatus::Success) {
@@ -611,6 +612,10 @@ void System::PrepareReschedule(const u32 core_index) {
611 impl->kernel.PrepareReschedule(core_index); 612 impl->kernel.PrepareReschedule(core_index);
612} 613}
613 614
615size_t System::GetCurrentHostThreadID() const {
616 return impl->kernel.GetCurrentHostThreadID();
617}
618
614PerfStatsResults System::GetAndResetPerfStats() { 619PerfStatsResults System::GetAndResetPerfStats() {
615 return impl->GetAndResetPerfStats(); 620 return impl->GetAndResetPerfStats();
616} 621}
diff --git a/src/core/core.h b/src/core/core.h
index 4a5aba032..4f153154f 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -222,6 +222,8 @@ public:
222 /// Prepare the core emulation for a reschedule 222 /// Prepare the core emulation for a reschedule
223 void PrepareReschedule(u32 core_index); 223 void PrepareReschedule(u32 core_index);
224 224
225 [[nodiscard]] size_t GetCurrentHostThreadID() const;
226
225 /// Gets and resets core performance statistics 227 /// Gets and resets core performance statistics
226 [[nodiscard]] PerfStatsResults GetAndResetPerfStats(); 228 [[nodiscard]] PerfStatsResults GetAndResetPerfStats();
227 229
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index da05dd395..3e6426afc 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <set>
5#include <utility> 6#include <utility>
6#include "core/file_sys/vfs_layered.h" 7#include "core/file_sys/vfs_layered.h"
7 8
@@ -58,11 +59,13 @@ std::string LayeredVfsDirectory::GetFullPath() const {
58 59
59std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const { 60std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
60 std::vector<VirtualFile> out; 61 std::vector<VirtualFile> out;
62 std::set<std::string, std::less<>> out_names;
63
61 for (const auto& layer : dirs) { 64 for (const auto& layer : dirs) {
62 for (const auto& file : layer->GetFiles()) { 65 for (const auto& file : layer->GetFiles()) {
63 if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) { 66 auto file_name = file->GetName();
64 return comp->GetName() == file->GetName(); 67 if (!out_names.contains(file_name)) {
65 }) == out.end()) { 68 out_names.emplace(std::move(file_name));
66 out.push_back(file); 69 out.push_back(file);
67 } 70 }
68 } 71 }
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 251d9d7c9..af1df4c51 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -67,6 +67,23 @@ VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
67 67
68VectorVfsDirectory::~VectorVfsDirectory() = default; 68VectorVfsDirectory::~VectorVfsDirectory() = default;
69 69
70VirtualFile VectorVfsDirectory::GetFile(std::string_view file_name) const {
71 if (!optimized_file_index_built) {
72 optimized_file_index.clear();
73 for (size_t i = 0; i < files.size(); i++) {
74 optimized_file_index.emplace(files[i]->GetName(), i);
75 }
76 optimized_file_index_built = true;
77 }
78
79 const auto it = optimized_file_index.find(file_name);
80 if (it != optimized_file_index.end()) {
81 return files[it->second];
82 }
83
84 return nullptr;
85}
86
70std::vector<VirtualFile> VectorVfsDirectory::GetFiles() const { 87std::vector<VirtualFile> VectorVfsDirectory::GetFiles() const {
71 return files; 88 return files;
72} 89}
@@ -107,6 +124,7 @@ bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) {
107} 124}
108 125
109bool VectorVfsDirectory::DeleteFile(std::string_view file_name) { 126bool VectorVfsDirectory::DeleteFile(std::string_view file_name) {
127 optimized_file_index_built = false;
110 return FindAndRemoveVectorElement(files, file_name); 128 return FindAndRemoveVectorElement(files, file_name);
111} 129}
112 130
@@ -124,6 +142,7 @@ VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) {
124} 142}
125 143
126void VectorVfsDirectory::AddFile(VirtualFile file) { 144void VectorVfsDirectory::AddFile(VirtualFile file) {
145 optimized_file_index_built = false;
127 files.push_back(std::move(file)); 146 files.push_back(std::move(file));
128} 147}
129 148
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index bfedb6e42..c9955755b 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -105,6 +105,7 @@ public:
105 VirtualDir parent = nullptr); 105 VirtualDir parent = nullptr);
106 ~VectorVfsDirectory() override; 106 ~VectorVfsDirectory() override;
107 107
108 VirtualFile GetFile(std::string_view file_name) const override;
108 std::vector<VirtualFile> GetFiles() const override; 109 std::vector<VirtualFile> GetFiles() const override;
109 std::vector<VirtualDir> GetSubdirectories() const override; 110 std::vector<VirtualDir> GetSubdirectories() const override;
110 bool IsWritable() const override; 111 bool IsWritable() const override;
@@ -126,6 +127,9 @@ private:
126 127
127 VirtualDir parent; 128 VirtualDir parent;
128 std::string name; 129 std::string name;
130
131 mutable std::map<std::string, size_t, std::less<>> optimized_file_index;
132 mutable bool optimized_file_index_built{};
129}; 133};
130 134
131} // namespace FileSys 135} // namespace FileSys
diff --git a/src/core/frontend/applets/cabinet.cpp b/src/core/frontend/applets/cabinet.cpp
index 2d501eeae..c33ce248b 100644
--- a/src/core/frontend/applets/cabinet.cpp
+++ b/src/core/frontend/applets/cabinet.cpp
@@ -14,7 +14,7 @@ void DefaultCabinetApplet::Close() const {}
14 14
15void DefaultCabinetApplet::ShowCabinetApplet( 15void DefaultCabinetApplet::ShowCabinetApplet(
16 const CabinetCallback& callback, const CabinetParameters& parameters, 16 const CabinetCallback& callback, const CabinetParameters& parameters,
17 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const { 17 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const {
18 LOG_WARNING(Service_AM, "(STUBBED) called"); 18 LOG_WARNING(Service_AM, "(STUBBED) called");
19 callback(false, {}); 19 callback(false, {});
20} 20}
diff --git a/src/core/frontend/applets/cabinet.h b/src/core/frontend/applets/cabinet.h
index 74dc5a4f6..af3fc6c3d 100644
--- a/src/core/frontend/applets/cabinet.h
+++ b/src/core/frontend/applets/cabinet.h
@@ -7,9 +7,9 @@
7#include "core/frontend/applets/applet.h" 7#include "core/frontend/applets/applet.h"
8#include "core/hle/service/nfp/nfp_types.h" 8#include "core/hle/service/nfp/nfp_types.h"
9 9
10namespace Service::NFP { 10namespace Service::NFC {
11class NfpDevice; 11class NfcDevice;
12} // namespace Service::NFP 12} // namespace Service::NFC
13 13
14namespace Core::Frontend { 14namespace Core::Frontend {
15 15
@@ -26,14 +26,14 @@ public:
26 virtual ~CabinetApplet(); 26 virtual ~CabinetApplet();
27 virtual void ShowCabinetApplet(const CabinetCallback& callback, 27 virtual void ShowCabinetApplet(const CabinetCallback& callback,
28 const CabinetParameters& parameters, 28 const CabinetParameters& parameters,
29 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0; 29 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const = 0;
30}; 30};
31 31
32class DefaultCabinetApplet final : public CabinetApplet { 32class DefaultCabinetApplet final : public CabinetApplet {
33public: 33public:
34 void Close() const override; 34 void Close() const override;
35 void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters, 35 void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
36 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override; 36 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const override;
37}; 37};
38 38
39} // namespace Core::Frontend 39} // namespace Core::Frontend
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index a70f8807c..366880711 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -376,6 +376,7 @@ void EmulatedController::ReloadInput() {
376 motion.accel = emulated_motion.GetAcceleration(); 376 motion.accel = emulated_motion.GetAcceleration();
377 motion.gyro = emulated_motion.GetGyroscope(); 377 motion.gyro = emulated_motion.GetGyroscope();
378 motion.rotation = emulated_motion.GetRotations(); 378 motion.rotation = emulated_motion.GetRotations();
379 motion.euler = emulated_motion.GetEulerAngles();
379 motion.orientation = emulated_motion.GetOrientation(); 380 motion.orientation = emulated_motion.GetOrientation();
380 motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity); 381 motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
381 } 382 }
@@ -551,6 +552,8 @@ void EmulatedController::EnableSystemButtons() {
551void EmulatedController::DisableSystemButtons() { 552void EmulatedController::DisableSystemButtons() {
552 std::scoped_lock lock{mutex}; 553 std::scoped_lock lock{mutex};
553 system_buttons_enabled = false; 554 system_buttons_enabled = false;
555 controller.home_button_state.raw = 0;
556 controller.capture_button_state.raw = 0;
554} 557}
555 558
556void EmulatedController::ResetSystemButtons() { 559void EmulatedController::ResetSystemButtons() {
@@ -685,6 +688,12 @@ void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage
685 ReloadInput(); 688 ReloadInput();
686} 689}
687 690
691void EmulatedController::StartMotionCalibration() {
692 for (ControllerMotionInfo& motion : controller.motion_values) {
693 motion.emulated.Calibrate();
694 }
695}
696
688void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, 697void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
689 Common::UUID uuid) { 698 Common::UUID uuid) {
690 if (index >= controller.button_values.size()) { 699 if (index >= controller.button_values.size()) {
@@ -734,6 +743,8 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
734 if (is_configuring) { 743 if (is_configuring) {
735 controller.npad_button_state.raw = NpadButton::None; 744 controller.npad_button_state.raw = NpadButton::None;
736 controller.debug_pad_button_state.raw = 0; 745 controller.debug_pad_button_state.raw = 0;
746 controller.home_button_state.raw = 0;
747 controller.capture_button_state.raw = 0;
737 lock.unlock(); 748 lock.unlock();
738 TriggerOnChange(ControllerTriggerType::Button, false); 749 TriggerOnChange(ControllerTriggerType::Button, false);
739 return; 750 return;
@@ -974,16 +985,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
974 emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold); 985 emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold);
975 emulated.UpdateRotation(raw_status.delta_timestamp); 986 emulated.UpdateRotation(raw_status.delta_timestamp);
976 emulated.UpdateOrientation(raw_status.delta_timestamp); 987 emulated.UpdateOrientation(raw_status.delta_timestamp);
977 force_update_motion = raw_status.force_update;
978
979 if (is_configuring) {
980 return;
981 }
982 988
983 auto& motion = controller.motion_state[index]; 989 auto& motion = controller.motion_state[index];
984 motion.accel = emulated.GetAcceleration(); 990 motion.accel = emulated.GetAcceleration();
985 motion.gyro = emulated.GetGyroscope(); 991 motion.gyro = emulated.GetGyroscope();
986 motion.rotation = emulated.GetRotations(); 992 motion.rotation = emulated.GetRotations();
993 motion.euler = emulated.GetEulerAngles();
987 motion.orientation = emulated.GetOrientation(); 994 motion.orientation = emulated.GetOrientation();
988 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 995 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
989} 996}
@@ -1616,19 +1623,6 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
1616 1623
1617MotionState EmulatedController::GetMotions() const { 1624MotionState EmulatedController::GetMotions() const {
1618 std::unique_lock lock{mutex}; 1625 std::unique_lock lock{mutex};
1619
1620 // Some drivers like mouse motion need constant refreshing
1621 if (force_update_motion) {
1622 for (auto& device : motion_devices) {
1623 if (!device) {
1624 continue;
1625 }
1626 lock.unlock();
1627 device->ForceUpdate();
1628 lock.lock();
1629 }
1630 }
1631
1632 return controller.motion_state; 1626 return controller.motion_state;
1633} 1627}
1634 1628
@@ -1694,8 +1688,21 @@ void EmulatedController::DeleteCallback(int key) {
1694 callback_list.erase(iterator); 1688 callback_list.erase(iterator);
1695} 1689}
1696 1690
1697void EmulatedController::TurboButtonUpdate() { 1691void EmulatedController::StatusUpdate() {
1698 turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2); 1692 turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
1693
1694 // Some drivers like key motion need constant refreshing
1695 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
1696 const auto& raw_status = controller.motion_values[index].raw_status;
1697 auto& device = motion_devices[index];
1698 if (!raw_status.force_update) {
1699 continue;
1700 }
1701 if (!device) {
1702 continue;
1703 }
1704 device->ForceUpdate();
1705 }
1699} 1706}
1700 1707
1701NpadButton EmulatedController::GetTurboButtonMask() const { 1708NpadButton EmulatedController::GetTurboButtonMask() const {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 429655355..88fad2f56 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -106,6 +106,7 @@ struct ControllerMotion {
106 Common::Vec3f accel{}; 106 Common::Vec3f accel{};
107 Common::Vec3f gyro{}; 107 Common::Vec3f gyro{};
108 Common::Vec3f rotation{}; 108 Common::Vec3f rotation{};
109 Common::Vec3f euler{};
109 std::array<Common::Vec3f, 3> orientation{}; 110 std::array<Common::Vec3f, 3> orientation{};
110 bool is_at_rest{}; 111 bool is_at_rest{};
111}; 112};
@@ -289,6 +290,9 @@ public:
289 */ 290 */
290 void SetMotionParam(std::size_t index, Common::ParamPackage param); 291 void SetMotionParam(std::size_t index, Common::ParamPackage param);
291 292
293 /// Auto calibrates the current motion devices
294 void StartMotionCalibration();
295
292 /// Returns the latest button status from the controller with parameters 296 /// Returns the latest button status from the controller with parameters
293 ButtonValues GetButtonsValues() const; 297 ButtonValues GetButtonsValues() const;
294 298
@@ -414,8 +418,8 @@ public:
414 */ 418 */
415 void DeleteCallback(int key); 419 void DeleteCallback(int key);
416 420
417 /// Swaps the state of the turbo buttons 421 /// Swaps the state of the turbo buttons and updates motion input
418 void TurboButtonUpdate(); 422 void StatusUpdate();
419 423
420private: 424private:
421 /// creates input devices from params 425 /// creates input devices from params
@@ -527,7 +531,6 @@ private:
527 bool is_configuring{false}; 531 bool is_configuring{false};
528 bool system_buttons_enabled{true}; 532 bool system_buttons_enabled{true};
529 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 533 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
530 bool force_update_motion{false};
531 u32 turbo_button_state{0}; 534 u32 turbo_button_state{0};
532 535
533 // Temporary values to avoid doing changes while the controller is in configuring mode 536 // Temporary values to avoid doing changes while the controller is in configuring mode
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 7cee39a53..4ccb1c596 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -54,6 +54,7 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
54 case Common::Input::InputType::Analog: 54 case Common::Input::InputType::Analog:
55 status.value = TransformToTrigger(callback).pressed.value; 55 status.value = TransformToTrigger(callback).pressed.value;
56 status.toggle = callback.analog_status.properties.toggle; 56 status.toggle = callback.analog_status.properties.toggle;
57 status.inverted = callback.analog_status.properties.inverted_button;
57 break; 58 break;
58 case Common::Input::InputType::Trigger: 59 case Common::Input::InputType::Trigger:
59 status.value = TransformToTrigger(callback).pressed.value; 60 status.value = TransformToTrigger(callback).pressed.value;
@@ -61,6 +62,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
61 case Common::Input::InputType::Button: 62 case Common::Input::InputType::Button:
62 status = callback.button_status; 63 status = callback.button_status;
63 break; 64 break;
65 case Common::Input::InputType::Motion:
66 status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f;
67 break;
64 default: 68 default:
65 LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type); 69 LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
66 break; 70 break;
@@ -82,7 +86,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
82 .range = 1.0f, 86 .range = 1.0f,
83 .offset = 0.0f, 87 .offset = 0.0f,
84 }; 88 };
85 status.delta_timestamp = 5000; 89 status.delta_timestamp = 1000;
86 status.force_update = true; 90 status.force_update = true;
87 status.accel.x = { 91 status.accel.x = {
88 .value = 0.0f, 92 .value = 0.0f,
@@ -226,6 +230,10 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
226 status = callback.trigger_status; 230 status = callback.trigger_status;
227 calculate_button_value = false; 231 calculate_button_value = false;
228 break; 232 break;
233 case Common::Input::InputType::Motion:
234 status.analog.properties.range = 1.0f;
235 raw_value = callback.motion_status.accel.x.raw_value;
236 break;
229 default: 237 default:
230 LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type); 238 LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
231 break; 239 break;
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp
index 0dd66c1cc..f56f2ae1d 100644
--- a/src/core/hid/motion_input.cpp
+++ b/src/core/hid/motion_input.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 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
4#include "common/math_util.h" 6#include "common/math_util.h"
5#include "core/hid/motion_input.h" 7#include "core/hid/motion_input.h"
6 8
@@ -35,11 +37,17 @@ void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
35 gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue); 37 gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue);
36 gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue); 38 gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue);
37 39
38 // Auto adjust drift to minimize drift 40 // Auto adjust gyro_bias to minimize drift
39 if (!IsMoving(IsAtRestRelaxed)) { 41 if (!IsMoving(IsAtRestRelaxed)) {
40 gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f); 42 gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
41 } 43 }
42 44
45 // Adjust drift when calibration mode is enabled
46 if (calibration_mode) {
47 gyro_bias = (gyro_bias * 0.99f) + (gyroscope * 0.01f);
48 StopCalibration();
49 }
50
43 if (gyro.Length() < gyro_threshold * user_gyro_threshold) { 51 if (gyro.Length() < gyro_threshold * user_gyro_threshold) {
44 gyro = {}; 52 gyro = {};
45 } else { 53 } else {
@@ -51,6 +59,20 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
51 quat = quaternion; 59 quat = quaternion;
52} 60}
53 61
62void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
63 const float cr = std::cos(euler_angles.x * 0.5f);
64 const float sr = std::sin(euler_angles.x * 0.5f);
65 const float cp = std::cos(euler_angles.y * 0.5f);
66 const float sp = std::sin(euler_angles.y * 0.5f);
67 const float cy = std::cos(euler_angles.z * 0.5f);
68 const float sy = std::sin(euler_angles.z * 0.5f);
69
70 quat.w = cr * cp * cy + sr * sp * sy;
71 quat.xyz.x = sr * cp * cy - cr * sp * sy;
72 quat.xyz.y = cr * sp * cy + sr * cp * sy;
73 quat.xyz.z = cr * cp * sy - sr * sp * cy;
74}
75
54void MotionInput::SetGyroBias(const Common::Vec3f& bias) { 76void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
55 gyro_bias = bias; 77 gyro_bias = bias;
56} 78}
@@ -91,6 +113,19 @@ void MotionInput::UpdateRotation(u64 elapsed_time) {
91 rotations += gyro * sample_period; 113 rotations += gyro * sample_period;
92} 114}
93 115
116void MotionInput::Calibrate() {
117 calibration_mode = true;
118 calibration_counter = 0;
119}
120
121void MotionInput::StopCalibration() {
122 if (calibration_counter++ > CalibrationSamples) {
123 calibration_mode = false;
124 ResetQuaternion();
125 ResetRotations();
126 }
127}
128
94// Based on Madgwick's implementation of Mayhony's AHRS algorithm. 129// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
95// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs 130// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
96void MotionInput::UpdateOrientation(u64 elapsed_time) { 131void MotionInput::UpdateOrientation(u64 elapsed_time) {
@@ -222,6 +257,26 @@ Common::Vec3f MotionInput::GetRotations() const {
222 return rotations; 257 return rotations;
223} 258}
224 259
260Common::Vec3f MotionInput::GetEulerAngles() const {
261 // roll (x-axis rotation)
262 const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
263 const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);
264
265 // pitch (y-axis rotation)
266 const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
267 const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
268
269 // yaw (z-axis rotation)
270 const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
271 const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);
272
273 return {
274 std::atan2(sinr_cosp, cosr_cosp),
275 2 * std::atan2(sinp, cosp) - Common::PI / 2,
276 std::atan2(siny_cosp, cosy_cosp),
277 };
278}
279
225void MotionInput::ResetOrientation() { 280void MotionInput::ResetOrientation() {
226 if (!reset_enabled || only_accelerometer) { 281 if (!reset_enabled || only_accelerometer) {
227 return; 282 return;
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h
index 9f3fc1cf7..11678983d 100644
--- a/src/core/hid/motion_input.h
+++ b/src/core/hid/motion_input.h
@@ -23,6 +23,8 @@ public:
23 static constexpr float GyroMaxValue = 5.0f; 23 static constexpr float GyroMaxValue = 5.0f;
24 static constexpr float AccelMaxValue = 7.0f; 24 static constexpr float AccelMaxValue = 7.0f;
25 25
26 static constexpr std::size_t CalibrationSamples = 300;
27
26 explicit MotionInput(); 28 explicit MotionInput();
27 29
28 MotionInput(const MotionInput&) = default; 30 MotionInput(const MotionInput&) = default;
@@ -35,6 +37,7 @@ public:
35 void SetAcceleration(const Common::Vec3f& acceleration); 37 void SetAcceleration(const Common::Vec3f& acceleration);
36 void SetGyroscope(const Common::Vec3f& gyroscope); 38 void SetGyroscope(const Common::Vec3f& gyroscope);
37 void SetQuaternion(const Common::Quaternion<f32>& quaternion); 39 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
40 void SetEulerAngles(const Common::Vec3f& euler_angles);
38 void SetGyroBias(const Common::Vec3f& bias); 41 void SetGyroBias(const Common::Vec3f& bias);
39 void SetGyroThreshold(f32 threshold); 42 void SetGyroThreshold(f32 threshold);
40 43
@@ -48,17 +51,21 @@ public:
48 void UpdateRotation(u64 elapsed_time); 51 void UpdateRotation(u64 elapsed_time);
49 void UpdateOrientation(u64 elapsed_time); 52 void UpdateOrientation(u64 elapsed_time);
50 53
54 void Calibrate();
55
51 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const; 56 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
52 [[nodiscard]] Common::Vec3f GetAcceleration() const; 57 [[nodiscard]] Common::Vec3f GetAcceleration() const;
53 [[nodiscard]] Common::Vec3f GetGyroscope() const; 58 [[nodiscard]] Common::Vec3f GetGyroscope() const;
54 [[nodiscard]] Common::Vec3f GetGyroBias() const; 59 [[nodiscard]] Common::Vec3f GetGyroBias() const;
55 [[nodiscard]] Common::Vec3f GetRotations() const; 60 [[nodiscard]] Common::Vec3f GetRotations() const;
56 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; 61 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
62 [[nodiscard]] Common::Vec3f GetEulerAngles() const;
57 63
58 [[nodiscard]] bool IsMoving(f32 sensitivity) const; 64 [[nodiscard]] bool IsMoving(f32 sensitivity) const;
59 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; 65 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
60 66
61private: 67private:
68 void StopCalibration();
62 void ResetOrientation(); 69 void ResetOrientation();
63 void SetOrientationFromAccelerometer(); 70 void SetOrientationFromAccelerometer();
64 71
@@ -101,6 +108,12 @@ private:
101 108
102 // Use accelerometer values to calculate position 109 // Use accelerometer values to calculate position
103 bool only_accelerometer = true; 110 bool only_accelerometer = true;
111
112 // When enabled it will aggressively adjust for gyro drift
113 bool calibration_mode = false;
114
115 // Used to auto disable calibration mode
116 std::size_t calibration_counter = 0;
104}; 117};
105 118
106} // namespace Core::HID 119} // namespace Core::HID
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 36d0d20d2..49bdc671e 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -35,12 +35,13 @@ namespace {
35using namespace Common::Literals; 35using namespace Common::Literals;
36 36
37u32 GetMemorySizeForInit() { 37u32 GetMemorySizeForInit() {
38 return Settings::values.use_extended_memory_layout ? Smc::MemorySize_8GB : Smc::MemorySize_4GB; 38 return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemorySize_8GB
39 : Smc::MemorySize_4GB;
39} 40}
40 41
41Smc::MemoryArrangement GetMemoryArrangeForInit() { 42Smc::MemoryArrangement GetMemoryArrangeForInit() {
42 return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_8GB 43 return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemoryArrangement_8GB
43 : Smc::MemoryArrangement_4GB; 44 : Smc::MemoryArrangement_4GB;
44} 45}
45} // namespace 46} // namespace
46 47
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 9b71fe371..f384b1568 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -182,8 +182,8 @@ public:
182 explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {} 182 explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {}
183 183
184 static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) { 184 static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
185 const u64 lid = lhs.GetId(); 185 const uintptr_t lid = reinterpret_cast<uintptr_t>(std::addressof(lhs));
186 const u64 rid = rhs.GetId(); 186 const uintptr_t rid = reinterpret_cast<uintptr_t>(std::addressof(rhs));
187 187
188 if (lid < rid) { 188 if (lid < rid) {
189 return -1; 189 return -1;
diff --git a/src/core/hle/kernel/k_event_info.h b/src/core/hle/kernel/k_event_info.h
index 25b3ff594..eacfa5dc6 100644
--- a/src/core/hle/kernel/k_event_info.h
+++ b/src/core/hle/kernel/k_event_info.h
@@ -5,14 +5,15 @@
5 5
6#include <array> 6#include <array>
7 7
8#include <boost/intrusive/list.hpp> 8#include "common/intrusive_list.h"
9 9
10#include "core/hle/kernel/slab_helpers.h" 10#include "core/hle/kernel/slab_helpers.h"
11#include "core/hle/kernel/svc_types.h" 11#include "core/hle/kernel/svc_types.h"
12 12
13namespace Kernel { 13namespace Kernel {
14 14
15class KEventInfo : public KSlabAllocated<KEventInfo>, public boost::intrusive::list_base_hook<> { 15class KEventInfo : public KSlabAllocated<KEventInfo>,
16 public Common::IntrusiveListBaseNode<KEventInfo> {
16public: 17public:
17 struct InfoCreateThread { 18 struct InfoCreateThread {
18 u32 thread_id{}; 19 u32 thread_id{};
diff --git a/src/core/hle/kernel/k_object_name.h b/src/core/hle/kernel/k_object_name.h
index 2d97fc777..a8876fe37 100644
--- a/src/core/hle/kernel/k_object_name.h
+++ b/src/core/hle/kernel/k_object_name.h
@@ -5,7 +5,8 @@
5 5
6#include <array> 6#include <array>
7#include <memory> 7#include <memory>
8#include <boost/intrusive/list.hpp> 8
9#include "common/intrusive_list.h"
9 10
10#include "core/hle/kernel/k_light_lock.h" 11#include "core/hle/kernel/k_light_lock.h"
11#include "core/hle/kernel/slab_helpers.h" 12#include "core/hle/kernel/slab_helpers.h"
@@ -15,13 +16,14 @@ namespace Kernel {
15 16
16class KObjectNameGlobalData; 17class KObjectNameGlobalData;
17 18
18class KObjectName : public KSlabAllocated<KObjectName>, public boost::intrusive::list_base_hook<> { 19class KObjectName : public KSlabAllocated<KObjectName>,
20 public Common::IntrusiveListBaseNode<KObjectName> {
19public: 21public:
20 explicit KObjectName(KernelCore&) {} 22 explicit KObjectName(KernelCore&) {}
21 virtual ~KObjectName() = default; 23 virtual ~KObjectName() = default;
22 24
23 static constexpr size_t NameLengthMax = 12; 25 static constexpr size_t NameLengthMax = 12;
24 using List = boost::intrusive::list<KObjectName>; 26 using List = Common::IntrusiveListBaseTraits<KObjectName>::ListType;
25 27
26 static Result NewFromName(KernelCore& kernel, KAutoObject* obj, const char* name); 28 static Result NewFromName(KernelCore& kernel, KAutoObject* obj, const char* name);
27 static Result Delete(KernelCore& kernel, KAutoObject* obj, const char* name); 29 static Result Delete(KernelCore& kernel, KAutoObject* obj, const char* name);
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index 21c040e62..625280290 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -7,7 +7,7 @@
7#include <string> 7#include <string>
8#include <utility> 8#include <utility>
9 9
10#include <boost/intrusive/list.hpp> 10#include "common/intrusive_list.h"
11 11
12#include "core/hle/kernel/k_server_session.h" 12#include "core/hle/kernel/k_server_session.h"
13#include "core/hle/kernel/k_synchronization_object.h" 13#include "core/hle/kernel/k_synchronization_object.h"
@@ -42,7 +42,7 @@ public:
42 bool IsSignaled() const override; 42 bool IsSignaled() const override;
43 43
44private: 44private:
45 using SessionList = boost::intrusive::list<KServerSession>; 45 using SessionList = Common::IntrusiveListBaseTraits<KServerSession>::ListType;
46 46
47 void CleanupSessions(); 47 void CleanupSessions();
48 48
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 5ee02f556..403891919 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -8,7 +8,7 @@
8#include <string> 8#include <string>
9#include <utility> 9#include <utility>
10 10
11#include <boost/intrusive/list.hpp> 11#include "common/intrusive_list.h"
12 12
13#include "core/hle/kernel/k_light_lock.h" 13#include "core/hle/kernel/k_light_lock.h"
14#include "core/hle/kernel/k_session_request.h" 14#include "core/hle/kernel/k_session_request.h"
@@ -27,7 +27,7 @@ class KSession;
27class KThread; 27class KThread;
28 28
29class KServerSession final : public KSynchronizationObject, 29class KServerSession final : public KSynchronizationObject,
30 public boost::intrusive::list_base_hook<> { 30 public Common::IntrusiveListBaseNode<KServerSession> {
31 KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject); 31 KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
32 32
33 friend class ServiceThread; 33 friend class ServiceThread;
@@ -67,7 +67,8 @@ private:
67 KSession* m_parent{}; 67 KSession* m_parent{};
68 68
69 /// List of threads which are pending a reply. 69 /// List of threads which are pending a reply.
70 boost::intrusive::list<KSessionRequest> m_request_list{}; 70 using RequestList = Common::IntrusiveListBaseTraits<KSessionRequest>::ListType;
71 RequestList m_request_list{};
71 KSessionRequest* m_current_request{}; 72 KSessionRequest* m_current_request{};
72 73
73 KLightLock m_lock; 74 KLightLock m_lock;
diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h
index b5f04907b..283669e0a 100644
--- a/src/core/hle/kernel/k_session_request.h
+++ b/src/core/hle/kernel/k_session_request.h
@@ -5,6 +5,8 @@
5 5
6#include <array> 6#include <array>
7 7
8#include "common/intrusive_list.h"
9
8#include "core/hle/kernel/k_auto_object.h" 10#include "core/hle/kernel/k_auto_object.h"
9#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_memory_block.h" 12#include "core/hle/kernel/k_memory_block.h"
@@ -16,7 +18,7 @@ namespace Kernel {
16 18
17class KSessionRequest final : public KSlabAllocated<KSessionRequest>, 19class KSessionRequest final : public KSlabAllocated<KSessionRequest>,
18 public KAutoObject, 20 public KAutoObject,
19 public boost::intrusive::list_base_hook<> { 21 public Common::IntrusiveListBaseNode<KSessionRequest> {
20 KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject); 22 KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject);
21 23
22public: 24public:
diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h
index 75b73ba39..2d8ff20d6 100644
--- a/src/core/hle/kernel/k_shared_memory_info.h
+++ b/src/core/hle/kernel/k_shared_memory_info.h
@@ -3,7 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <boost/intrusive/list.hpp> 6#include "common/intrusive_list.h"
7 7
8#include "core/hle/kernel/slab_helpers.h" 8#include "core/hle/kernel/slab_helpers.h"
9 9
@@ -12,7 +12,7 @@ namespace Kernel {
12class KSharedMemory; 12class KSharedMemory;
13 13
14class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>, 14class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>,
15 public boost::intrusive::list_base_hook<> { 15 public Common::IntrusiveListBaseNode<KSharedMemoryInfo> {
16 16
17public: 17public:
18 explicit KSharedMemoryInfo(KernelCore&) {} 18 explicit KSharedMemoryInfo(KernelCore&) {}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 9c1a41128..f9814ac8f 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -12,7 +12,7 @@
12#include <utility> 12#include <utility>
13#include <vector> 13#include <vector>
14 14
15#include <boost/intrusive/list.hpp> 15#include "common/intrusive_list.h"
16 16
17#include "common/intrusive_red_black_tree.h" 17#include "common/intrusive_red_black_tree.h"
18#include "common/spin_lock.h" 18#include "common/spin_lock.h"
@@ -119,7 +119,7 @@ s32 GetCurrentCoreId(KernelCore& kernel);
119Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel); 119Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel);
120 120
121class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, 121class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
122 public boost::intrusive::list_base_hook<>, 122 public Common::IntrusiveListBaseNode<KThread>,
123 public KTimerTask { 123 public KTimerTask {
124 KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); 124 KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
125 125
@@ -138,7 +138,7 @@ public:
138public: 138public:
139 using ThreadContext32 = Core::ARM_Interface::ThreadContext32; 139 using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
140 using ThreadContext64 = Core::ARM_Interface::ThreadContext64; 140 using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
141 using WaiterList = boost::intrusive::list<KThread>; 141 using WaiterList = Common::IntrusiveListBaseTraits<KThread>::ListType;
142 142
143 /** 143 /**
144 * Gets the thread's current priority 144 * Gets the thread's current priority
@@ -750,8 +750,9 @@ private:
750 ConditionVariableThreadTreeTraits::TreeType<LockWithPriorityInheritanceComparator>; 750 ConditionVariableThreadTreeTraits::TreeType<LockWithPriorityInheritanceComparator>;
751 751
752public: 752public:
753 class LockWithPriorityInheritanceInfo : public KSlabAllocated<LockWithPriorityInheritanceInfo>, 753 class LockWithPriorityInheritanceInfo
754 public boost::intrusive::list_base_hook<> { 754 : public KSlabAllocated<LockWithPriorityInheritanceInfo>,
755 public Common::IntrusiveListBaseNode<LockWithPriorityInheritanceInfo> {
755 public: 756 public:
756 explicit LockWithPriorityInheritanceInfo(KernelCore&) {} 757 explicit LockWithPriorityInheritanceInfo(KernelCore&) {}
757 758
@@ -839,7 +840,7 @@ public:
839 840
840private: 841private:
841 using LockWithPriorityInheritanceInfoList = 842 using LockWithPriorityInheritanceInfoList =
842 boost::intrusive::list<LockWithPriorityInheritanceInfo>; 843 Common::IntrusiveListBaseTraits<LockWithPriorityInheritanceInfo>::ListType;
843 844
844 ConditionVariableThreadTree* m_condvar_tree{}; 845 ConditionVariableThreadTree* m_condvar_tree{};
845 u64 m_condvar_key{}; 846 u64 m_condvar_key{};
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4f3366c9d..f33600ca5 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -95,7 +95,7 @@ struct KernelCore::Impl {
95 pt_heap_region.GetSize()); 95 pt_heap_region.GetSize());
96 } 96 }
97 97
98 InitializeHackSharedMemory(); 98 InitializeHackSharedMemory(kernel);
99 RegisterHostThread(nullptr); 99 RegisterHostThread(nullptr);
100 } 100 }
101 101
@@ -216,10 +216,12 @@ struct KernelCore::Impl {
216 auto* main_thread{Kernel::KThread::Create(system.Kernel())}; 216 auto* main_thread{Kernel::KThread::Create(system.Kernel())};
217 main_thread->SetCurrentCore(core); 217 main_thread->SetCurrentCore(core);
218 ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess()); 218 ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
219 KThread::Register(system.Kernel(), main_thread);
219 220
220 auto* idle_thread{Kernel::KThread::Create(system.Kernel())}; 221 auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
221 idle_thread->SetCurrentCore(core); 222 idle_thread->SetCurrentCore(core);
222 ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess()); 223 ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
224 KThread::Register(system.Kernel(), idle_thread);
223 225
224 schedulers[i]->Initialize(main_thread, idle_thread, core); 226 schedulers[i]->Initialize(main_thread, idle_thread, core);
225 } 227 }
@@ -230,6 +232,7 @@ struct KernelCore::Impl {
230 const Core::Timing::CoreTiming& core_timing) { 232 const Core::Timing::CoreTiming& core_timing) {
231 system_resource_limit = KResourceLimit::Create(system.Kernel()); 233 system_resource_limit = KResourceLimit::Create(system.Kernel());
232 system_resource_limit->Initialize(&core_timing); 234 system_resource_limit->Initialize(&core_timing);
235 KResourceLimit::Register(kernel, system_resource_limit);
233 236
234 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()}; 237 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
235 const auto total_size{sizes.first}; 238 const auto total_size{sizes.first};
@@ -355,6 +358,7 @@ struct KernelCore::Impl {
355 ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {}, 358 ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {},
356 core_id) 359 core_id)
357 .IsSuccess()); 360 .IsSuccess());
361 KThread::Register(system.Kernel(), shutdown_threads[core_id]);
358 } 362 }
359 } 363 }
360 364
@@ -729,7 +733,7 @@ struct KernelCore::Impl {
729 memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); 733 memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
730 } 734 }
731 735
732 void InitializeHackSharedMemory() { 736 void InitializeHackSharedMemory(KernelCore& kernel) {
733 // Setup memory regions for emulated processes 737 // Setup memory regions for emulated processes
734 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel 738 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel
735 constexpr std::size_t hid_size{0x40000}; 739 constexpr std::size_t hid_size{0x40000};
@@ -746,14 +750,23 @@ struct KernelCore::Impl {
746 750
747 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 751 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
748 Svc::MemoryPermission::Read, hid_size); 752 Svc::MemoryPermission::Read, hid_size);
753 KSharedMemory::Register(kernel, hid_shared_mem);
754
749 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 755 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
750 Svc::MemoryPermission::Read, font_size); 756 Svc::MemoryPermission::Read, font_size);
757 KSharedMemory::Register(kernel, font_shared_mem);
758
751 irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 759 irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
752 Svc::MemoryPermission::Read, irs_size); 760 Svc::MemoryPermission::Read, irs_size);
761 KSharedMemory::Register(kernel, irs_shared_mem);
762
753 time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 763 time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
754 Svc::MemoryPermission::Read, time_size); 764 Svc::MemoryPermission::Read, time_size);
765 KSharedMemory::Register(kernel, time_shared_mem);
766
755 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 767 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
756 Svc::MemoryPermission::Read, hidbus_size); 768 Svc::MemoryPermission::Read, hidbus_size);
769 KSharedMemory::Register(kernel, hidbus_shared_mem);
757 } 770 }
758 771
759 std::mutex registered_objects_lock; 772 std::mutex registered_objects_lock;
@@ -1072,12 +1085,15 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process,
1072 // Commit the thread reservation. 1085 // Commit the thread reservation.
1073 thread_reservation.Commit(); 1086 thread_reservation.Commit();
1074 1087
1088 // Register the thread.
1089 KThread::Register(kernel, thread);
1090
1075 return std::jthread( 1091 return std::jthread(
1076 [&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] { 1092 [&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] {
1077 // Set the thread name. 1093 // Set the thread name.
1078 Common::SetCurrentThreadName(thread_name.c_str()); 1094 Common::SetCurrentThreadName(thread_name.c_str());
1079 1095
1080 // Register the thread. 1096 // Set the thread as current.
1081 kernel.RegisterHostThread(thread); 1097 kernel.RegisterHostThread(thread);
1082 1098
1083 // Run the callback. 1099 // Run the callback.
@@ -1099,6 +1115,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
1099 // Ensure that we don't hold onto any extra references. 1115 // Ensure that we don't hold onto any extra references.
1100 SCOPE_EXIT({ process->Close(); }); 1116 SCOPE_EXIT({ process->Close(); });
1101 1117
1118 // Register the new process.
1119 KProcess::Register(*this, process);
1120
1102 // Run the host thread. 1121 // Run the host thread.
1103 return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func)); 1122 return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func));
1104} 1123}
@@ -1124,6 +1143,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
1124 // Ensure that we don't hold onto any extra references. 1143 // Ensure that we don't hold onto any extra references.
1125 SCOPE_EXIT({ process->Close(); }); 1144 SCOPE_EXIT({ process->Close(); });
1126 1145
1146 // Register the new process.
1147 KProcess::Register(*this, process);
1148
1127 // Reserve a new thread from the process resource limit. 1149 // Reserve a new thread from the process resource limit.
1128 KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax); 1150 KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax);
1129 ASSERT(thread_reservation.Succeeded()); 1151 ASSERT(thread_reservation.Succeeded());
@@ -1136,6 +1158,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
1136 // Commit the thread reservation. 1158 // Commit the thread reservation.
1137 thread_reservation.Commit(); 1159 thread_reservation.Commit();
1138 1160
1161 // Register the new thread.
1162 KThread::Register(*this, thread);
1163
1139 // Begin running the thread. 1164 // Begin running the thread.
1140 ASSERT(R_SUCCEEDED(thread->Run())); 1165 ASSERT(R_SUCCEEDED(thread->Run()));
1141} 1166}
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index 93c9f2a55..8b754e9d4 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -11,7 +11,7 @@
11#include "core/hle/service/am/am.h" 11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/applet_cabinet.h" 12#include "core/hle/service/am/applets/applet_cabinet.h"
13#include "core/hle/service/mii/mii_manager.h" 13#include "core/hle/service/mii/mii_manager.h"
14#include "core/hle/service/nfp/nfp_device.h" 14#include "core/hle/service/nfc/common/device.h"
15 15
16namespace Service::AM::Applets { 16namespace Service::AM::Applets {
17 17
@@ -72,10 +72,10 @@ void Cabinet::Execute() {
72 72
73 // TODO: listen on all controllers 73 // TODO: listen on all controllers
74 if (nfp_device == nullptr) { 74 if (nfp_device == nullptr) {
75 nfp_device = std::make_shared<Service::NFP::NfpDevice>( 75 nfp_device = std::make_shared<Service::NFC::NfcDevice>(
76 system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event); 76 system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
77 nfp_device->Initialize(); 77 nfp_device->Initialize();
78 nfp_device->StartDetection(Service::NFP::TagProtocol::All); 78 nfp_device->StartDetection(Service::NFC::NfcProtocol::All);
79 } 79 }
80 80
81 const Core::Frontend::CabinetParameters parameters{ 81 const Core::Frontend::CabinetParameters parameters{
@@ -106,20 +106,22 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
106 Cancel(); 106 Cancel();
107 } 107 }
108 108
109 if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && 109 if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound &&
110 nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { 110 nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) {
111 Cancel(); 111 Cancel();
112 } 112 }
113 113
114 if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) { 114 if (nfp_device->GetCurrentState() == Service::NFC::DeviceState::TagFound) {
115 nfp_device->Mount(Service::NFP::MountTarget::All); 115 nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All);
116 } 116 }
117 117
118 switch (applet_input_common.applet_mode) { 118 switch (applet_input_common.applet_mode) {
119 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: { 119 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
120 Service::NFP::AmiiboName name{}; 120 Service::NFP::RegisterInfoPrivate register_info{};
121 std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1)); 121 std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
122 nfp_device->SetRegisterInfoPrivate(name); 122 std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
123
124 nfp_device->SetRegisterInfoPrivate(register_info);
123 break; 125 break;
124 } 126 }
125 case Service::NFP::CabinetMode::StartGameDataEraser: 127 case Service::NFP::CabinetMode::StartGameDataEraser:
@@ -139,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
139 applet_output.device_handle = applet_input_common.device_handle; 141 applet_output.device_handle = applet_input_common.device_handle;
140 applet_output.result = CabinetResult::Cancel; 142 applet_output.result = CabinetResult::Cancel;
141 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); 143 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
142 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info); 144 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false);
143 nfp_device->Finalize(); 145 nfp_device->Finalize();
144 146
145 if (reg_result.IsSuccess()) { 147 if (reg_result.IsSuccess()) {
diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/applets/applet_cabinet.h
index edd295a27..b56427021 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.h
+++ b/src/core/hle/service/am/applets/applet_cabinet.h
@@ -19,8 +19,8 @@ namespace Core {
19class System; 19class System;
20} // namespace Core 20} // namespace Core
21 21
22namespace Service::NFP { 22namespace Service::NFC {
23class NfpDevice; 23class NfcDevice;
24} 24}
25 25
26namespace Service::AM::Applets { 26namespace Service::AM::Applets {
@@ -96,7 +96,7 @@ private:
96 Core::System& system; 96 Core::System& system;
97 97
98 bool is_complete{false}; 98 bool is_complete{false};
99 std::shared_ptr<Service::NFP::NfpDevice> nfp_device; 99 std::shared_ptr<Service::NFC::NfcDevice> nfp_device;
100 Kernel::KEvent* availability_change_event; 100 Kernel::KEvent* availability_change_event;
101 KernelHelpers::ServiceContext service_context; 101 KernelHelpers::ServiceContext service_context;
102 StartParamForAmiiboSettings applet_input_common{}; 102 StartParamForAmiiboSettings applet_input_common{};
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 8abf71608..ef4aec4ea 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -423,8 +423,8 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
423 return; 423 return;
424 } 424 }
425 425
426 // This function is unique to yuzu for the turbo buttons to work properly 426 // This function is unique to yuzu for the turbo buttons and motion to work properly
427 controller.device->TurboButtonUpdate(); 427 controller.device->StatusUpdate();
428 428
429 auto& pad_entry = controller.npad_pad_state; 429 auto& pad_entry = controller.npad_pad_state;
430 auto& trigger_entry = controller.npad_trigger_state; 430 auto& trigger_entry = controller.npad_trigger_state;
diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h
index e4cb4e1f2..0e222362e 100644
--- a/src/core/hle/service/ipc_helpers.h
+++ b/src/core/hle/service/ipc_helpers.h
@@ -156,6 +156,7 @@ public:
156 156
157 auto* session = Kernel::KSession::Create(kernel); 157 auto* session = Kernel::KSession::Create(kernel);
158 session->Initialize(nullptr, 0); 158 session->Initialize(nullptr, 0);
159 Kernel::KSession::Register(kernel, session);
159 160
160 auto next_manager = std::make_shared<Service::SessionRequestManager>( 161 auto next_manager = std::make_shared<Service::SessionRequestManager>(
161 kernel, manager->GetServerManager()); 162 kernel, manager->GetServerManager());
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index a39ce5212..6a313a03b 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -25,6 +25,9 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_)
25 Kernel::KProcess::ProcessType::KernelInternal, 25 Kernel::KProcess::ProcessType::KernelInternal,
26 kernel.GetSystemResourceLimit()) 26 kernel.GetSystemResourceLimit())
27 .IsSuccess()); 27 .IsSuccess());
28
29 // Register the process.
30 Kernel::KProcess::Register(kernel, process);
28 process_created = true; 31 process_created = true;
29} 32}
30 33
diff --git a/src/core/hle/service/mutex.cpp b/src/core/hle/service/mutex.cpp
index 07589a0f0..b0ff71d1b 100644
--- a/src/core/hle/service/mutex.cpp
+++ b/src/core/hle/service/mutex.cpp
@@ -12,6 +12,9 @@ Mutex::Mutex(Core::System& system) : m_system(system) {
12 m_event = Kernel::KEvent::Create(system.Kernel()); 12 m_event = Kernel::KEvent::Create(system.Kernel());
13 m_event->Initialize(nullptr); 13 m_event->Initialize(nullptr);
14 14
15 // Register the event.
16 Kernel::KEvent::Register(system.Kernel(), m_event);
17
15 ASSERT(R_SUCCEEDED(m_event->Signal())); 18 ASSERT(R_SUCCEEDED(m_event->Signal()));
16} 19}
17 20
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
index a3622e792..f3901ee8d 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
@@ -12,7 +12,7 @@
12#include "common/fs/fs.h" 12#include "common/fs/fs.h"
13#include "common/fs/path_util.h" 13#include "common/fs/path_util.h"
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15#include "core/hle/service/nfp/amiibo_crypto.h" 15#include "core/hle/service/nfc/common/amiibo_crypto.h"
16 16
17namespace Service::NFP::AmiiboCrypto { 17namespace Service::NFP::AmiiboCrypto {
18 18
@@ -55,7 +55,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
55 if (amiibo_data.constant_value != 0xA5) { 55 if (amiibo_data.constant_value != 0xA5) {
56 return false; 56 return false;
57 } 57 }
58 if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { 58 if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) {
59 return false; 59 return false;
60 } 60 }
61 if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { 61 if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) {
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h
index f6208ee6b..bf3044ed9 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.h
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.h
@@ -24,9 +24,9 @@ using DrgbOutput = std::array<u8, 0x20>;
24struct HashSeed { 24struct HashSeed {
25 u16_be magic; 25 u16_be magic;
26 std::array<u8, 0xE> padding; 26 std::array<u8, 0xE> padding;
27 UniqueSerialNumber uid_1; 27 NFC::UniqueSerialNumber uid_1;
28 u8 nintendo_id_1; 28 u8 nintendo_id_1;
29 UniqueSerialNumber uid_2; 29 NFC::UniqueSerialNumber uid_2;
30 u8 nintendo_id_2; 30 u8 nintendo_id_2;
31 std::array<u8, 0x20> keygen_salt; 31 std::array<u8, 0x20> keygen_salt;
32}; 32};
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 3f9af53c8..e5de65ce0 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -1,8 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 <array>
5
6#ifdef _MSC_VER 4#ifdef _MSC_VER
7#pragma warning(push) 5#pragma warning(push)
8#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used 6#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
@@ -26,21 +24,22 @@
26#include "core/hle/service/ipc_helpers.h" 24#include "core/hle/service/ipc_helpers.h"
27#include "core/hle/service/mii/mii_manager.h" 25#include "core/hle/service/mii/mii_manager.h"
28#include "core/hle/service/mii/types.h" 26#include "core/hle/service/mii/types.h"
29#include "core/hle/service/nfp/amiibo_crypto.h" 27#include "core/hle/service/nfc/common/amiibo_crypto.h"
30#include "core/hle/service/nfp/nfp_device.h" 28#include "core/hle/service/nfc/common/device.h"
31#include "core/hle/service/nfp/nfp_result.h" 29#include "core/hle/service/nfc/mifare_result.h"
30#include "core/hle/service/nfc/nfc_result.h"
32#include "core/hle/service/time/time_manager.h" 31#include "core/hle/service/time/time_manager.h"
33#include "core/hle/service/time/time_zone_content_manager.h" 32#include "core/hle/service/time/time_zone_content_manager.h"
34#include "core/hle/service/time/time_zone_types.h" 33#include "core/hle/service/time/time_zone_types.h"
35 34
36namespace Service::NFP { 35namespace Service::NFC {
37NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, 36NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
38 KernelHelpers::ServiceContext& service_context_, 37 KernelHelpers::ServiceContext& service_context_,
39 Kernel::KEvent* availability_change_event_) 38 Kernel::KEvent* availability_change_event_)
40 : npad_id{npad_id_}, system{system_}, service_context{service_context_}, 39 : npad_id{npad_id_}, system{system_}, service_context{service_context_},
41 availability_change_event{availability_change_event_} { 40 availability_change_event{availability_change_event_} {
42 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); 41 activate_event = service_context.CreateEvent("NFC:ActivateEvent");
43 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); 42 deactivate_event = service_context.CreateEvent("NFC:DeactivateEvent");
44 npad_device = system.HIDCore().GetEmulatedController(npad_id); 43 npad_device = system.HIDCore().GetEmulatedController(npad_id);
45 44
46 Core::HID::ControllerUpdateCallback engine_callback{ 45 Core::HID::ControllerUpdateCallback engine_callback{
@@ -54,9 +53,9 @@ NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
54 current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; 53 current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point;
55} 54}
56 55
57NfpDevice::~NfpDevice() { 56NfcDevice::~NfcDevice() {
58 activate_event->Close(); 57 service_context.CloseEvent(activate_event);
59 deactivate_event->Close(); 58 service_context.CloseEvent(deactivate_event);
60 if (!is_controller_set) { 59 if (!is_controller_set) {
61 return; 60 return;
62 } 61 }
@@ -64,7 +63,7 @@ NfpDevice::~NfpDevice() {
64 is_controller_set = false; 63 is_controller_set = false;
65}; 64};
66 65
67void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { 66void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
68 if (!is_initalized) { 67 if (!is_initalized) {
69 return; 68 return;
70 } 69 }
@@ -92,14 +91,14 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
92 const auto nfc_status = npad_device->GetNfc(); 91 const auto nfc_status = npad_device->GetNfc();
93 switch (nfc_status.state) { 92 switch (nfc_status.state) {
94 case Common::Input::NfcState::NewAmiibo: 93 case Common::Input::NfcState::NewAmiibo:
95 LoadAmiibo(nfc_status.data); 94 LoadNfcTag(nfc_status.data);
96 break; 95 break;
97 case Common::Input::NfcState::AmiiboRemoved: 96 case Common::Input::NfcState::AmiiboRemoved:
98 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { 97 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
99 break; 98 break;
100 } 99 }
101 if (device_state != DeviceState::SearchingForTag) { 100 if (device_state != DeviceState::SearchingForTag) {
102 CloseAmiibo(); 101 CloseNfcTag();
103 } 102 }
104 break; 103 break;
105 default: 104 default:
@@ -107,28 +106,29 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
107 } 106 }
108} 107}
109 108
110bool NfpDevice::LoadAmiibo(std::span<const u8> data) { 109bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
111 if (device_state != DeviceState::SearchingForTag) { 110 if (device_state != DeviceState::SearchingForTag) {
112 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); 111 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
113 return false; 112 return false;
114 } 113 }
115 114
116 if (data.size() != sizeof(EncryptedNTAG215File)) { 115 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
117 LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); 116 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
118 return false; 117 return false;
119 } 118 }
120 119
121 // TODO: Filter by allowed_protocols here 120 mifare_data.resize(data.size());
121 memcpy(mifare_data.data(), data.data(), data.size());
122 122
123 memcpy(&tag_data, data.data(), sizeof(EncryptedNTAG215File)); 123 memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
124 is_plain_amiibo = AmiiboCrypto::IsAmiiboValid(tag_data); 124 is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data);
125 125
126 if (is_plain_amiibo) { 126 if (is_plain_amiibo) {
127 encrypted_tag_data = AmiiboCrypto::EncodedDataToNfcData(tag_data); 127 encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(tag_data);
128 LOG_INFO(Service_NFP, "Using plain amiibo"); 128 LOG_INFO(Service_NFP, "Using plain amiibo");
129 } else { 129 } else {
130 tag_data = {}; 130 tag_data = {};
131 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); 131 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
132 } 132 }
133 133
134 device_state = DeviceState::TagFound; 134 device_state = DeviceState::TagFound;
@@ -137,8 +137,8 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
137 return true; 137 return true;
138} 138}
139 139
140void NfpDevice::CloseAmiibo() { 140void NfcDevice::CloseNfcTag() {
141 LOG_INFO(Service_NFP, "Remove amiibo"); 141 LOG_INFO(Service_NFC, "Remove nfc tag");
142 142
143 if (device_state == DeviceState::TagMounted) { 143 if (device_state == DeviceState::TagMounted) {
144 Unmount(); 144 Unmount();
@@ -147,26 +147,28 @@ void NfpDevice::CloseAmiibo() {
147 device_state = DeviceState::TagRemoved; 147 device_state = DeviceState::TagRemoved;
148 encrypted_tag_data = {}; 148 encrypted_tag_data = {};
149 tag_data = {}; 149 tag_data = {};
150 mifare_data = {};
150 activate_event->GetReadableEvent().Clear(); 151 activate_event->GetReadableEvent().Clear();
151 deactivate_event->Signal(); 152 deactivate_event->Signal();
152} 153}
153 154
154Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { 155Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
155 return activate_event->GetReadableEvent(); 156 return activate_event->GetReadableEvent();
156} 157}
157 158
158Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { 159Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
159 return deactivate_event->GetReadableEvent(); 160 return deactivate_event->GetReadableEvent();
160} 161}
161 162
162void NfpDevice::Initialize() { 163void NfcDevice::Initialize() {
163 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; 164 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
164 encrypted_tag_data = {}; 165 encrypted_tag_data = {};
165 tag_data = {}; 166 tag_data = {};
167 mifare_data = {};
166 is_initalized = true; 168 is_initalized = true;
167} 169}
168 170
169void NfpDevice::Finalize() { 171void NfcDevice::Finalize() {
170 if (device_state == DeviceState::TagMounted) { 172 if (device_state == DeviceState::TagMounted) {
171 Unmount(); 173 Unmount();
172 } 174 }
@@ -177,17 +179,17 @@ void NfpDevice::Finalize() {
177 is_initalized = false; 179 is_initalized = false;
178} 180}
179 181
180Result NfpDevice::StartDetection(TagProtocol allowed_protocol) { 182Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
181 if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { 183 if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
182 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 184 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
183 return WrongDeviceState; 185 return ResultWrongDeviceState;
184 } 186 }
185 187
186 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, 188 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
187 Common::Input::PollingMode::NFC) != 189 Common::Input::PollingMode::NFC) !=
188 Common::Input::DriverResult::Success) { 190 Common::Input::DriverResult::Success) {
189 LOG_ERROR(Service_NFP, "Nfc not supported"); 191 LOG_ERROR(Service_NFC, "Nfc not supported");
190 return NfcDisabled; 192 return ResultNfcDisabled;
191 } 193 }
192 194
193 device_state = DeviceState::SearchingForTag; 195 device_state = DeviceState::SearchingForTag;
@@ -195,7 +197,7 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
195 return ResultSuccess; 197 return ResultSuccess;
196} 198}
197 199
198Result NfpDevice::StopDetection() { 200Result NfcDevice::StopDetection() {
199 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, 201 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
200 Common::Input::PollingMode::Active); 202 Common::Input::PollingMode::Active);
201 203
@@ -204,7 +206,7 @@ Result NfpDevice::StopDetection() {
204 } 206 }
205 207
206 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { 208 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
207 CloseAmiibo(); 209 CloseNfcTag();
208 } 210 }
209 211
210 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { 212 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
@@ -212,94 +214,129 @@ Result NfpDevice::StopDetection() {
212 return ResultSuccess; 214 return ResultSuccess;
213 } 215 }
214 216
215 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 217 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
216 return WrongDeviceState; 218 return ResultWrongDeviceState;
217} 219}
218 220
219Result NfpDevice::Flush() { 221Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
220 if (device_state != DeviceState::TagMounted) { 222 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
221 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 223 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
222 if (device_state == DeviceState::TagRemoved) { 224 if (device_state == DeviceState::TagRemoved) {
223 return TagRemoved; 225 return ResultTagRemoved;
224 } 226 }
225 return WrongDeviceState; 227 return ResultWrongDeviceState;
226 } 228 }
227 229
228 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 230 if (is_mifare) {
229 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 231 tag_info = {
230 return WrongDeviceState; 232 .uuid = encrypted_tag_data.uuid.uid,
233 .uuid_extension = {},
234 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
235 .protocol = NfcProtocol::TypeA,
236 .tag_type = TagType::Type4,
237 };
238 return ResultSuccess;
231 } 239 }
232 240
233 auto& settings = tag_data.settings; 241 // Protocol and tag type may change here
234 242 tag_info = {
235 const auto& current_date = GetAmiiboDate(current_posix_time); 243 .uuid = encrypted_tag_data.uuid.uid,
236 if (settings.write_date.raw_date != current_date.raw_date) { 244 .uuid_extension = {},
237 settings.write_date = current_date; 245 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
238 UpdateSettingsCrc(); 246 .protocol = NfcProtocol::TypeA,
239 } 247 .tag_type = TagType::Type2,
248 };
240 249
241 tag_data.write_counter++; 250 return ResultSuccess;
251}
242 252
243 FlushWithBreak(BreakType::Normal); 253Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters,
254 std::span<MifareReadBlockData> read_block_data) const {
255 Result result = ResultSuccess;
244 256
245 is_data_moddified = false; 257 for (std::size_t i = 0; i < parameters.size(); i++) {
258 result = ReadMifare(parameters[i], read_block_data[i]);
259 if (result.IsError()) {
260 break;
261 }
262 }
246 263
247 return ResultSuccess; 264 return result;
248} 265}
249 266
250Result NfpDevice::FlushDebug() { 267Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
251 if (device_state != DeviceState::TagMounted) { 268 MifareReadBlockData& read_block_data) const {
269 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
270 read_block_data.sector_number = parameter.sector_number;
271
272 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
252 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 273 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
253 if (device_state == DeviceState::TagRemoved) { 274 if (device_state == DeviceState::TagRemoved) {
254 return TagRemoved; 275 return ResultTagRemoved;
255 } 276 }
256 return WrongDeviceState; 277 return ResultWrongDeviceState;
257 } 278 }
258 279
259 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 280 if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
260 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 281 return Mifare::ResultReadError;
261 return WrongDeviceState;
262 } 282 }
263 283
264 tag_data.write_counter++; 284 // TODO: Use parameter.sector_key to read encrypted data
265 285 memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock));
266 FlushWithBreak(BreakType::Normal);
267
268 is_data_moddified = false;
269 286
270 return ResultSuccess; 287 return ResultSuccess;
271} 288}
272 289
273Result NfpDevice::FlushWithBreak(BreakType break_type) { 290Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) {
274 if (break_type != BreakType::Normal) { 291 Result result = ResultSuccess;
275 LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type);
276 return WrongDeviceState;
277 }
278 292
279 std::vector<u8> data(sizeof(EncryptedNTAG215File)); 293 for (std::size_t i = 0; i < parameters.size(); i++) {
280 if (is_plain_amiibo) { 294 result = WriteMifare(parameters[i]);
281 memcpy(data.data(), &tag_data, sizeof(tag_data)); 295 if (result.IsError()) {
282 } else { 296 break;
283 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
284 LOG_ERROR(Service_NFP, "Failed to encode data");
285 return WriteAmiiboFailed;
286 } 297 }
287
288 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
289 } 298 }
290 299
291 if (!npad_device->WriteNfc(data)) { 300 if (!npad_device->WriteNfc(mifare_data)) {
292 LOG_ERROR(Service_NFP, "Error writing to file"); 301 LOG_ERROR(Service_NFP, "Error writing to file");
293 return WriteAmiiboFailed; 302 return Mifare::ResultReadError;
303 }
304
305 return result;
306}
307
308Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) {
309 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
310
311 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
312 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
313 if (device_state == DeviceState::TagRemoved) {
314 return ResultTagRemoved;
315 }
316 return ResultWrongDeviceState;
317 }
318
319 if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
320 return Mifare::ResultReadError;
294 } 321 }
295 322
323 // TODO: Use parameter.sector_key to encrypt the data
324 memcpy(mifare_data.data() + sector_index, parameter.data.data(), sizeof(DataBlock));
325
296 return ResultSuccess; 326 return ResultSuccess;
297} 327}
298 328
299Result NfpDevice::Mount(MountTarget mount_target_) { 329Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
330 std::span<const u8> command_data,
331 std::span<u8> out_data) {
332 // Not implemented
333 return ResultSuccess;
334}
335
336Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) {
300 if (device_state != DeviceState::TagFound) { 337 if (device_state != DeviceState::TagFound) {
301 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 338 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
302 return WrongDeviceState; 339 return ResultWrongDeviceState;
303 } 340 }
304 341
305 // The loaded amiibo is not encrypted 342 // The loaded amiibo is not encrypted
@@ -309,22 +346,22 @@ Result NfpDevice::Mount(MountTarget mount_target_) {
309 return ResultSuccess; 346 return ResultSuccess;
310 } 347 }
311 348
312 if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { 349 if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
313 LOG_ERROR(Service_NFP, "Not an amiibo"); 350 LOG_ERROR(Service_NFP, "Not an amiibo");
314 return NotAnAmiibo; 351 return ResultNotAnAmiibo;
315 } 352 }
316 353
317 // Mark amiibos as read only when keys are missing 354 // Mark amiibos as read only when keys are missing
318 if (!AmiiboCrypto::IsKeyAvailable()) { 355 if (!NFP::AmiiboCrypto::IsKeyAvailable()) {
319 LOG_ERROR(Service_NFP, "No keys detected"); 356 LOG_ERROR(Service_NFP, "No keys detected");
320 device_state = DeviceState::TagMounted; 357 device_state = DeviceState::TagMounted;
321 mount_target = MountTarget::Rom; 358 mount_target = NFP::MountTarget::Rom;
322 return ResultSuccess; 359 return ResultSuccess;
323 } 360 }
324 361
325 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { 362 if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
326 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); 363 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
327 return CorruptedData; 364 return ResultCorruptedData;
328 } 365 }
329 366
330 device_state = DeviceState::TagMounted; 367 device_state = DeviceState::TagMounted;
@@ -332,13 +369,13 @@ Result NfpDevice::Mount(MountTarget mount_target_) {
332 return ResultSuccess; 369 return ResultSuccess;
333} 370}
334 371
335Result NfpDevice::Unmount() { 372Result NfcDevice::Unmount() {
336 if (device_state != DeviceState::TagMounted) { 373 if (device_state != DeviceState::TagMounted) {
337 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 374 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
338 if (device_state == DeviceState::TagRemoved) { 375 if (device_state == DeviceState::TagRemoved) {
339 return TagRemoved; 376 return ResultTagRemoved;
340 } 377 }
341 return WrongDeviceState; 378 return ResultWrongDeviceState;
342 } 379 }
343 380
344 // Save data before unloading the amiibo 381 // Save data before unloading the amiibo
@@ -347,43 +384,123 @@ Result NfpDevice::Unmount() {
347 } 384 }
348 385
349 device_state = DeviceState::TagFound; 386 device_state = DeviceState::TagFound;
350 mount_target = MountTarget::None; 387 mount_target = NFP::MountTarget::None;
351 is_app_area_open = false; 388 is_app_area_open = false;
352 389
353 return ResultSuccess; 390 return ResultSuccess;
354} 391}
355 392
356Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { 393Result NfcDevice::Flush() {
357 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { 394 if (device_state != DeviceState::TagMounted) {
358 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 395 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
359 if (device_state == DeviceState::TagRemoved) { 396 if (device_state == DeviceState::TagRemoved) {
360 return TagRemoved; 397 return ResultTagRemoved;
361 } 398 }
362 return WrongDeviceState; 399 return ResultWrongDeviceState;
363 } 400 }
364 401
365 tag_info = { 402 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
366 .uuid = encrypted_tag_data.uuid.uid, 403 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
367 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()), 404 return ResultWrongDeviceState;
368 .protocol = TagProtocol::TypeA, 405 }
369 .tag_type = TagType::Type2, 406
370 }; 407 auto& settings = tag_data.settings;
408
409 const auto& current_date = GetAmiiboDate(current_posix_time);
410 if (settings.write_date.raw_date != current_date.raw_date) {
411 settings.write_date = current_date;
412 UpdateSettingsCrc();
413 }
414
415 tag_data.write_counter++;
416
417 FlushWithBreak(NFP::BreakType::Normal);
418
419 is_data_moddified = false;
371 420
372 return ResultSuccess; 421 return ResultSuccess;
373} 422}
374 423
375Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { 424Result NfcDevice::FlushDebug() {
425 if (device_state != DeviceState::TagMounted) {
426 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
427 if (device_state == DeviceState::TagRemoved) {
428 return ResultTagRemoved;
429 }
430 return ResultWrongDeviceState;
431 }
432
433 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
434 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
435 return ResultWrongDeviceState;
436 }
437
438 tag_data.write_counter++;
439
440 FlushWithBreak(NFP::BreakType::Normal);
441
442 is_data_moddified = false;
443
444 return ResultSuccess;
445}
446
447Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
448 if (break_type != NFP::BreakType::Normal) {
449 LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type);
450 return ResultWrongDeviceState;
451 }
452
453 std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
454 if (is_plain_amiibo) {
455 memcpy(data.data(), &tag_data, sizeof(tag_data));
456 } else {
457 if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
458 LOG_ERROR(Service_NFP, "Failed to encode data");
459 return ResultWriteAmiiboFailed;
460 }
461
462 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
463 }
464
465 if (!npad_device->WriteNfc(data)) {
466 LOG_ERROR(Service_NFP, "Error writing to file");
467 return ResultWriteAmiiboFailed;
468 }
469
470 return ResultSuccess;
471}
472
473Result NfcDevice::Restore() {
474 if (device_state != DeviceState::TagMounted) {
475 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
476 if (device_state == DeviceState::TagRemoved) {
477 return ResultTagRemoved;
478 }
479 return ResultWrongDeviceState;
480 }
481
482 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
483 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
484 return ResultWrongDeviceState;
485 }
486
487 // TODO: Load amiibo from backup on system
488 LOG_ERROR(Service_NFP, "Not Implemented");
489 return ResultSuccess;
490}
491
492Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const {
376 if (device_state != DeviceState::TagMounted) { 493 if (device_state != DeviceState::TagMounted) {
377 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 494 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
378 if (device_state == DeviceState::TagRemoved) { 495 if (device_state == DeviceState::TagRemoved) {
379 return TagRemoved; 496 return ResultTagRemoved;
380 } 497 }
381 return WrongDeviceState; 498 return ResultWrongDeviceState;
382 } 499 }
383 500
384 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 501 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
385 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 502 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
386 return WrongDeviceState; 503 return ResultWrongDeviceState;
387 } 504 }
388 505
389 const auto& settings = tag_data.settings; 506 const auto& settings = tag_data.settings;
@@ -393,18 +510,18 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const {
393 .last_write_date = settings.write_date.GetWriteDate(), 510 .last_write_date = settings.write_date.GetWriteDate(),
394 .write_counter = tag_data.write_counter, 511 .write_counter = tag_data.write_counter,
395 .version = tag_data.amiibo_version, 512 .version = tag_data.amiibo_version,
396 .application_area_size = sizeof(ApplicationArea), 513 .application_area_size = sizeof(NFP::ApplicationArea),
397 }; 514 };
398 return ResultSuccess; 515 return ResultSuccess;
399} 516}
400 517
401Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { 518Result NfcDevice::GetModelInfo(NFP::ModelInfo& model_info) const {
402 if (device_state != DeviceState::TagMounted) { 519 if (device_state != DeviceState::TagMounted) {
403 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 520 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
404 if (device_state == DeviceState::TagRemoved) { 521 if (device_state == DeviceState::TagRemoved) {
405 return TagRemoved; 522 return ResultTagRemoved;
406 } 523 }
407 return WrongDeviceState; 524 return ResultWrongDeviceState;
408 } 525 }
409 526
410 const auto& model_info_data = encrypted_tag_data.user_memory.model_info; 527 const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
@@ -418,22 +535,22 @@ Result NfpDevice::GetModelInfo(ModelInfo& model_info) const {
418 return ResultSuccess; 535 return ResultSuccess;
419} 536}
420 537
421Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { 538Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const {
422 if (device_state != DeviceState::TagMounted) { 539 if (device_state != DeviceState::TagMounted) {
423 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 540 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
424 if (device_state == DeviceState::TagRemoved) { 541 if (device_state == DeviceState::TagRemoved) {
425 return TagRemoved; 542 return ResultTagRemoved;
426 } 543 }
427 return WrongDeviceState; 544 return ResultWrongDeviceState;
428 } 545 }
429 546
430 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 547 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
431 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 548 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
432 return WrongDeviceState; 549 return ResultWrongDeviceState;
433 } 550 }
434 551
435 if (tag_data.settings.settings.amiibo_initialized == 0) { 552 if (tag_data.settings.settings.amiibo_initialized == 0) {
436 return RegistrationIsNotInitialized; 553 return ResultRegistrationIsNotInitialized;
437 } 554 }
438 555
439 Service::Mii::MiiManager manager; 556 Service::Mii::MiiManager manager;
@@ -450,22 +567,22 @@ Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
450 return ResultSuccess; 567 return ResultSuccess;
451} 568}
452 569
453Result NfpDevice::GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const { 570Result NfcDevice::GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const {
454 if (device_state != DeviceState::TagMounted) { 571 if (device_state != DeviceState::TagMounted) {
455 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 572 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
456 if (device_state == DeviceState::TagRemoved) { 573 if (device_state == DeviceState::TagRemoved) {
457 return TagRemoved; 574 return ResultTagRemoved;
458 } 575 }
459 return WrongDeviceState; 576 return ResultWrongDeviceState;
460 } 577 }
461 578
462 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 579 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
463 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 580 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
464 return WrongDeviceState; 581 return ResultWrongDeviceState;
465 } 582 }
466 583
467 if (tag_data.settings.settings.amiibo_initialized == 0) { 584 if (tag_data.settings.settings.amiibo_initialized == 0) {
468 return RegistrationIsNotInitialized; 585 return ResultRegistrationIsNotInitialized;
469 } 586 }
470 587
471 Service::Mii::MiiManager manager; 588 Service::Mii::MiiManager manager;
@@ -482,18 +599,18 @@ Result NfpDevice::GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) con
482 return ResultSuccess; 599 return ResultSuccess;
483} 600}
484 601
485Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const { 602Result NfcDevice::GetAdminInfo(NFP::AdminInfo& admin_info) const {
486 if (device_state != DeviceState::TagMounted) { 603 if (device_state != DeviceState::TagMounted) {
487 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 604 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
488 if (device_state == DeviceState::TagRemoved) { 605 if (device_state == DeviceState::TagRemoved) {
489 return TagRemoved; 606 return ResultTagRemoved;
490 } 607 }
491 return WrongDeviceState; 608 return ResultWrongDeviceState;
492 } 609 }
493 610
494 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 611 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
495 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 612 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
496 return WrongDeviceState; 613 return ResultWrongDeviceState;
497 } 614 }
498 615
499 u8 flags = static_cast<u8>(tag_data.settings.settings.raw >> 0x4); 616 u8 flags = static_cast<u8>(tag_data.settings.settings.raw >> 0x4);
@@ -503,17 +620,18 @@ Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const {
503 620
504 u64 application_id = 0; 621 u64 application_id = 0;
505 u32 application_area_id = 0; 622 u32 application_area_id = 0;
506 AppAreaVersion app_area_version = AppAreaVersion::NotSet; 623 NFP::AppAreaVersion app_area_version = NFP::AppAreaVersion::NotSet;
507 if (tag_data.settings.settings.appdata_initialized != 0) { 624 if (tag_data.settings.settings.appdata_initialized != 0) {
508 application_id = tag_data.application_id; 625 application_id = tag_data.application_id;
509 app_area_version = 626 app_area_version = static_cast<NFP::AppAreaVersion>(
510 static_cast<AppAreaVersion>(application_id >> application_id_version_offset & 0xf); 627 application_id >> NFP::application_id_version_offset & 0xf);
511 628
512 // Restore application id to original value 629 // Restore application id to original value
513 if (application_id >> 0x38 != 0) { 630 if (application_id >> 0x38 != 0) {
514 const u8 application_byte = tag_data.application_id_byte & 0xf; 631 const u8 application_byte = tag_data.application_id_byte & 0xf;
515 application_id = RemoveVersionByte(application_id) | 632 application_id =
516 (static_cast<u64>(application_byte) << application_id_version_offset); 633 RemoveVersionByte(application_id) |
634 (static_cast<u64>(application_byte) << NFP::application_id_version_offset);
517 } 635 }
518 636
519 application_area_id = tag_data.application_area_id; 637 application_area_id = tag_data.application_area_id;
@@ -532,22 +650,22 @@ Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const {
532 return ResultSuccess; 650 return ResultSuccess;
533} 651}
534 652
535Result NfpDevice::DeleteRegisterInfo() { 653Result NfcDevice::DeleteRegisterInfo() {
536 if (device_state != DeviceState::TagMounted) { 654 if (device_state != DeviceState::TagMounted) {
537 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 655 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
538 if (device_state == DeviceState::TagRemoved) { 656 if (device_state == DeviceState::TagRemoved) {
539 return TagRemoved; 657 return ResultTagRemoved;
540 } 658 }
541 return WrongDeviceState; 659 return ResultWrongDeviceState;
542 } 660 }
543 661
544 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 662 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
545 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 663 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
546 return WrongDeviceState; 664 return ResultWrongDeviceState;
547 } 665 }
548 666
549 if (tag_data.settings.settings.amiibo_initialized == 0) { 667 if (tag_data.settings.settings.amiibo_initialized == 0) {
550 return RegistrationIsNotInitialized; 668 return ResultRegistrationIsNotInitialized;
551 } 669 }
552 670
553 Common::TinyMT rng{}; 671 Common::TinyMT rng{};
@@ -564,18 +682,18 @@ Result NfpDevice::DeleteRegisterInfo() {
564 return Flush(); 682 return Flush();
565} 683}
566 684
567Result NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) { 685Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info) {
568 if (device_state != DeviceState::TagMounted) { 686 if (device_state != DeviceState::TagMounted) {
569 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 687 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
570 if (device_state == DeviceState::TagRemoved) { 688 if (device_state == DeviceState::TagRemoved) {
571 return TagRemoved; 689 return ResultTagRemoved;
572 } 690 }
573 return WrongDeviceState; 691 return ResultWrongDeviceState;
574 } 692 }
575 693
576 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 694 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
577 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 695 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
578 return WrongDeviceState; 696 return ResultWrongDeviceState;
579 } 697 }
580 698
581 Service::Mii::MiiManager manager; 699 Service::Mii::MiiManager manager;
@@ -587,7 +705,7 @@ Result NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) {
587 settings.write_date.raw_date = 0; 705 settings.write_date.raw_date = 0;
588 } 706 }
589 707
590 SetAmiiboName(settings, amiibo_name); 708 SetAmiiboName(settings, register_info.amiibo_name);
591 tag_data.owner_mii = manager.BuildFromStoreData(mii); 709 tag_data.owner_mii = manager.BuildFromStoreData(mii);
592 tag_data.mii_extension = manager.SetFromStoreData(mii); 710 tag_data.mii_extension = manager.SetFromStoreData(mii);
593 tag_data.unknown = 0; 711 tag_data.unknown = 0;
@@ -601,18 +719,18 @@ Result NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) {
601 return Flush(); 719 return Flush();
602} 720}
603 721
604Result NfpDevice::RestoreAmiibo() { 722Result NfcDevice::RestoreAmiibo() {
605 if (device_state != DeviceState::TagMounted) { 723 if (device_state != DeviceState::TagMounted) {
606 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 724 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
607 if (device_state == DeviceState::TagRemoved) { 725 if (device_state == DeviceState::TagRemoved) {
608 return TagRemoved; 726 return ResultTagRemoved;
609 } 727 }
610 return WrongDeviceState; 728 return ResultWrongDeviceState;
611 } 729 }
612 730
613 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 731 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
614 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 732 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
615 return WrongDeviceState; 733 return ResultWrongDeviceState;
616 } 734 }
617 735
618 // TODO: Load amiibo from backup on system 736 // TODO: Load amiibo from backup on system
@@ -620,7 +738,7 @@ Result NfpDevice::RestoreAmiibo() {
620 return ResultSuccess; 738 return ResultSuccess;
621} 739}
622 740
623Result NfpDevice::Format() { 741Result NfcDevice::Format() {
624 auto result1 = DeleteApplicationArea(); 742 auto result1 = DeleteApplicationArea();
625 auto result2 = DeleteRegisterInfo(); 743 auto result2 = DeleteRegisterInfo();
626 744
@@ -635,28 +753,28 @@ Result NfpDevice::Format() {
635 return Flush(); 753 return Flush();
636} 754}
637 755
638Result NfpDevice::OpenApplicationArea(u32 access_id) { 756Result NfcDevice::OpenApplicationArea(u32 access_id) {
639 if (device_state != DeviceState::TagMounted) { 757 if (device_state != DeviceState::TagMounted) {
640 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 758 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
641 if (device_state == DeviceState::TagRemoved) { 759 if (device_state == DeviceState::TagRemoved) {
642 return TagRemoved; 760 return ResultTagRemoved;
643 } 761 }
644 return WrongDeviceState; 762 return ResultWrongDeviceState;
645 } 763 }
646 764
647 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 765 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
648 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 766 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
649 return WrongDeviceState; 767 return ResultWrongDeviceState;
650 } 768 }
651 769
652 if (tag_data.settings.settings.appdata_initialized.Value() == 0) { 770 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
653 LOG_WARNING(Service_NFP, "Application area is not initialized"); 771 LOG_WARNING(Service_NFP, "Application area is not initialized");
654 return ApplicationAreaIsNotInitialized; 772 return ResultApplicationAreaIsNotInitialized;
655 } 773 }
656 774
657 if (tag_data.application_area_id != access_id) { 775 if (tag_data.application_area_id != access_id) {
658 LOG_WARNING(Service_NFP, "Wrong application area id"); 776 LOG_WARNING(Service_NFP, "Wrong application area id");
659 return WrongApplicationAreaId; 777 return ResultWrongApplicationAreaId;
660 } 778 }
661 779
662 is_app_area_open = true; 780 is_app_area_open = true;
@@ -664,25 +782,25 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) {
664 return ResultSuccess; 782 return ResultSuccess;
665} 783}
666 784
667Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const { 785Result NfcDevice::GetApplicationAreaId(u32& application_area_id) const {
668 application_area_id = {}; 786 application_area_id = {};
669 787
670 if (device_state != DeviceState::TagMounted) { 788 if (device_state != DeviceState::TagMounted) {
671 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 789 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
672 if (device_state == DeviceState::TagRemoved) { 790 if (device_state == DeviceState::TagRemoved) {
673 return TagRemoved; 791 return ResultTagRemoved;
674 } 792 }
675 return WrongDeviceState; 793 return ResultWrongDeviceState;
676 } 794 }
677 795
678 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 796 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
679 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 797 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
680 return WrongDeviceState; 798 return ResultWrongDeviceState;
681 } 799 }
682 800
683 if (tag_data.settings.settings.appdata_initialized.Value() == 0) { 801 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
684 LOG_WARNING(Service_NFP, "Application area is not initialized"); 802 LOG_WARNING(Service_NFP, "Application area is not initialized");
685 return ApplicationAreaIsNotInitialized; 803 return ResultApplicationAreaIsNotInitialized;
686 } 804 }
687 805
688 application_area_id = tag_data.application_area_id; 806 application_area_id = tag_data.application_area_id;
@@ -690,64 +808,61 @@ Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const {
690 return ResultSuccess; 808 return ResultSuccess;
691} 809}
692 810
693Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { 811Result NfcDevice::GetApplicationArea(std::span<u8> data) const {
694 if (device_state != DeviceState::TagMounted) { 812 if (device_state != DeviceState::TagMounted) {
695 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 813 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
696 if (device_state == DeviceState::TagRemoved) { 814 if (device_state == DeviceState::TagRemoved) {
697 return TagRemoved; 815 return ResultTagRemoved;
698 } 816 }
699 return WrongDeviceState; 817 return ResultWrongDeviceState;
700 } 818 }
701 819
702 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 820 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
703 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 821 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
704 return WrongDeviceState; 822 return ResultWrongDeviceState;
705 } 823 }
706 824
707 if (!is_app_area_open) { 825 if (!is_app_area_open) {
708 LOG_ERROR(Service_NFP, "Application area is not open"); 826 LOG_ERROR(Service_NFP, "Application area is not open");
709 return WrongDeviceState; 827 return ResultWrongDeviceState;
710 } 828 }
711 829
712 if (tag_data.settings.settings.appdata_initialized.Value() == 0) { 830 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
713 LOG_ERROR(Service_NFP, "Application area is not initialized"); 831 LOG_ERROR(Service_NFP, "Application area is not initialized");
714 return ApplicationAreaIsNotInitialized; 832 return ResultApplicationAreaIsNotInitialized;
715 }
716
717 if (data.size() > sizeof(ApplicationArea)) {
718 data.resize(sizeof(ApplicationArea));
719 } 833 }
720 834
721 memcpy(data.data(), tag_data.application_area.data(), data.size()); 835 memcpy(data.data(), tag_data.application_area.data(),
836 std::min(data.size(), sizeof(NFP::ApplicationArea)));
722 837
723 return ResultSuccess; 838 return ResultSuccess;
724} 839}
725 840
726Result NfpDevice::SetApplicationArea(std::span<const u8> data) { 841Result NfcDevice::SetApplicationArea(std::span<const u8> data) {
727 if (device_state != DeviceState::TagMounted) { 842 if (device_state != DeviceState::TagMounted) {
728 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 843 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
729 if (device_state == DeviceState::TagRemoved) { 844 if (device_state == DeviceState::TagRemoved) {
730 return TagRemoved; 845 return ResultTagRemoved;
731 } 846 }
732 return WrongDeviceState; 847 return ResultWrongDeviceState;
733 } 848 }
734 849
735 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 850 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
736 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 851 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
737 return WrongDeviceState; 852 return ResultWrongDeviceState;
738 } 853 }
739 854
740 if (!is_app_area_open) { 855 if (!is_app_area_open) {
741 LOG_ERROR(Service_NFP, "Application area is not open"); 856 LOG_ERROR(Service_NFP, "Application area is not open");
742 return WrongDeviceState; 857 return ResultWrongDeviceState;
743 } 858 }
744 859
745 if (tag_data.settings.settings.appdata_initialized.Value() == 0) { 860 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
746 LOG_ERROR(Service_NFP, "Application area is not initialized"); 861 LOG_ERROR(Service_NFP, "Application area is not initialized");
747 return ApplicationAreaIsNotInitialized; 862 return ResultApplicationAreaIsNotInitialized;
748 } 863 }
749 864
750 if (data.size() > sizeof(ApplicationArea)) { 865 if (data.size() > sizeof(NFP::ApplicationArea)) {
751 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); 866 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
752 return ResultUnknown; 867 return ResultUnknown;
753 } 868 }
@@ -756,9 +871,9 @@ Result NfpDevice::SetApplicationArea(std::span<const u8> data) {
756 std::memcpy(tag_data.application_area.data(), data.data(), data.size()); 871 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
757 // Fill remaining data with random numbers 872 // Fill remaining data with random numbers
758 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), 873 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
759 sizeof(ApplicationArea) - data.size()); 874 sizeof(NFP::ApplicationArea) - data.size());
760 875
761 if (tag_data.application_write_counter != counter_limit) { 876 if (tag_data.application_write_counter != NFP::counter_limit) {
762 tag_data.application_write_counter++; 877 tag_data.application_write_counter++;
763 } 878 }
764 879
@@ -767,64 +882,64 @@ Result NfpDevice::SetApplicationArea(std::span<const u8> data) {
767 return ResultSuccess; 882 return ResultSuccess;
768} 883}
769 884
770Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) { 885Result NfcDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) {
771 if (device_state != DeviceState::TagMounted) { 886 if (device_state != DeviceState::TagMounted) {
772 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 887 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
773 if (device_state == DeviceState::TagRemoved) { 888 if (device_state == DeviceState::TagRemoved) {
774 return TagRemoved; 889 return ResultTagRemoved;
775 } 890 }
776 return WrongDeviceState; 891 return ResultWrongDeviceState;
777 } 892 }
778 893
779 if (tag_data.settings.settings.appdata_initialized.Value() != 0) { 894 if (tag_data.settings.settings.appdata_initialized.Value() != 0) {
780 LOG_ERROR(Service_NFP, "Application area already exist"); 895 LOG_ERROR(Service_NFP, "Application area already exist");
781 return ApplicationAreaExist; 896 return ResultApplicationAreaExist;
782 } 897 }
783 898
784 return RecreateApplicationArea(access_id, data); 899 return RecreateApplicationArea(access_id, data);
785} 900}
786 901
787Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) { 902Result NfcDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) {
788 if (device_state != DeviceState::TagMounted) { 903 if (device_state != DeviceState::TagMounted) {
789 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 904 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
790 if (device_state == DeviceState::TagRemoved) { 905 if (device_state == DeviceState::TagRemoved) {
791 return TagRemoved; 906 return ResultTagRemoved;
792 } 907 }
793 return WrongDeviceState; 908 return ResultWrongDeviceState;
794 } 909 }
795 910
796 if (is_app_area_open) { 911 if (is_app_area_open) {
797 LOG_ERROR(Service_NFP, "Application area is open"); 912 LOG_ERROR(Service_NFP, "Application area is open");
798 return WrongDeviceState; 913 return ResultWrongDeviceState;
799 } 914 }
800 915
801 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 916 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
802 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 917 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
803 return WrongDeviceState; 918 return ResultWrongDeviceState;
804 } 919 }
805 920
806 if (data.size() > sizeof(ApplicationArea)) { 921 if (data.size() > sizeof(NFP::ApplicationArea)) {
807 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); 922 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
808 return WrongApplicationAreaSize; 923 return ResultWrongApplicationAreaSize;
809 } 924 }
810 925
811 Common::TinyMT rng{}; 926 Common::TinyMT rng{};
812 std::memcpy(tag_data.application_area.data(), data.data(), data.size()); 927 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
813 // Fill remaining data with random numbers 928 // Fill remaining data with random numbers
814 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), 929 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
815 sizeof(ApplicationArea) - data.size()); 930 sizeof(NFP::ApplicationArea) - data.size());
816 931
817 if (tag_data.application_write_counter != counter_limit) { 932 if (tag_data.application_write_counter != NFP::counter_limit) {
818 tag_data.application_write_counter++; 933 tag_data.application_write_counter++;
819 } 934 }
820 935
821 const u64 application_id = system.GetApplicationProcessProgramID(); 936 const u64 application_id = system.GetApplicationProcessProgramID();
822 937
823 tag_data.application_id_byte = 938 tag_data.application_id_byte =
824 static_cast<u8>(application_id >> application_id_version_offset & 0xf); 939 static_cast<u8>(application_id >> NFP::application_id_version_offset & 0xf);
825 tag_data.application_id = 940 tag_data.application_id =
826 RemoveVersionByte(application_id) | 941 RemoveVersionByte(application_id) | (static_cast<u64>(NFP::AppAreaVersion::NintendoSwitch)
827 (static_cast<u64>(AppAreaVersion::NintendoSwitch) << application_id_version_offset); 942 << NFP::application_id_version_offset);
828 tag_data.settings.settings.appdata_initialized.Assign(1); 943 tag_data.settings.settings.appdata_initialized.Assign(1);
829 tag_data.application_area_id = access_id; 944 tag_data.application_area_id = access_id;
830 tag_data.unknown = {}; 945 tag_data.unknown = {};
@@ -835,30 +950,30 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> dat
835 return Flush(); 950 return Flush();
836} 951}
837 952
838Result NfpDevice::DeleteApplicationArea() { 953Result NfcDevice::DeleteApplicationArea() {
839 if (device_state != DeviceState::TagMounted) { 954 if (device_state != DeviceState::TagMounted) {
840 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 955 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
841 if (device_state == DeviceState::TagRemoved) { 956 if (device_state == DeviceState::TagRemoved) {
842 return TagRemoved; 957 return ResultTagRemoved;
843 } 958 }
844 return WrongDeviceState; 959 return ResultWrongDeviceState;
845 } 960 }
846 961
847 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 962 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
848 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); 963 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
849 return WrongDeviceState; 964 return ResultWrongDeviceState;
850 } 965 }
851 966
852 if (tag_data.settings.settings.appdata_initialized == 0) { 967 if (tag_data.settings.settings.appdata_initialized == 0) {
853 return ApplicationAreaIsNotInitialized; 968 return ResultApplicationAreaIsNotInitialized;
854 } 969 }
855 970
856 if (tag_data.application_write_counter != counter_limit) { 971 if (tag_data.application_write_counter != NFP::counter_limit) {
857 tag_data.application_write_counter++; 972 tag_data.application_write_counter++;
858 } 973 }
859 974
860 Common::TinyMT rng{}; 975 Common::TinyMT rng{};
861 rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); 976 rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(NFP::ApplicationArea));
862 rng.GenerateRandomBytes(&tag_data.application_id, sizeof(u64)); 977 rng.GenerateRandomBytes(&tag_data.application_id, sizeof(u64));
863 rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); 978 rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32));
864 rng.GenerateRandomBytes(&tag_data.application_id_byte, sizeof(u8)); 979 rng.GenerateRandomBytes(&tag_data.application_id_byte, sizeof(u8));
@@ -872,18 +987,18 @@ Result NfpDevice::DeleteApplicationArea() {
872 return Flush(); 987 return Flush();
873} 988}
874 989
875Result NfpDevice::ExistApplicationArea(bool& has_application_area) { 990Result NfcDevice::ExistsApplicationArea(bool& has_application_area) const {
876 if (device_state != DeviceState::TagMounted) { 991 if (device_state != DeviceState::TagMounted) {
877 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 992 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
878 if (device_state == DeviceState::TagRemoved) { 993 if (device_state == DeviceState::TagRemoved) {
879 return TagRemoved; 994 return ResultTagRemoved;
880 } 995 }
881 return WrongDeviceState; 996 return ResultWrongDeviceState;
882 } 997 }
883 998
884 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 999 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
885 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 1000 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
886 return WrongDeviceState; 1001 return ResultWrongDeviceState;
887 } 1002 }
888 1003
889 has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0; 1004 has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0;
@@ -891,21 +1006,21 @@ Result NfpDevice::ExistApplicationArea(bool& has_application_area) {
891 return ResultSuccess; 1006 return ResultSuccess;
892} 1007}
893 1008
894Result NfpDevice::GetAll(NfpData& data) const { 1009Result NfcDevice::GetAll(NFP::NfpData& data) const {
895 if (device_state != DeviceState::TagMounted) { 1010 if (device_state != DeviceState::TagMounted) {
896 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 1011 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
897 if (device_state == DeviceState::TagRemoved) { 1012 if (device_state == DeviceState::TagRemoved) {
898 return TagRemoved; 1013 return ResultTagRemoved;
899 } 1014 }
900 return WrongDeviceState; 1015 return ResultWrongDeviceState;
901 } 1016 }
902 1017
903 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 1018 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
904 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 1019 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
905 return WrongDeviceState; 1020 return ResultWrongDeviceState;
906 } 1021 }
907 1022
908 CommonInfo common_info{}; 1023 NFP::CommonInfo common_info{};
909 Service::Mii::MiiManager manager; 1024 Service::Mii::MiiManager manager;
910 const u64 application_id = tag_data.application_id; 1025 const u64 application_id = tag_data.application_id;
911 1026
@@ -930,8 +1045,8 @@ Result NfpDevice::GetAll(NfpData& data) const {
930 .settings_crc_counter = tag_data.settings.crc_counter, 1045 .settings_crc_counter = tag_data.settings.crc_counter,
931 .font_region = tag_data.settings.settings.font_region, 1046 .font_region = tag_data.settings.settings.font_region,
932 .tag_type = PackedTagType::Type2, 1047 .tag_type = PackedTagType::Type2,
933 .console_type = 1048 .console_type = static_cast<NFP::AppAreaVersion>(
934 static_cast<AppAreaVersion>(application_id >> application_id_version_offset & 0xf), 1049 application_id >> NFP::application_id_version_offset & 0xf),
935 .application_id_byte = tag_data.application_id_byte, 1050 .application_id_byte = tag_data.application_id_byte,
936 .application_area = tag_data.application_area, 1051 .application_area = tag_data.application_area,
937 }; 1052 };
@@ -939,18 +1054,18 @@ Result NfpDevice::GetAll(NfpData& data) const {
939 return ResultSuccess; 1054 return ResultSuccess;
940} 1055}
941 1056
942Result NfpDevice::SetAll(const NfpData& data) { 1057Result NfcDevice::SetAll(const NFP::NfpData& data) {
943 if (device_state != DeviceState::TagMounted) { 1058 if (device_state != DeviceState::TagMounted) {
944 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 1059 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
945 if (device_state == DeviceState::TagRemoved) { 1060 if (device_state == DeviceState::TagRemoved) {
946 return TagRemoved; 1061 return ResultTagRemoved;
947 } 1062 }
948 return WrongDeviceState; 1063 return ResultWrongDeviceState;
949 } 1064 }
950 1065
951 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 1066 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
952 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 1067 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
953 return WrongDeviceState; 1068 return ResultWrongDeviceState;
954 } 1069 }
955 1070
956 tag_data.constant_value = data.magic; 1071 tag_data.constant_value = data.magic;
@@ -977,18 +1092,18 @@ Result NfpDevice::SetAll(const NfpData& data) {
977 return ResultSuccess; 1092 return ResultSuccess;
978} 1093}
979 1094
980Result NfpDevice::BreakTag(BreakType break_type) { 1095Result NfcDevice::BreakTag(NFP::BreakType break_type) {
981 if (device_state != DeviceState::TagMounted) { 1096 if (device_state != DeviceState::TagMounted) {
982 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 1097 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
983 if (device_state == DeviceState::TagRemoved) { 1098 if (device_state == DeviceState::TagRemoved) {
984 return TagRemoved; 1099 return ResultTagRemoved;
985 } 1100 }
986 return WrongDeviceState; 1101 return ResultWrongDeviceState;
987 } 1102 }
988 1103
989 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 1104 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
990 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 1105 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
991 return WrongDeviceState; 1106 return ResultWrongDeviceState;
992 } 1107 }
993 1108
994 // TODO: Complete this implementation 1109 // TODO: Complete this implementation
@@ -996,28 +1111,28 @@ Result NfpDevice::BreakTag(BreakType break_type) {
996 return FlushWithBreak(break_type); 1111 return FlushWithBreak(break_type);
997} 1112}
998 1113
999Result NfpDevice::ReadBackupData() { 1114Result NfcDevice::ReadBackupData(std::span<u8> data) const {
1000 // Not implemented 1115 // Not implemented
1001 return ResultSuccess; 1116 return ResultSuccess;
1002} 1117}
1003 1118
1004Result NfpDevice::WriteBackupData() { 1119Result NfcDevice::WriteBackupData(std::span<const u8> data) {
1005 // Not implemented 1120 // Not implemented
1006 return ResultSuccess; 1121 return ResultSuccess;
1007} 1122}
1008 1123
1009Result NfpDevice::WriteNtf() { 1124Result NfcDevice::WriteNtf(std::span<const u8> data) {
1010 if (device_state != DeviceState::TagMounted) { 1125 if (device_state != DeviceState::TagMounted) {
1011 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 1126 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
1012 if (device_state == DeviceState::TagRemoved) { 1127 if (device_state == DeviceState::TagRemoved) {
1013 return TagRemoved; 1128 return ResultTagRemoved;
1014 } 1129 }
1015 return WrongDeviceState; 1130 return ResultWrongDeviceState;
1016 } 1131 }
1017 1132
1018 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { 1133 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
1019 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); 1134 LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
1020 return WrongDeviceState; 1135 return ResultWrongDeviceState;
1021 } 1136 }
1022 1137
1023 // Not implemented 1138 // Not implemented
@@ -1025,29 +1140,12 @@ Result NfpDevice::WriteNtf() {
1025 return ResultSuccess; 1140 return ResultSuccess;
1026} 1141}
1027 1142
1028u64 NfpDevice::GetHandle() const { 1143NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) const {
1029 // Generate a handle based of the npad id 1144 std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{};
1030 return static_cast<u64>(npad_id); 1145 NFP::AmiiboName amiibo_name{};
1031}
1032
1033u32 NfpDevice::GetApplicationAreaSize() const {
1034 return sizeof(ApplicationArea);
1035}
1036
1037DeviceState NfpDevice::GetCurrentState() const {
1038 return device_state;
1039}
1040
1041Core::HID::NpadIdType NfpDevice::GetNpadId() const {
1042 return npad_id;
1043}
1044
1045AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const {
1046 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
1047 AmiiboName amiibo_name{};
1048 1146
1049 // Convert from big endian to little endian 1147 // Convert from big endian to little endian
1050 for (std::size_t i = 0; i < amiibo_name_length; i++) { 1148 for (std::size_t i = 0; i < NFP::amiibo_name_length; i++) {
1051 settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]); 1149 settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
1052 } 1150 }
1053 1151
@@ -1058,8 +1156,8 @@ AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const {
1058 return amiibo_name; 1156 return amiibo_name;
1059} 1157}
1060 1158
1061void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { 1159void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) {
1062 std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; 1160 std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{};
1063 1161
1064 // Convert from utf8 to utf16 1162 // Convert from utf8 to utf16
1065 const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); 1163 const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data());
@@ -1067,16 +1165,16 @@ void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo
1067 amiibo_name_utf16.size() * sizeof(char16_t)); 1165 amiibo_name_utf16.size() * sizeof(char16_t));
1068 1166
1069 // Convert from little endian to big endian 1167 // Convert from little endian to big endian
1070 for (std::size_t i = 0; i < amiibo_name_length; i++) { 1168 for (std::size_t i = 0; i < NFP::amiibo_name_length; i++) {
1071 settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]); 1169 settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]);
1072 } 1170 }
1073} 1171}
1074 1172
1075AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { 1173NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const {
1076 const auto& time_zone_manager = 1174 const auto& time_zone_manager =
1077 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); 1175 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
1078 Time::TimeZone::CalendarInfo calendar_info{}; 1176 Time::TimeZone::CalendarInfo calendar_info{};
1079 AmiiboDate amiibo_date{}; 1177 NFP::AmiiboDate amiibo_date{};
1080 1178
1081 amiibo_date.SetYear(2000); 1179 amiibo_date.SetYear(2000);
1082 amiibo_date.SetMonth(1); 1180 amiibo_date.SetMonth(1);
@@ -1091,14 +1189,14 @@ AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const {
1091 return amiibo_date; 1189 return amiibo_date;
1092} 1190}
1093 1191
1094u64 NfpDevice::RemoveVersionByte(u64 application_id) const { 1192u64 NfcDevice::RemoveVersionByte(u64 application_id) const {
1095 return application_id & ~(0xfULL << application_id_version_offset); 1193 return application_id & ~(0xfULL << NFP::application_id_version_offset);
1096} 1194}
1097 1195
1098void NfpDevice::UpdateSettingsCrc() { 1196void NfcDevice::UpdateSettingsCrc() {
1099 auto& settings = tag_data.settings; 1197 auto& settings = tag_data.settings;
1100 1198
1101 if (settings.crc_counter != counter_limit) { 1199 if (settings.crc_counter != NFP::counter_limit) {
1102 settings.crc_counter++; 1200 settings.crc_counter++;
1103 } 1201 }
1104 1202
@@ -1109,7 +1207,7 @@ void NfpDevice::UpdateSettingsCrc() {
1109 settings.crc = crc.checksum(); 1207 settings.crc = crc.checksum();
1110} 1208}
1111 1209
1112void NfpDevice::UpdateRegisterInfoCrc() { 1210void NfcDevice::UpdateRegisterInfoCrc() {
1113#pragma pack(push, 1) 1211#pragma pack(push, 1)
1114 struct CrcData { 1212 struct CrcData {
1115 Mii::Ver3StoreData mii; 1213 Mii::Ver3StoreData mii;
@@ -1134,4 +1232,18 @@ void NfpDevice::UpdateRegisterInfoCrc() {
1134 tag_data.register_info_crc = crc.checksum(); 1232 tag_data.register_info_crc = crc.checksum();
1135} 1233}
1136 1234
1137} // namespace Service::NFP 1235u64 NfcDevice::GetHandle() const {
1236 // Generate a handle based of the npad id
1237 return static_cast<u64>(npad_id);
1238}
1239
1240DeviceState NfcDevice::GetCurrentState() const {
1241 return device_state;
1242}
1243
1244Result NfcDevice::GetNpadId(Core::HID::NpadIdType& out_npad_id) const {
1245 out_npad_id = npad_id;
1246 return ResultSuccess;
1247}
1248
1249} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
new file mode 100644
index 000000000..654eda98e
--- /dev/null
+++ b/src/core/hle/service/nfc/common/device.h
@@ -0,0 +1,138 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "common/common_types.h"
9#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/nfc/mifare_types.h"
11#include "core/hle/service/nfc/nfc_types.h"
12#include "core/hle/service/nfp/nfp_types.h"
13#include "core/hle/service/service.h"
14#include "core/hle/service/time/clock_types.h"
15
16namespace Kernel {
17class KEvent;
18class KReadableEvent;
19} // namespace Kernel
20
21namespace Core {
22class System;
23} // namespace Core
24
25namespace Core::HID {
26class EmulatedController;
27enum class ControllerTriggerType;
28enum class NpadIdType : u32;
29} // namespace Core::HID
30
31namespace Service::NFC {
32class NfcDevice {
33public:
34 NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
35 KernelHelpers::ServiceContext& service_context_,
36 Kernel::KEvent* availability_change_event_);
37 ~NfcDevice();
38
39 void Initialize();
40 void Finalize();
41
42 Result StartDetection(NfcProtocol allowed_protocol);
43 Result StopDetection();
44
45 Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const;
46
47 Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,
48 std::span<MifareReadBlockData> read_block_data) const;
49 Result ReadMifare(const MifareReadBlockParameter& parameter,
50 MifareReadBlockData& read_block_data) const;
51
52 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
53 Result WriteMifare(const MifareWriteBlockParameter& parameter);
54
55 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
56 std::span<const u8> command_data, std::span<u8> out_data);
57
58 Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target);
59 Result Unmount();
60
61 Result Flush();
62 Result FlushDebug();
63 Result FlushWithBreak(NFP::BreakType break_type);
64 Result Restore();
65
66 Result GetCommonInfo(NFP::CommonInfo& common_info) const;
67 Result GetModelInfo(NFP::ModelInfo& model_info) const;
68 Result GetRegisterInfo(NFP::RegisterInfo& register_info) const;
69 Result GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const;
70 Result GetAdminInfo(NFP::AdminInfo& admin_info) const;
71
72 Result DeleteRegisterInfo();
73 Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info);
74 Result RestoreAmiibo();
75 Result Format();
76
77 Result OpenApplicationArea(u32 access_id);
78 Result GetApplicationAreaId(u32& application_area_id) const;
79 Result GetApplicationArea(std::span<u8> data) const;
80 Result SetApplicationArea(std::span<const u8> data);
81 Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
82 Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
83 Result DeleteApplicationArea();
84 Result ExistsApplicationArea(bool& has_application_area) const;
85
86 Result GetAll(NFP::NfpData& data) const;
87 Result SetAll(const NFP::NfpData& data);
88 Result BreakTag(NFP::BreakType break_type);
89 Result ReadBackupData(std::span<u8> data) const;
90 Result WriteBackupData(std::span<const u8> data);
91 Result WriteNtf(std::span<const u8> data);
92
93 u64 GetHandle() const;
94 DeviceState GetCurrentState() const;
95 Result GetNpadId(Core::HID::NpadIdType& out_npad_id) const;
96
97 Kernel::KReadableEvent& GetActivateEvent() const;
98 Kernel::KReadableEvent& GetDeactivateEvent() const;
99
100private:
101 void NpadUpdate(Core::HID::ControllerTriggerType type);
102 bool LoadNfcTag(std::span<const u8> data);
103 void CloseNfcTag();
104
105 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
106 void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
107 NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
108 u64 RemoveVersionByte(u64 application_id) const;
109 void UpdateSettingsCrc();
110 void UpdateRegisterInfoCrc();
111
112 bool is_controller_set{};
113 int callback_key;
114 const Core::HID::NpadIdType npad_id;
115 Core::System& system;
116 Core::HID::EmulatedController* npad_device = nullptr;
117 KernelHelpers::ServiceContext& service_context;
118 Kernel::KEvent* activate_event = nullptr;
119 Kernel::KEvent* deactivate_event = nullptr;
120 Kernel::KEvent* availability_change_event = nullptr;
121
122 bool is_initalized{};
123 NfcProtocol allowed_protocols{};
124 DeviceState device_state{DeviceState::Unavailable};
125
126 // NFP data
127 bool is_data_moddified{};
128 bool is_app_area_open{};
129 bool is_plain_amiibo{};
130 s64 current_posix_time{};
131 NFP::MountTarget mount_target{NFP::MountTarget::None};
132
133 NFP::NTAG215File tag_data{};
134 std::vector<u8> mifare_data{};
135 NFP::EncryptedNTAG215File encrypted_tag_data{};
136};
137
138} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
new file mode 100644
index 000000000..d5deaaf27
--- /dev/null
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -0,0 +1,695 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hid/hid_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nfc/common/device.h"
10#include "core/hle/service/nfc/common/device_manager.h"
11#include "core/hle/service/nfc/nfc_result.h"
12#include "core/hle/service/time/clock_types.h"
13
14namespace Service::NFC {
15
16DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
17 : system{system_}, service_context{service_context_} {
18
19 availability_change_event =
20 service_context.CreateEvent("Nfc:DeviceManager:AvailabilityChangeEvent");
21
22 for (u32 device_index = 0; device_index < devices.size(); device_index++) {
23 devices[device_index] =
24 std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
25 service_context, availability_change_event);
26 }
27
28 is_initialized = false;
29}
30
31DeviceManager ::~DeviceManager() {
32 service_context.CloseEvent(availability_change_event);
33}
34
35Result DeviceManager::Initialize() {
36 for (auto& device : devices) {
37 device->Initialize();
38 }
39 is_initialized = true;
40 return ResultSuccess;
41}
42
43Result DeviceManager::Finalize() {
44 for (auto& device : devices) {
45 device->Finalize();
46 }
47 is_initialized = false;
48 return ResultSuccess;
49}
50
51Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices,
52 std::size_t max_allowed_devices) const {
53 for (auto& device : devices) {
54 if (nfp_devices.size() >= max_allowed_devices) {
55 continue;
56 }
57 if (device->GetCurrentState() != DeviceState::Unavailable) {
58 nfp_devices.push_back(device->GetHandle());
59 }
60 }
61
62 if (nfp_devices.empty()) {
63 return ResultDeviceNotFound;
64 }
65
66 return ResultSuccess;
67}
68
69DeviceState DeviceManager::GetDeviceState(u64 device_handle) const {
70 std::scoped_lock lock{mutex};
71
72 std::shared_ptr<NfcDevice> device = nullptr;
73 const auto result = GetDeviceFromHandle(device_handle, device, false);
74
75 if (result.IsSuccess()) {
76 return device->GetCurrentState();
77 }
78
79 return DeviceState::Unavailable;
80}
81
82Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const {
83 std::scoped_lock lock{mutex};
84
85 std::shared_ptr<NfcDevice> device = nullptr;
86 auto result = GetDeviceHandle(device_handle, device);
87
88 if (result.IsSuccess()) {
89 result = device->GetNpadId(npad_id);
90 result = VerifyDeviceResult(device, result);
91 }
92
93 return result;
94}
95
96Kernel::KReadableEvent& DeviceManager::AttachAvailabilityChangeEvent() const {
97 return availability_change_event->GetReadableEvent();
98}
99
100Result DeviceManager::StartDetection(u64 device_handle, NfcProtocol tag_protocol) {
101 std::scoped_lock lock{mutex};
102
103 std::shared_ptr<NfcDevice> device = nullptr;
104 auto result = GetDeviceHandle(device_handle, device);
105
106 if (result.IsSuccess()) {
107 result = device->StartDetection(tag_protocol);
108 result = VerifyDeviceResult(device, result);
109 }
110
111 return result;
112}
113
114Result DeviceManager::StopDetection(u64 device_handle) {
115 std::scoped_lock lock{mutex};
116
117 std::shared_ptr<NfcDevice> device = nullptr;
118 auto result = GetDeviceHandle(device_handle, device);
119
120 if (result.IsSuccess()) {
121 result = device->StopDetection();
122 result = VerifyDeviceResult(device, result);
123 }
124
125 return result;
126}
127
128Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const {
129 std::scoped_lock lock{mutex};
130
131 std::shared_ptr<NfcDevice> device = nullptr;
132 auto result = GetDeviceHandle(device_handle, device);
133
134 if (result.IsSuccess()) {
135 result = device->GetTagInfo(tag_info, is_mifare);
136 result = VerifyDeviceResult(device, result);
137 }
138
139 return result;
140}
141
142Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const {
143 std::scoped_lock lock{mutex};
144
145 std::shared_ptr<NfcDevice> device = nullptr;
146 GetDeviceFromHandle(device_handle, device, false);
147
148 // TODO: Return proper error code on failure
149 return device->GetActivateEvent();
150}
151
152Kernel::KReadableEvent& DeviceManager::AttachDeactivateEvent(u64 device_handle) const {
153 std::scoped_lock lock{mutex};
154
155 std::shared_ptr<NfcDevice> device = nullptr;
156 GetDeviceFromHandle(device_handle, device, false);
157
158 // TODO: Return proper error code on failure
159 return device->GetDeactivateEvent();
160}
161
162Result DeviceManager::ReadMifare(u64 device_handle,
163 std::span<const MifareReadBlockParameter> read_parameters,
164 std::span<MifareReadBlockData> read_data) {
165 std::scoped_lock lock{mutex};
166
167 std::shared_ptr<NfcDevice> device = nullptr;
168 auto result = GetDeviceHandle(device_handle, device);
169
170 if (result.IsSuccess()) {
171 result = device->ReadMifare(read_parameters, read_data);
172 result = VerifyDeviceResult(device, result);
173 }
174
175 return result;
176}
177
178Result DeviceManager::WriteMifare(u64 device_handle,
179 std::span<const MifareWriteBlockParameter> write_parameters) {
180 std::scoped_lock lock{mutex};
181
182 std::shared_ptr<NfcDevice> device = nullptr;
183 auto result = GetDeviceHandle(device_handle, device);
184
185 if (result.IsSuccess()) {
186 result = device->WriteMifare(write_parameters);
187 result = VerifyDeviceResult(device, result);
188 }
189
190 return result;
191}
192
193Result DeviceManager::SendCommandByPassThrough(u64 device_handle,
194 const Time::Clock::TimeSpanType& timeout,
195 std::span<const u8> command_data,
196 std::span<u8> out_data) {
197 std::scoped_lock lock{mutex};
198
199 std::shared_ptr<NfcDevice> device = nullptr;
200 auto result = GetDeviceHandle(device_handle, device);
201
202 if (result.IsSuccess()) {
203 result = device->SendCommandByPassThrough(timeout, command_data, out_data);
204 result = VerifyDeviceResult(device, result);
205 }
206
207 return result;
208}
209
210Result DeviceManager::Mount(u64 device_handle, NFP::ModelType model_type,
211 NFP::MountTarget mount_target) {
212 std::scoped_lock lock{mutex};
213
214 std::shared_ptr<NfcDevice> device = nullptr;
215 auto result = GetDeviceHandle(device_handle, device);
216
217 if (result.IsSuccess()) {
218 result = device->Mount(model_type, mount_target);
219 result = VerifyDeviceResult(device, result);
220 }
221
222 return result;
223}
224
225Result DeviceManager::Unmount(u64 device_handle) {
226 std::scoped_lock lock{mutex};
227
228 std::shared_ptr<NfcDevice> device = nullptr;
229 auto result = GetDeviceHandle(device_handle, device);
230
231 if (result.IsSuccess()) {
232 result = device->Unmount();
233 result = VerifyDeviceResult(device, result);
234 }
235
236 return result;
237}
238
239Result DeviceManager::OpenApplicationArea(u64 device_handle, u32 access_id) {
240 std::scoped_lock lock{mutex};
241
242 std::shared_ptr<NfcDevice> device = nullptr;
243 auto result = GetDeviceHandle(device_handle, device);
244
245 if (result.IsSuccess()) {
246 result = device->OpenApplicationArea(access_id);
247 result = VerifyDeviceResult(device, result);
248 }
249
250 return result;
251}
252
253Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) const {
254 std::scoped_lock lock{mutex};
255
256 std::shared_ptr<NfcDevice> device = nullptr;
257 auto result = GetDeviceHandle(device_handle, device);
258
259 if (result.IsSuccess()) {
260 result = device->GetApplicationArea(data);
261 result = VerifyDeviceResult(device, result);
262 }
263
264 return result;
265}
266
267Result DeviceManager::SetApplicationArea(u64 device_handle, std::span<const u8> data) {
268 std::scoped_lock lock{mutex};
269
270 std::shared_ptr<NfcDevice> device = nullptr;
271 auto result = GetDeviceHandle(device_handle, device);
272
273 if (result.IsSuccess()) {
274 result = device->SetApplicationArea(data);
275 result = VerifyDeviceResult(device, result);
276 }
277
278 return result;
279}
280
281Result DeviceManager::Flush(u64 device_handle) {
282 std::scoped_lock lock{mutex};
283
284 std::shared_ptr<NfcDevice> device = nullptr;
285 auto result = GetDeviceHandle(device_handle, device);
286
287 if (result.IsSuccess()) {
288 result = device->Flush();
289 result = VerifyDeviceResult(device, result);
290 }
291
292 return result;
293}
294
295Result DeviceManager::Restore(u64 device_handle) {
296 std::scoped_lock lock{mutex};
297
298 std::shared_ptr<NfcDevice> device = nullptr;
299 auto result = GetDeviceHandle(device_handle, device);
300
301 if (result.IsSuccess()) {
302 result = device->Restore();
303 result = VerifyDeviceResult(device, result);
304 }
305
306 return result;
307}
308
309Result DeviceManager::CreateApplicationArea(u64 device_handle, u32 access_id,
310 std::span<const u8> data) {
311 std::scoped_lock lock{mutex};
312
313 std::shared_ptr<NfcDevice> device = nullptr;
314 auto result = GetDeviceHandle(device_handle, device);
315
316 if (result.IsSuccess()) {
317 result = device->CreateApplicationArea(access_id, data);
318 result = VerifyDeviceResult(device, result);
319 }
320
321 return result;
322}
323
324Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const {
325 std::scoped_lock lock{mutex};
326
327 std::shared_ptr<NfcDevice> device = nullptr;
328 auto result = GetDeviceHandle(device_handle, device);
329
330 if (result.IsSuccess()) {
331 result = device->GetRegisterInfo(register_info);
332 result = VerifyDeviceResult(device, result);
333 }
334
335 return result;
336}
337
338Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const {
339 std::scoped_lock lock{mutex};
340
341 std::shared_ptr<NfcDevice> device = nullptr;
342 auto result = GetDeviceHandle(device_handle, device);
343
344 if (result.IsSuccess()) {
345 result = device->GetCommonInfo(common_info);
346 result = VerifyDeviceResult(device, result);
347 }
348
349 return result;
350}
351
352Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const {
353 std::scoped_lock lock{mutex};
354
355 std::shared_ptr<NfcDevice> device = nullptr;
356 auto result = GetDeviceHandle(device_handle, device);
357
358 if (result.IsSuccess()) {
359 result = device->GetModelInfo(model_info);
360 result = VerifyDeviceResult(device, result);
361 }
362
363 return result;
364}
365
366u32 DeviceManager::GetApplicationAreaSize() const {
367 return sizeof(NFP::ApplicationArea);
368}
369
370Result DeviceManager::RecreateApplicationArea(u64 device_handle, u32 access_id,
371 std::span<const u8> data) {
372 std::scoped_lock lock{mutex};
373
374 std::shared_ptr<NfcDevice> device = nullptr;
375 auto result = GetDeviceHandle(device_handle, device);
376
377 if (result.IsSuccess()) {
378 result = device->RecreateApplicationArea(access_id, data);
379 result = VerifyDeviceResult(device, result);
380 }
381
382 return result;
383}
384
385Result DeviceManager::Format(u64 device_handle) {
386 std::scoped_lock lock{mutex};
387
388 std::shared_ptr<NfcDevice> device = nullptr;
389 auto result = GetDeviceHandle(device_handle, device);
390
391 if (result.IsSuccess()) {
392 result = device->Format();
393 result = VerifyDeviceResult(device, result);
394 }
395
396 return result;
397}
398
399Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const {
400 std::scoped_lock lock{mutex};
401
402 std::shared_ptr<NfcDevice> device = nullptr;
403 auto result = GetDeviceHandle(device_handle, device);
404
405 if (result.IsSuccess()) {
406 result = device->GetAdminInfo(admin_info);
407 result = VerifyDeviceResult(device, result);
408 }
409
410 return result;
411}
412
413Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle,
414 NFP::RegisterInfoPrivate& register_info) const {
415 std::scoped_lock lock{mutex};
416
417 std::shared_ptr<NfcDevice> device = nullptr;
418 auto result = GetDeviceHandle(device_handle, device);
419
420 if (result.IsSuccess()) {
421 result = device->GetRegisterInfoPrivate(register_info);
422 result = VerifyDeviceResult(device, result);
423 }
424
425 return result;
426}
427
428Result DeviceManager::SetRegisterInfoPrivate(u64 device_handle,
429 const NFP::RegisterInfoPrivate& register_info) {
430 std::scoped_lock lock{mutex};
431
432 std::shared_ptr<NfcDevice> device = nullptr;
433 auto result = GetDeviceHandle(device_handle, device);
434
435 if (result.IsSuccess()) {
436 result = device->SetRegisterInfoPrivate(register_info);
437 result = VerifyDeviceResult(device, result);
438 }
439
440 return result;
441}
442
443Result DeviceManager::DeleteRegisterInfo(u64 device_handle) {
444 std::scoped_lock lock{mutex};
445
446 std::shared_ptr<NfcDevice> device = nullptr;
447 auto result = GetDeviceHandle(device_handle, device);
448
449 if (result.IsSuccess()) {
450 result = device->DeleteRegisterInfo();
451 result = VerifyDeviceResult(device, result);
452 }
453
454 return result;
455}
456
457Result DeviceManager::DeleteApplicationArea(u64 device_handle) {
458 std::scoped_lock lock{mutex};
459
460 std::shared_ptr<NfcDevice> device = nullptr;
461 auto result = GetDeviceHandle(device_handle, device);
462
463 if (result.IsSuccess()) {
464 result = device->DeleteApplicationArea();
465 result = VerifyDeviceResult(device, result);
466 }
467
468 return result;
469}
470
471Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const {
472 std::scoped_lock lock{mutex};
473
474 std::shared_ptr<NfcDevice> device = nullptr;
475 auto result = GetDeviceHandle(device_handle, device);
476
477 if (result.IsSuccess()) {
478 result = device->ExistsApplicationArea(has_application_area);
479 result = VerifyDeviceResult(device, result);
480 }
481
482 return result;
483}
484
485Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const {
486 std::scoped_lock lock{mutex};
487
488 std::shared_ptr<NfcDevice> device = nullptr;
489 auto result = GetDeviceHandle(device_handle, device);
490
491 if (result.IsSuccess()) {
492 result = device->GetAll(nfp_data);
493 result = VerifyDeviceResult(device, result);
494 }
495
496 return result;
497}
498
499Result DeviceManager::SetAll(u64 device_handle, const NFP::NfpData& nfp_data) {
500 std::scoped_lock lock{mutex};
501
502 std::shared_ptr<NfcDevice> device = nullptr;
503 auto result = GetDeviceHandle(device_handle, device);
504
505 if (result.IsSuccess()) {
506 result = device->SetAll(nfp_data);
507 result = VerifyDeviceResult(device, result);
508 }
509
510 return result;
511}
512
513Result DeviceManager::FlushDebug(u64 device_handle) {
514 std::scoped_lock lock{mutex};
515
516 std::shared_ptr<NfcDevice> device = nullptr;
517 auto result = GetDeviceHandle(device_handle, device);
518
519 if (result.IsSuccess()) {
520 result = device->FlushDebug();
521 result = VerifyDeviceResult(device, result);
522 }
523
524 return result;
525}
526
527Result DeviceManager::BreakTag(u64 device_handle, NFP::BreakType break_type) {
528 std::scoped_lock lock{mutex};
529
530 std::shared_ptr<NfcDevice> device = nullptr;
531 auto result = GetDeviceHandle(device_handle, device);
532
533 if (result.IsSuccess()) {
534 result = device->BreakTag(break_type);
535 result = VerifyDeviceResult(device, result);
536 }
537
538 return result;
539}
540
541Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) const {
542 std::scoped_lock lock{mutex};
543
544 std::shared_ptr<NfcDevice> device = nullptr;
545 auto result = GetDeviceHandle(device_handle, device);
546
547 if (result.IsSuccess()) {
548 result = device->ReadBackupData(data);
549 result = VerifyDeviceResult(device, result);
550 }
551
552 return result;
553}
554
555Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> data) {
556 std::scoped_lock lock{mutex};
557
558 std::shared_ptr<NfcDevice> device = nullptr;
559 auto result = GetDeviceHandle(device_handle, device);
560
561 if (result.IsSuccess()) {
562 result = device->WriteBackupData(data);
563 result = VerifyDeviceResult(device, result);
564 }
565
566 return result;
567}
568
569Result DeviceManager::WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data) {
570 std::scoped_lock lock{mutex};
571
572 std::shared_ptr<NfcDevice> device = nullptr;
573 auto result = GetDeviceHandle(device_handle, device);
574
575 if (result.IsSuccess()) {
576 result = device->WriteNtf(data);
577 result = VerifyDeviceResult(device, result);
578 }
579
580 return result;
581}
582
583Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device,
584 bool check_state) const {
585 if (check_state) {
586 const Result is_parameter_set = IsNfcParameterSet();
587 if (is_parameter_set.IsError()) {
588 return is_parameter_set;
589 }
590 const Result is_enabled = IsNfcEnabled();
591 if (is_enabled.IsError()) {
592 return is_enabled;
593 }
594 const Result is_nfc_initialized = IsNfcInitialized();
595 if (is_nfc_initialized.IsError()) {
596 return is_nfc_initialized;
597 }
598 }
599
600 for (auto& device : devices) {
601 if (device->GetHandle() == handle) {
602 nfc_device = device;
603 return ResultSuccess;
604 }
605 }
606
607 return ResultDeviceNotFound;
608}
609
610std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) {
611 for (auto& device : devices) {
612 if (device->GetHandle() == handle) {
613 return device;
614 }
615 }
616 return std::nullopt;
617}
618
619const std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) const {
620 for (auto& device : devices) {
621 if (device->GetHandle() == handle) {
622 return device;
623 }
624 }
625 return std::nullopt;
626}
627
628Result DeviceManager::GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const {
629 const auto result = GetDeviceFromHandle(handle, device, true);
630 if (result.IsError()) {
631 return result;
632 }
633 return CheckDeviceState(device);
634}
635
636Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
637 Result operation_result) const {
638 if (operation_result.IsSuccess()) {
639 return operation_result;
640 }
641
642 const Result is_parameter_set = IsNfcParameterSet();
643 if (is_parameter_set.IsError()) {
644 return is_parameter_set;
645 }
646 const Result is_enabled = IsNfcEnabled();
647 if (is_enabled.IsError()) {
648 return is_enabled;
649 }
650 const Result is_nfc_initialized = IsNfcInitialized();
651 if (is_nfc_initialized.IsError()) {
652 return is_nfc_initialized;
653 }
654 const Result device_state = CheckDeviceState(device);
655 if (device_state.IsError()) {
656 return device_state;
657 }
658
659 return operation_result;
660}
661
662Result DeviceManager::CheckDeviceState(std::shared_ptr<NfcDevice> device) const {
663 if (device == nullptr) {
664 return ResultInvalidArgument;
665 }
666
667 return ResultSuccess;
668}
669
670Result DeviceManager::IsNfcEnabled() const {
671 // TODO: This calls nn::settings::detail::GetNfcEnableFlag
672 const bool is_enabled = true;
673 if (!is_enabled) {
674 return ResultNfcDisabled;
675 }
676 return ResultSuccess;
677}
678
679Result DeviceManager::IsNfcParameterSet() const {
680 // TODO: This calls checks against a bool on offset 0x450
681 const bool is_set = true;
682 if (!is_set) {
683 return ResultUnknown76;
684 }
685 return ResultSuccess;
686}
687
688Result DeviceManager::IsNfcInitialized() const {
689 if (!is_initialized) {
690 return ResultNfcNotInitialized;
691 }
692 return ResultSuccess;
693}
694
695} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h
new file mode 100644
index 000000000..2971e280f
--- /dev/null
+++ b/src/core/hle/service/nfc/common/device_manager.h
@@ -0,0 +1,100 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <optional>
9#include <span>
10
11#include "core/hid/hid_types.h"
12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nfc/mifare_types.h"
14#include "core/hle/service/nfc/nfc_types.h"
15#include "core/hle/service/nfp/nfp_types.h"
16#include "core/hle/service/service.h"
17#include "core/hle/service/time/clock_types.h"
18
19namespace Service::NFC {
20class NfcDevice;
21
22class DeviceManager {
23public:
24 explicit DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
25 ~DeviceManager();
26
27 // Nfc device manager
28 Result Initialize();
29 Result Finalize();
30 Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices) const;
31 DeviceState GetDeviceState(u64 device_handle) const;
32 Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const;
33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
35 Result StopDetection(u64 device_handle);
36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const;
37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;
38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;
39 Result ReadMifare(u64 device_handle,
40 const std::span<const MifareReadBlockParameter> read_parameters,
41 std::span<MifareReadBlockData> read_data);
42 Result WriteMifare(u64 device_handle,
43 std::span<const MifareWriteBlockParameter> write_parameters);
44 Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout,
45 std::span<const u8> command_data, std::span<u8> out_data);
46
47 // Nfp device manager
48 Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target);
49 Result Unmount(u64 device_handle);
50 Result OpenApplicationArea(u64 device_handle, u32 access_id);
51 Result GetApplicationArea(u64 device_handle, std::span<u8> data) const;
52 Result SetApplicationArea(u64 device_handle, std::span<const u8> data);
53 Result Flush(u64 device_handle);
54 Result Restore(u64 device_handle);
55 Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
56 Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const;
57 Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const;
58 Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const;
59 u32 GetApplicationAreaSize() const;
60 Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
61 Result Format(u64 device_handle);
62 Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const;
63 Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info) const;
64 Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info);
65 Result DeleteRegisterInfo(u64 device_handle);
66 Result DeleteApplicationArea(u64 device_handle);
67 Result ExistsApplicationArea(u64 device_handle, bool& has_application_area) const;
68 Result GetAll(u64 device_handle, NFP::NfpData& nfp_data) const;
69 Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data);
70 Result FlushDebug(u64 device_handle);
71 Result BreakTag(u64 device_handle, NFP::BreakType break_type);
72 Result ReadBackupData(u64 device_handle, std::span<u8> data) const;
73 Result WriteBackupData(u64 device_handle, std::span<const u8> data);
74 Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data);
75
76private:
77 Result IsNfcEnabled() const;
78 Result IsNfcParameterSet() const;
79 Result IsNfcInitialized() const;
80
81 Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device,
82 bool check_state) const;
83
84 Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const;
85 Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result) const;
86 Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const;
87
88 std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
89 const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
90
91 bool is_initialized = false;
92 mutable std::mutex mutex;
93 std::array<std::shared_ptr<NfcDevice>, 10> devices{};
94
95 Core::System& system;
96 KernelHelpers::ServiceContext service_context;
97 Kernel::KEvent* availability_change_event;
98};
99
100} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h
new file mode 100644
index 000000000..4b60048a5
--- /dev/null
+++ b/src/core/hle/service/nfc/mifare_result.h
@@ -0,0 +1,17 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::NFC::Mifare {
9
10constexpr Result ResultDeviceNotFound(ErrorModule::NFCMifare, 64);
11constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
12constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
13constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
14constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
15constexpr Result ResultReadError(ErrorModule::NFCMifare, 288);
16
17} // namespace Service::NFC::Mifare
diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h
new file mode 100644
index 000000000..75b59f021
--- /dev/null
+++ b/src/core/hle/service/nfc/mifare_types.h
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11namespace Service::NFC {
12
13enum class MifareCmd : u8 {
14 AuthA = 0x60,
15 AuthB = 0x61,
16 Read = 0x30,
17 Write = 0xA0,
18 Transfer = 0xB0,
19 Decrement = 0xC0,
20 Increment = 0xC1,
21 Store = 0xC2
22};
23
24using DataBlock = std::array<u8, 0x10>;
25using KeyData = std::array<u8, 0x6>;
26
27struct SectorKey {
28 MifareCmd command;
29 u8 unknown; // Usually 1
30 INSERT_PADDING_BYTES(0x6);
31 KeyData sector_key;
32 INSERT_PADDING_BYTES(0x2);
33};
34static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
35
36// This is nn::nfc::MifareReadBlockParameter
37struct MifareReadBlockParameter {
38 u8 sector_number;
39 INSERT_PADDING_BYTES(0x7);
40 SectorKey sector_key;
41};
42static_assert(sizeof(MifareReadBlockParameter) == 0x18,
43 "MifareReadBlockParameter is an invalid size");
44
45// This is nn::nfc::MifareReadBlockData
46struct MifareReadBlockData {
47 DataBlock data;
48 u8 sector_number;
49 INSERT_PADDING_BYTES(0x7);
50};
51static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
52
53// This is nn::nfc::MifareWriteBlockParameter
54struct MifareWriteBlockParameter {
55 DataBlock data;
56 u8 sector_number;
57 INSERT_PADDING_BYTES(0x7);
58 SectorKey sector_key;
59};
60static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
61 "MifareWriteBlockParameter is an invalid size");
62
63} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/mifare_user.cpp b/src/core/hle/service/nfc/mifare_user.cpp
deleted file mode 100644
index e0bbd46e1..000000000
--- a/src/core/hle/service/nfc/mifare_user.cpp
+++ /dev/null
@@ -1,400 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hid/hid_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nfc/mifare_user.h"
10#include "core/hle/service/nfc/nfc_device.h"
11#include "core/hle/service/nfc/nfc_result.h"
12
13namespace Service::NFC {
14
15MFIUser::MFIUser(Core::System& system_)
16 : ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} {
17 static const FunctionInfo functions[] = {
18 {0, &MFIUser::Initialize, "Initialize"},
19 {1, &MFIUser::Finalize, "Finalize"},
20 {2, &MFIUser::ListDevices, "ListDevices"},
21 {3, &MFIUser::StartDetection, "StartDetection"},
22 {4, &MFIUser::StopDetection, "StopDetection"},
23 {5, &MFIUser::Read, "Read"},
24 {6, &MFIUser::Write, "Write"},
25 {7, &MFIUser::GetTagInfo, "GetTagInfo"},
26 {8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"},
27 {9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"},
28 {10, &MFIUser::GetState, "GetState"},
29 {11, &MFIUser::GetDeviceState, "GetDeviceState"},
30 {12, &MFIUser::GetNpadId, "GetNpadId"},
31 {13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"},
32 };
33 RegisterHandlers(functions);
34
35 availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent");
36
37 for (u32 device_index = 0; device_index < 10; device_index++) {
38 devices[device_index] =
39 std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
40 service_context, availability_change_event);
41 }
42}
43
44MFIUser ::~MFIUser() {
45 availability_change_event->Close();
46}
47
48void MFIUser::Initialize(HLERequestContext& ctx) {
49 LOG_INFO(Service_NFC, "called");
50
51 state = State::Initialized;
52
53 for (auto& device : devices) {
54 device->Initialize();
55 }
56
57 IPC::ResponseBuilder rb{ctx, 2, 0};
58 rb.Push(ResultSuccess);
59}
60
61void MFIUser::Finalize(HLERequestContext& ctx) {
62 LOG_INFO(Service_NFC, "called");
63
64 state = State::NonInitialized;
65
66 for (auto& device : devices) {
67 device->Finalize();
68 }
69
70 IPC::ResponseBuilder rb{ctx, 2};
71 rb.Push(ResultSuccess);
72}
73
74void MFIUser::ListDevices(HLERequestContext& ctx) {
75 LOG_DEBUG(Service_NFC, "called");
76
77 if (state == State::NonInitialized) {
78 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(MifareNfcDisabled);
80 return;
81 }
82
83 if (!ctx.CanWriteBuffer()) {
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(MifareInvalidArgument);
86 return;
87 }
88
89 if (ctx.GetWriteBufferSize() == 0) {
90 IPC::ResponseBuilder rb{ctx, 2};
91 rb.Push(MifareInvalidArgument);
92 return;
93 }
94
95 std::vector<u64> nfp_devices;
96 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
97
98 for (const auto& device : devices) {
99 if (nfp_devices.size() >= max_allowed_devices) {
100 continue;
101 }
102 if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
103 nfp_devices.push_back(device->GetHandle());
104 }
105 }
106
107 if (nfp_devices.empty()) {
108 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(MifareDeviceNotFound);
110 return;
111 }
112
113 ctx.WriteBuffer(nfp_devices);
114
115 IPC::ResponseBuilder rb{ctx, 3};
116 rb.Push(ResultSuccess);
117 rb.Push(static_cast<s32>(nfp_devices.size()));
118}
119
120void MFIUser::StartDetection(HLERequestContext& ctx) {
121 IPC::RequestParser rp{ctx};
122 const auto device_handle{rp.Pop<u64>()};
123 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
124
125 if (state == State::NonInitialized) {
126 IPC::ResponseBuilder rb{ctx, 2};
127 rb.Push(MifareNfcDisabled);
128 return;
129 }
130
131 auto device = GetNfcDevice(device_handle);
132
133 if (!device.has_value()) {
134 IPC::ResponseBuilder rb{ctx, 2};
135 rb.Push(MifareDeviceNotFound);
136 return;
137 }
138
139 const auto result = device.value()->StartDetection(NFP::TagProtocol::All);
140 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(result);
142}
143
144void MFIUser::StopDetection(HLERequestContext& ctx) {
145 IPC::RequestParser rp{ctx};
146 const auto device_handle{rp.Pop<u64>()};
147 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
148
149 if (state == State::NonInitialized) {
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(MifareNfcDisabled);
152 return;
153 }
154
155 auto device = GetNfcDevice(device_handle);
156
157 if (!device.has_value()) {
158 IPC::ResponseBuilder rb{ctx, 2};
159 rb.Push(MifareDeviceNotFound);
160 return;
161 }
162
163 const auto result = device.value()->StopDetection();
164 IPC::ResponseBuilder rb{ctx, 2};
165 rb.Push(result);
166}
167
168void MFIUser::Read(HLERequestContext& ctx) {
169 IPC::RequestParser rp{ctx};
170 const auto device_handle{rp.Pop<u64>()};
171 const auto buffer{ctx.ReadBuffer()};
172 const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()};
173 std::vector<NFP::MifareReadBlockParameter> read_commands(number_of_commands);
174
175 memcpy(read_commands.data(), buffer.data(),
176 number_of_commands * sizeof(NFP::MifareReadBlockParameter));
177
178 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
179 device_handle, number_of_commands);
180
181 if (state == State::NonInitialized) {
182 IPC::ResponseBuilder rb{ctx, 2};
183 rb.Push(MifareNfcDisabled);
184 return;
185 }
186
187 auto device = GetNfcDevice(device_handle);
188
189 if (!device.has_value()) {
190 IPC::ResponseBuilder rb{ctx, 2};
191 rb.Push(MifareDeviceNotFound);
192 return;
193 }
194
195 Result result = ResultSuccess;
196 std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
197 for (std::size_t i = 0; i < number_of_commands; i++) {
198 result = device.value()->MifareRead(read_commands[i], out_data[i]);
199 if (result.IsError()) {
200 break;
201 }
202 }
203
204 ctx.WriteBuffer(out_data);
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(result);
207}
208
209void MFIUser::Write(HLERequestContext& ctx) {
210 IPC::RequestParser rp{ctx};
211 const auto device_handle{rp.Pop<u64>()};
212 const auto buffer{ctx.ReadBuffer()};
213 const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()};
214 std::vector<NFP::MifareWriteBlockParameter> write_commands(number_of_commands);
215
216 memcpy(write_commands.data(), buffer.data(),
217 number_of_commands * sizeof(NFP::MifareWriteBlockParameter));
218
219 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
220 device_handle, number_of_commands);
221
222 if (state == State::NonInitialized) {
223 IPC::ResponseBuilder rb{ctx, 2};
224 rb.Push(MifareNfcDisabled);
225 return;
226 }
227
228 auto device = GetNfcDevice(device_handle);
229
230 if (!device.has_value()) {
231 IPC::ResponseBuilder rb{ctx, 2};
232 rb.Push(MifareDeviceNotFound);
233 return;
234 }
235
236 Result result = ResultSuccess;
237 std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
238 for (std::size_t i = 0; i < number_of_commands; i++) {
239 result = device.value()->MifareWrite(write_commands[i]);
240 if (result.IsError()) {
241 break;
242 }
243 }
244
245 if (result.IsSuccess()) {
246 result = device.value()->Flush();
247 }
248
249 IPC::ResponseBuilder rb{ctx, 2};
250 rb.Push(result);
251}
252
253void MFIUser::GetTagInfo(HLERequestContext& ctx) {
254 IPC::RequestParser rp{ctx};
255 const auto device_handle{rp.Pop<u64>()};
256 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
257
258 if (state == State::NonInitialized) {
259 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(MifareNfcDisabled);
261 return;
262 }
263
264 auto device = GetNfcDevice(device_handle);
265
266 if (!device.has_value()) {
267 IPC::ResponseBuilder rb{ctx, 2};
268 rb.Push(MifareDeviceNotFound);
269 return;
270 }
271
272 NFP::TagInfo tag_info{};
273 const auto result = device.value()->GetTagInfo(tag_info, true);
274 ctx.WriteBuffer(tag_info);
275 IPC::ResponseBuilder rb{ctx, 2};
276 rb.Push(result);
277}
278
279void MFIUser::GetActivateEventHandle(HLERequestContext& ctx) {
280 IPC::RequestParser rp{ctx};
281 const auto device_handle{rp.Pop<u64>()};
282 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
283
284 if (state == State::NonInitialized) {
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(MifareNfcDisabled);
287 return;
288 }
289
290 auto device = GetNfcDevice(device_handle);
291
292 if (!device.has_value()) {
293 IPC::ResponseBuilder rb{ctx, 2};
294 rb.Push(MifareDeviceNotFound);
295 return;
296 }
297
298 IPC::ResponseBuilder rb{ctx, 2, 1};
299 rb.Push(ResultSuccess);
300 rb.PushCopyObjects(device.value()->GetActivateEvent());
301}
302
303void MFIUser::GetDeactivateEventHandle(HLERequestContext& ctx) {
304 IPC::RequestParser rp{ctx};
305 const auto device_handle{rp.Pop<u64>()};
306 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
307
308 if (state == State::NonInitialized) {
309 IPC::ResponseBuilder rb{ctx, 2};
310 rb.Push(MifareNfcDisabled);
311 return;
312 }
313
314 auto device = GetNfcDevice(device_handle);
315
316 if (!device.has_value()) {
317 IPC::ResponseBuilder rb{ctx, 2};
318 rb.Push(MifareDeviceNotFound);
319 return;
320 }
321
322 IPC::ResponseBuilder rb{ctx, 2, 1};
323 rb.Push(ResultSuccess);
324 rb.PushCopyObjects(device.value()->GetDeactivateEvent());
325}
326
327void MFIUser::GetState(HLERequestContext& ctx) {
328 LOG_DEBUG(Service_NFC, "called");
329
330 IPC::ResponseBuilder rb{ctx, 3};
331 rb.Push(ResultSuccess);
332 rb.PushEnum(state);
333}
334
335void MFIUser::GetDeviceState(HLERequestContext& ctx) {
336 IPC::RequestParser rp{ctx};
337 const auto device_handle{rp.Pop<u64>()};
338 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
339
340 auto device = GetNfcDevice(device_handle);
341
342 if (!device.has_value()) {
343 IPC::ResponseBuilder rb{ctx, 2};
344 rb.Push(MifareDeviceNotFound);
345 return;
346 }
347
348 IPC::ResponseBuilder rb{ctx, 3};
349 rb.Push(ResultSuccess);
350 rb.PushEnum(device.value()->GetCurrentState());
351}
352
353void MFIUser::GetNpadId(HLERequestContext& ctx) {
354 IPC::RequestParser rp{ctx};
355 const auto device_handle{rp.Pop<u64>()};
356 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
357
358 if (state == State::NonInitialized) {
359 IPC::ResponseBuilder rb{ctx, 2};
360 rb.Push(MifareNfcDisabled);
361 return;
362 }
363
364 auto device = GetNfcDevice(device_handle);
365
366 if (!device.has_value()) {
367 IPC::ResponseBuilder rb{ctx, 2};
368 rb.Push(MifareDeviceNotFound);
369 return;
370 }
371
372 IPC::ResponseBuilder rb{ctx, 3};
373 rb.Push(ResultSuccess);
374 rb.PushEnum(device.value()->GetNpadId());
375}
376
377void MFIUser::GetAvailabilityChangeEventHandle(HLERequestContext& ctx) {
378 LOG_INFO(Service_NFC, "called");
379
380 if (state == State::NonInitialized) {
381 IPC::ResponseBuilder rb{ctx, 2};
382 rb.Push(MifareNfcDisabled);
383 return;
384 }
385
386 IPC::ResponseBuilder rb{ctx, 2, 1};
387 rb.Push(ResultSuccess);
388 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
389}
390
391std::optional<std::shared_ptr<NfcDevice>> MFIUser::GetNfcDevice(u64 handle) {
392 for (auto& device : devices) {
393 if (device->GetHandle() == handle) {
394 return device;
395 }
396 }
397 return std::nullopt;
398}
399
400} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/mifare_user.h b/src/core/hle/service/nfc/mifare_user.h
deleted file mode 100644
index 9701f1d7f..000000000
--- a/src/core/hle/service/nfc/mifare_user.h
+++ /dev/null
@@ -1,52 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <optional>
9
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Service::NFC {
14class NfcDevice;
15
16class MFIUser final : public ServiceFramework<MFIUser> {
17public:
18 explicit MFIUser(Core::System& system_);
19 ~MFIUser();
20
21private:
22 enum class State : u32 {
23 NonInitialized,
24 Initialized,
25 };
26
27 void Initialize(HLERequestContext& ctx);
28 void Finalize(HLERequestContext& ctx);
29 void ListDevices(HLERequestContext& ctx);
30 void StartDetection(HLERequestContext& ctx);
31 void StopDetection(HLERequestContext& ctx);
32 void Read(HLERequestContext& ctx);
33 void Write(HLERequestContext& ctx);
34 void GetTagInfo(HLERequestContext& ctx);
35 void GetActivateEventHandle(HLERequestContext& ctx);
36 void GetDeactivateEventHandle(HLERequestContext& ctx);
37 void GetState(HLERequestContext& ctx);
38 void GetDeviceState(HLERequestContext& ctx);
39 void GetNpadId(HLERequestContext& ctx);
40 void GetAvailabilityChangeEventHandle(HLERequestContext& ctx);
41
42 std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
43
44 KernelHelpers::ServiceContext service_context;
45
46 std::array<std::shared_ptr<NfcDevice>, 10> devices{};
47
48 State state{State::NonInitialized};
49 Kernel::KEvent* availability_change_event;
50};
51
52} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 6595e34ed..30ae989b9 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -6,14 +6,115 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nfc/mifare_user.h"
10#include "core/hle/service/nfc/nfc.h" 9#include "core/hle/service/nfc/nfc.h"
11#include "core/hle/service/nfc/nfc_user.h" 10#include "core/hle/service/nfc/nfc_interface.h"
12#include "core/hle/service/server_manager.h" 11#include "core/hle/service/server_manager.h"
13#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
14 13
15namespace Service::NFC { 14namespace Service::NFC {
16 15
16class IUser final : public NfcInterface {
17public:
18 explicit IUser(Core::System& system_) : NfcInterface(system_, "NFC::IUser", BackendType::Nfc) {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, &NfcInterface::Initialize, "InitializeOld"},
22 {1, &NfcInterface::Finalize, "FinalizeOld"},
23 {2, &NfcInterface::GetState, "GetStateOld"},
24 {3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
25 {400, &NfcInterface::Initialize, "Initialize"},
26 {401, &NfcInterface::Finalize, "Finalize"},
27 {402, &NfcInterface::GetState, "GetState"},
28 {403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
29 {404, &NfcInterface::ListDevices, "ListDevices"},
30 {405, &NfcInterface::GetDeviceState, "GetDeviceState"},
31 {406, &NfcInterface::GetNpadId, "GetNpadId"},
32 {407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
33 {408, &NfcInterface::StartDetection, "StartDetection"},
34 {409, &NfcInterface::StopDetection, "StopDetection"},
35 {410, &NfcInterface::GetTagInfo, "GetTagInfo"},
36 {411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
37 {412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
38 {1000, &NfcInterface::ReadMifare, "ReadMifare"},
39 {1001, &NfcInterface::WriteMifare ,"WriteMifare"},
40 {1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
41 {1301, nullptr, "KeepPassThroughSession"},
42 {1302, nullptr, "ReleasePassThroughSession"},
43 };
44 // clang-format on
45
46 RegisterHandlers(functions);
47 }
48};
49
50class ISystem final : public NfcInterface {
51public:
52 explicit ISystem(Core::System& system_)
53 : NfcInterface{system_, "NFC::ISystem", BackendType::Nfc} {
54 // clang-format off
55 static const FunctionInfo functions[] = {
56 {0, &NfcInterface::Initialize, "InitializeOld"},
57 {1, &NfcInterface::Finalize, "FinalizeOld"},
58 {2, &NfcInterface::GetState, "GetStateOld"},
59 {3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
60 {100, nullptr, "SetNfcEnabledOld"},
61 {400, &NfcInterface::Initialize, "Initialize"},
62 {401, &NfcInterface::Finalize, "Finalize"},
63 {402, &NfcInterface::GetState, "GetState"},
64 {403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
65 {404, &NfcInterface::ListDevices, "ListDevices"},
66 {405, &NfcInterface::GetDeviceState, "GetDeviceState"},
67 {406, &NfcInterface::GetNpadId, "GetNpadId"},
68 {407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
69 {408, &NfcInterface::StartDetection, "StartDetection"},
70 {409, &NfcInterface::StopDetection, "StopDetection"},
71 {410, &NfcInterface::GetTagInfo, "GetTagInfo"},
72 {411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
73 {412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
74 {500, nullptr, "SetNfcEnabled"},
75 {510, nullptr, "OutputTestWave"},
76 {1000, &NfcInterface::ReadMifare, "ReadMifare"},
77 {1001, &NfcInterface::WriteMifare, "WriteMifare"},
78 {1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
79 {1301, nullptr, "KeepPassThroughSession"},
80 {1302, nullptr, "ReleasePassThroughSession"},
81 };
82 // clang-format on
83
84 RegisterHandlers(functions);
85 }
86};
87
88// MFInterface has an unique interface but it's identical to NfcInterface so we can keep the code
89// simpler
90using MFInterface = NfcInterface;
91class MFIUser final : public MFInterface {
92public:
93 explicit MFIUser(Core::System& system_)
94 : MFInterface{system_, "NFC::MFInterface", BackendType::Mifare} {
95 // clang-format off
96 static const FunctionInfoTyped<MFIUser> functions[] = {
97 {0, &MFIUser::Initialize, "Initialize"},
98 {1, &MFIUser::Finalize, "Finalize"},
99 {2, &MFIUser::ListDevices, "ListDevices"},
100 {3, &MFIUser::StartDetection, "StartDetection"},
101 {4, &MFIUser::StopDetection, "StopDetection"},
102 {5, &MFIUser::ReadMifare, "Read"},
103 {6, &MFIUser::WriteMifare, "Write"},
104 {7, &MFIUser::GetTagInfo, "GetTagInfo"},
105 {8, &MFIUser::AttachActivateEvent, "GetActivateEventHandle"},
106 {9, &MFIUser::AttachDeactivateEvent, "GetDeactivateEventHandle"},
107 {10, &MFIUser::GetState, "GetState"},
108 {11, &MFIUser::GetDeviceState, "GetDeviceState"},
109 {12, &MFIUser::GetNpadId, "GetNpadId"},
110 {13, &MFIUser::AttachAvailabilityChangeEvent, "GetAvailabilityChangeEventHandle"},
111 };
112 // clang-format on
113
114 RegisterHandlers(functions);
115 }
116};
117
17class IAm final : public ServiceFramework<IAm> { 118class IAm final : public ServiceFramework<IAm> {
18public: 119public:
19 explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} { 120 explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} {
@@ -34,7 +135,7 @@ public:
34 explicit NFC_AM(Core::System& system_) : ServiceFramework{system_, "nfc:am"} { 135 explicit NFC_AM(Core::System& system_) : ServiceFramework{system_, "nfc:am"} {
35 // clang-format off 136 // clang-format off
36 static const FunctionInfo functions[] = { 137 static const FunctionInfo functions[] = {
37 {0, &NFC_AM::CreateAmInterface, "CreateAmInterface"}, 138 {0, &NFC_AM::CreateAmNfcInterface, "CreateAmNfcInterface"},
38 }; 139 };
39 // clang-format on 140 // clang-format on
40 141
@@ -42,7 +143,7 @@ public:
42 } 143 }
43 144
44private: 145private:
45 void CreateAmInterface(HLERequestContext& ctx) { 146 void CreateAmNfcInterface(HLERequestContext& ctx) {
46 LOG_DEBUG(Service_NFC, "called"); 147 LOG_DEBUG(Service_NFC, "called");
47 148
48 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 149 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -56,7 +157,7 @@ public:
56 explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} { 157 explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {
57 // clang-format off 158 // clang-format off
58 static const FunctionInfo functions[] = { 159 static const FunctionInfo functions[] = {
59 {0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"}, 160 {0, &NFC_MF_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
60 }; 161 };
61 // clang-format on 162 // clang-format on
62 163
@@ -64,7 +165,7 @@ public:
64 } 165 }
65 166
66private: 167private:
67 void CreateUserInterface(HLERequestContext& ctx) { 168 void CreateUserNfcInterface(HLERequestContext& ctx) {
68 LOG_DEBUG(Service_NFC, "called"); 169 LOG_DEBUG(Service_NFC, "called");
69 170
70 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 171 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -78,7 +179,7 @@ public:
78 explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} { 179 explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {
79 // clang-format off 180 // clang-format off
80 static const FunctionInfo functions[] = { 181 static const FunctionInfo functions[] = {
81 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, 182 {0, &NFC_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
82 }; 183 };
83 // clang-format on 184 // clang-format on
84 185
@@ -86,7 +187,7 @@ public:
86 } 187 }
87 188
88private: 189private:
89 void CreateUserInterface(HLERequestContext& ctx) { 190 void CreateUserNfcInterface(HLERequestContext& ctx) {
90 LOG_DEBUG(Service_NFC, "called"); 191 LOG_DEBUG(Service_NFC, "called");
91 192
92 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 193 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -95,49 +196,12 @@ private:
95 } 196 }
96}; 197};
97 198
98class ISystem final : public ServiceFramework<ISystem> {
99public:
100 explicit ISystem(Core::System& system_) : ServiceFramework{system_, "ISystem"} {
101 // clang-format off
102 static const FunctionInfo functions[] = {
103 {0, nullptr, "Initialize"},
104 {1, nullptr, "Finalize"},
105 {2, nullptr, "GetStateOld"},
106 {3, nullptr, "IsNfcEnabledOld"},
107 {100, nullptr, "SetNfcEnabledOld"},
108 {400, nullptr, "InitializeSystem"},
109 {401, nullptr, "FinalizeSystem"},
110 {402, nullptr, "GetState"},
111 {403, nullptr, "IsNfcEnabled"},
112 {404, nullptr, "ListDevices"},
113 {405, nullptr, "GetDeviceState"},
114 {406, nullptr, "GetNpadId"},
115 {407, nullptr, "AttachAvailabilityChangeEvent"},
116 {408, nullptr, "StartDetection"},
117 {409, nullptr, "StopDetection"},
118 {410, nullptr, "GetTagInfo"},
119 {411, nullptr, "AttachActivateEvent"},
120 {412, nullptr, "AttachDeactivateEvent"},
121 {500, nullptr, "SetNfcEnabled"},
122 {510, nullptr, "OutputTestWave"},
123 {1000, nullptr, "ReadMifare"},
124 {1001, nullptr, "WriteMifare"},
125 {1300, nullptr, "SendCommandByPassThrough"},
126 {1301, nullptr, "KeepPassThroughSession"},
127 {1302, nullptr, "ReleasePassThroughSession"},
128 };
129 // clang-format on
130
131 RegisterHandlers(functions);
132 }
133};
134
135class NFC_SYS final : public ServiceFramework<NFC_SYS> { 199class NFC_SYS final : public ServiceFramework<NFC_SYS> {
136public: 200public:
137 explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} { 201 explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} {
138 // clang-format off 202 // clang-format off
139 static const FunctionInfo functions[] = { 203 static const FunctionInfo functions[] = {
140 {0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"}, 204 {0, &NFC_SYS::CreateSystemNfcInterface, "CreateSystemNfcInterface"},
141 }; 205 };
142 // clang-format on 206 // clang-format on
143 207
@@ -145,7 +209,7 @@ public:
145 } 209 }
146 210
147private: 211private:
148 void CreateSystemInterface(HLERequestContext& ctx) { 212 void CreateSystemNfcInterface(HLERequestContext& ctx) {
149 LOG_DEBUG(Service_NFC, "called"); 213 LOG_DEBUG(Service_NFC, "called");
150 214
151 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 215 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -161,6 +225,7 @@ void LoopProcess(Core::System& system) {
161 server_manager->RegisterNamedService("nfc:mf:u", std::make_shared<NFC_MF_U>(system)); 225 server_manager->RegisterNamedService("nfc:mf:u", std::make_shared<NFC_MF_U>(system));
162 server_manager->RegisterNamedService("nfc:user", std::make_shared<NFC_U>(system)); 226 server_manager->RegisterNamedService("nfc:user", std::make_shared<NFC_U>(system));
163 server_manager->RegisterNamedService("nfc:sys", std::make_shared<NFC_SYS>(system)); 227 server_manager->RegisterNamedService("nfc:sys", std::make_shared<NFC_SYS>(system));
228
164 ServerManager::RunServer(std::move(server_manager)); 229 ServerManager::RunServer(std::move(server_manager));
165} 230}
166 231
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp
deleted file mode 100644
index c7db74d14..000000000
--- a/src/core/hle/service/nfc/nfc_device.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/input.h"
5#include "common/logging/log.h"
6#include "core/core.h"
7#include "core/hid/emulated_controller.h"
8#include "core/hid/hid_core.h"
9#include "core/hid/hid_types.h"
10#include "core/hle/kernel/k_event.h"
11#include "core/hle/service/ipc_helpers.h"
12#include "core/hle/service/nfc/nfc_device.h"
13#include "core/hle/service/nfc/nfc_result.h"
14#include "core/hle/service/nfc/nfc_user.h"
15
16namespace Service::NFC {
17NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
18 KernelHelpers::ServiceContext& service_context_,
19 Kernel::KEvent* availability_change_event_)
20 : npad_id{npad_id_}, system{system_}, service_context{service_context_},
21 availability_change_event{availability_change_event_} {
22 activate_event = service_context.CreateEvent("IUser:NFCActivateEvent");
23 deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent");
24 npad_device = system.HIDCore().GetEmulatedController(npad_id);
25
26 Core::HID::ControllerUpdateCallback engine_callback{
27 .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
28 .is_npad_service = false,
29 };
30 is_controller_set = true;
31 callback_key = npad_device->SetCallback(engine_callback);
32}
33
34NfcDevice::~NfcDevice() {
35 activate_event->Close();
36 deactivate_event->Close();
37 if (!is_controller_set) {
38 return;
39 }
40 npad_device->DeleteCallback(callback_key);
41 is_controller_set = false;
42};
43
44void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
45 if (!is_initalized) {
46 return;
47 }
48
49 if (type == Core::HID::ControllerTriggerType::Connected) {
50 Initialize();
51 availability_change_event->Signal();
52 return;
53 }
54
55 if (type == Core::HID::ControllerTriggerType::Disconnected) {
56 device_state = NFP::DeviceState::Unavailable;
57 availability_change_event->Signal();
58 return;
59 }
60
61 if (type != Core::HID::ControllerTriggerType::Nfc) {
62 return;
63 }
64
65 if (!npad_device->IsConnected()) {
66 return;
67 }
68
69 const auto nfc_status = npad_device->GetNfc();
70 switch (nfc_status.state) {
71 case Common::Input::NfcState::NewAmiibo:
72 LoadNfcTag(nfc_status.data);
73 break;
74 case Common::Input::NfcState::AmiiboRemoved:
75 if (device_state != NFP::DeviceState::SearchingForTag) {
76 CloseNfcTag();
77 }
78 break;
79 default:
80 break;
81 }
82}
83
84bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
85 if (device_state != NFP::DeviceState::SearchingForTag) {
86 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
87 return false;
88 }
89
90 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
91 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
92 return false;
93 }
94
95 tag_data.resize(data.size());
96 memcpy(tag_data.data(), data.data(), data.size());
97 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
98
99 device_state = NFP::DeviceState::TagFound;
100 deactivate_event->GetReadableEvent().Clear();
101 activate_event->Signal();
102 return true;
103}
104
105void NfcDevice::CloseNfcTag() {
106 LOG_INFO(Service_NFC, "Remove nfc tag");
107
108 device_state = NFP::DeviceState::TagRemoved;
109 encrypted_tag_data = {};
110 activate_event->GetReadableEvent().Clear();
111 deactivate_event->Signal();
112}
113
114Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
115 return activate_event->GetReadableEvent();
116}
117
118Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
119 return deactivate_event->GetReadableEvent();
120}
121
122void NfcDevice::Initialize() {
123 device_state =
124 npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable;
125 encrypted_tag_data = {};
126 is_initalized = true;
127}
128
129void NfcDevice::Finalize() {
130 if (device_state == NFP::DeviceState::SearchingForTag ||
131 device_state == NFP::DeviceState::TagRemoved) {
132 StopDetection();
133 }
134 device_state = NFP::DeviceState::Unavailable;
135 is_initalized = false;
136}
137
138Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
139 if (device_state != NFP::DeviceState::Initialized &&
140 device_state != NFP::DeviceState::TagRemoved) {
141 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
142 return WrongDeviceState;
143 }
144
145 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
146 Common::Input::PollingMode::NFC) !=
147 Common::Input::DriverResult::Success) {
148 LOG_ERROR(Service_NFC, "Nfc not supported");
149 return NfcDisabled;
150 }
151
152 device_state = NFP::DeviceState::SearchingForTag;
153 allowed_protocols = allowed_protocol;
154 return ResultSuccess;
155}
156
157Result NfcDevice::StopDetection() {
158 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
159 Common::Input::PollingMode::Active);
160
161 if (device_state == NFP::DeviceState::Initialized) {
162 return ResultSuccess;
163 }
164
165 if (device_state == NFP::DeviceState::TagFound ||
166 device_state == NFP::DeviceState::TagMounted) {
167 CloseNfcTag();
168 return ResultSuccess;
169 }
170 if (device_state == NFP::DeviceState::SearchingForTag ||
171 device_state == NFP::DeviceState::TagRemoved) {
172 device_state = NFP::DeviceState::Initialized;
173 return ResultSuccess;
174 }
175
176 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
177 return WrongDeviceState;
178}
179
180Result NfcDevice::Flush() {
181 if (device_state != NFP::DeviceState::TagFound &&
182 device_state != NFP::DeviceState::TagMounted) {
183 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
184 if (device_state == NFP::DeviceState::TagRemoved) {
185 return TagRemoved;
186 }
187 return WrongDeviceState;
188 }
189
190 if (!npad_device->WriteNfc(tag_data)) {
191 LOG_ERROR(Service_NFP, "Error writing to file");
192 return MifareReadError;
193 }
194
195 return ResultSuccess;
196}
197
198Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
199 if (device_state != NFP::DeviceState::TagFound &&
200 device_state != NFP::DeviceState::TagMounted) {
201 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
202 if (device_state == NFP::DeviceState::TagRemoved) {
203 return TagRemoved;
204 }
205 return WrongDeviceState;
206 }
207
208 if (is_mifare) {
209 tag_info = {
210 .uuid = encrypted_tag_data.uuid.uid,
211 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
212 .protocol = NFP::TagProtocol::TypeA,
213 .tag_type = NFP::TagType::Type4,
214 };
215 return ResultSuccess;
216 }
217
218 // Protocol and tag type may change here
219 tag_info = {
220 .uuid = encrypted_tag_data.uuid.uid,
221 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
222 .protocol = NFP::TagProtocol::TypeA,
223 .tag_type = NFP::TagType::Type2,
224 };
225
226 return ResultSuccess;
227}
228
229Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter,
230 NFP::MifareReadBlockData& read_block_data) {
231 const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
232 read_block_data.sector_number = parameter.sector_number;
233
234 if (device_state != NFP::DeviceState::TagFound &&
235 device_state != NFP::DeviceState::TagMounted) {
236 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
237 if (device_state == NFP::DeviceState::TagRemoved) {
238 return TagRemoved;
239 }
240 return WrongDeviceState;
241 }
242
243 if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
244 return MifareReadError;
245 }
246
247 // TODO: Use parameter.sector_key to read encrypted data
248 memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock));
249
250 return ResultSuccess;
251}
252
253Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) {
254 const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
255
256 if (device_state != NFP::DeviceState::TagFound &&
257 device_state != NFP::DeviceState::TagMounted) {
258 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
259 if (device_state == NFP::DeviceState::TagRemoved) {
260 return TagRemoved;
261 }
262 return WrongDeviceState;
263 }
264
265 if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
266 return MifareReadError;
267 }
268
269 // TODO: Use parameter.sector_key to encrypt the data
270 memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock));
271
272 return ResultSuccess;
273}
274
275u64 NfcDevice::GetHandle() const {
276 // Generate a handle based of the npad id
277 return static_cast<u64>(npad_id);
278}
279
280NFP::DeviceState NfcDevice::GetCurrentState() const {
281 return device_state;
282}
283
284Core::HID::NpadIdType NfcDevice::GetNpadId() const {
285 return npad_id;
286}
287
288} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_device.h b/src/core/hle/service/nfc/nfc_device.h
deleted file mode 100644
index ea63f0537..000000000
--- a/src/core/hle/service/nfc/nfc_device.h
+++ /dev/null
@@ -1,78 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/nfp/nfp_types.h"
9#include "core/hle/service/service.h"
10
11namespace Kernel {
12class KEvent;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Core {
17class System;
18} // namespace Core
19
20namespace Core::HID {
21class EmulatedController;
22enum class ControllerTriggerType;
23enum class NpadIdType : u32;
24} // namespace Core::HID
25
26namespace Service::NFC {
27class NfcDevice {
28public:
29 NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
30 KernelHelpers::ServiceContext& service_context_,
31 Kernel::KEvent* availability_change_event_);
32 ~NfcDevice();
33
34 void Initialize();
35 void Finalize();
36
37 Result StartDetection(NFP::TagProtocol allowed_protocol);
38 Result StopDetection();
39 Result Flush();
40
41 Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const;
42
43 Result MifareRead(const NFP::MifareReadBlockParameter& parameter,
44 NFP::MifareReadBlockData& read_block_data);
45
46 Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter);
47
48 u64 GetHandle() const;
49 NFP::DeviceState GetCurrentState() const;
50 Core::HID::NpadIdType GetNpadId() const;
51
52 Kernel::KReadableEvent& GetActivateEvent() const;
53 Kernel::KReadableEvent& GetDeactivateEvent() const;
54
55private:
56 void NpadUpdate(Core::HID::ControllerTriggerType type);
57 bool LoadNfcTag(std::span<const u8> data);
58 void CloseNfcTag();
59
60 bool is_controller_set{};
61 int callback_key;
62 const Core::HID::NpadIdType npad_id;
63 Core::System& system;
64 Core::HID::EmulatedController* npad_device = nullptr;
65 KernelHelpers::ServiceContext& service_context;
66 Kernel::KEvent* activate_event = nullptr;
67 Kernel::KEvent* deactivate_event = nullptr;
68 Kernel::KEvent* availability_change_event = nullptr;
69
70 bool is_initalized{};
71 NFP::TagProtocol allowed_protocols{};
72 NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
73
74 NFP::EncryptedNTAG215File encrypted_tag_data{};
75 std::vector<u8> tag_data{};
76};
77
78} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
new file mode 100644
index 000000000..0fa29d398
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -0,0 +1,382 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hid/hid_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nfc/common/device.h"
10#include "core/hle/service/nfc/common/device_manager.h"
11#include "core/hle/service/nfc/mifare_result.h"
12#include "core/hle/service/nfc/mifare_types.h"
13#include "core/hle/service/nfc/nfc_interface.h"
14#include "core/hle/service/nfc/nfc_result.h"
15#include "core/hle/service/nfc/nfc_types.h"
16#include "core/hle/service/nfp/nfp_result.h"
17#include "core/hle/service/time/clock_types.h"
18
19namespace Service::NFC {
20
21NfcInterface::NfcInterface(Core::System& system_, const char* name, BackendType service_backend)
22 : ServiceFramework{system_, name}, service_context{system_, service_name},
23 backend_type{service_backend} {}
24
25NfcInterface ::~NfcInterface() = default;
26
27void NfcInterface::Initialize(HLERequestContext& ctx) {
28 LOG_INFO(Service_NFC, "called");
29
30 auto manager = GetManager();
31 auto result = manager->Initialize();
32
33 if (result.IsSuccess()) {
34 state = State::Initialized;
35 } else {
36 manager->Finalize();
37 }
38
39 IPC::ResponseBuilder rb{ctx, 2, 0};
40 rb.Push(result);
41}
42
43void NfcInterface::Finalize(HLERequestContext& ctx) {
44 LOG_INFO(Service_NFC, "called");
45
46 if (state != State::NonInitialized) {
47 if (GetBackendType() != BackendType::None) {
48 GetManager()->Finalize();
49 }
50 device_manager = nullptr;
51 state = State::NonInitialized;
52 }
53
54 IPC::ResponseBuilder rb{ctx, 2};
55 rb.Push(ResultSuccess);
56}
57
58void NfcInterface::GetState(HLERequestContext& ctx) {
59 LOG_DEBUG(Service_NFC, "called");
60
61 IPC::ResponseBuilder rb{ctx, 3};
62 rb.Push(ResultSuccess);
63 rb.PushEnum(state);
64}
65
66void NfcInterface::IsNfcEnabled(HLERequestContext& ctx) {
67 LOG_DEBUG(Service_NFC, "called");
68
69 // TODO: This calls nn::settings::detail::GetNfcEnableFlag
70 const bool is_enabled = true;
71
72 IPC::ResponseBuilder rb{ctx, 3};
73 rb.Push(ResultSuccess);
74 rb.Push(is_enabled);
75}
76
77void NfcInterface::ListDevices(HLERequestContext& ctx) {
78 std::vector<u64> nfp_devices;
79 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
80 LOG_DEBUG(Service_NFC, "called");
81
82 auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices);
83 result = TranslateResultToServiceError(result);
84
85 if (result.IsError()) {
86 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(result);
88 return;
89 }
90
91 ctx.WriteBuffer(nfp_devices);
92
93 IPC::ResponseBuilder rb{ctx, 3};
94 rb.Push(ResultSuccess);
95 rb.Push(static_cast<s32>(nfp_devices.size()));
96}
97
98void NfcInterface::GetDeviceState(HLERequestContext& ctx) {
99 IPC::RequestParser rp{ctx};
100 const auto device_handle{rp.Pop<u64>()};
101 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
102
103 const auto device_state = GetManager()->GetDeviceState(device_handle);
104
105 if (device_state > DeviceState::Finalized) {
106 ASSERT_MSG(false, "Invalid device state");
107 }
108
109 IPC::ResponseBuilder rb{ctx, 3};
110 rb.Push(ResultSuccess);
111 rb.PushEnum(device_state);
112}
113
114void NfcInterface::GetNpadId(HLERequestContext& ctx) {
115 IPC::RequestParser rp{ctx};
116 const auto device_handle{rp.Pop<u64>()};
117 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
118
119 Core::HID::NpadIdType npad_id{};
120 auto result = GetManager()->GetNpadId(device_handle, npad_id);
121 result = TranslateResultToServiceError(result);
122
123 if (result.IsError()) {
124 IPC::ResponseBuilder rb{ctx, 2};
125 rb.Push(result);
126 return;
127 }
128
129 IPC::ResponseBuilder rb{ctx, 3};
130 rb.Push(ResultSuccess);
131 rb.PushEnum(npad_id);
132}
133
134void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
135 LOG_INFO(Service_NFC, "called");
136
137 IPC::ResponseBuilder rb{ctx, 2, 1};
138 rb.Push(ResultSuccess);
139 rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
140}
141
142void NfcInterface::StartDetection(HLERequestContext& ctx) {
143 IPC::RequestParser rp{ctx};
144 const auto device_handle{rp.Pop<u64>()};
145 const auto tag_protocol{rp.PopEnum<NfcProtocol>()};
146 LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
147
148 auto result = GetManager()->StartDetection(device_handle, tag_protocol);
149 result = TranslateResultToServiceError(result);
150
151 IPC::ResponseBuilder rb{ctx, 2};
152 rb.Push(result);
153}
154
155void NfcInterface::StopDetection(HLERequestContext& ctx) {
156 IPC::RequestParser rp{ctx};
157 const auto device_handle{rp.Pop<u64>()};
158 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
159
160 auto result = GetManager()->StopDetection(device_handle);
161 result = TranslateResultToServiceError(result);
162
163 IPC::ResponseBuilder rb{ctx, 2};
164 rb.Push(result);
165}
166
167void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
168 IPC::RequestParser rp{ctx};
169 const auto device_handle{rp.Pop<u64>()};
170 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
171
172 TagInfo tag_info{};
173 auto result =
174 GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare);
175 result = TranslateResultToServiceError(result);
176
177 if (result.IsSuccess()) {
178 ctx.WriteBuffer(tag_info);
179 }
180
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(result);
183}
184
185void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
186 IPC::RequestParser rp{ctx};
187 const auto device_handle{rp.Pop<u64>()};
188 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
189
190 IPC::ResponseBuilder rb{ctx, 2, 1};
191 rb.Push(ResultSuccess);
192 rb.PushCopyObjects(GetManager()->AttachActivateEvent(device_handle));
193}
194
195void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
196 IPC::RequestParser rp{ctx};
197 const auto device_handle{rp.Pop<u64>()};
198 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
199
200 IPC::ResponseBuilder rb{ctx, 2, 1};
201 rb.Push(ResultSuccess);
202 rb.PushCopyObjects(GetManager()->AttachDeactivateEvent(device_handle));
203}
204
205void NfcInterface::ReadMifare(HLERequestContext& ctx) {
206 IPC::RequestParser rp{ctx};
207 const auto device_handle{rp.Pop<u64>()};
208 const auto buffer{ctx.ReadBuffer()};
209 const auto number_of_commands{ctx.GetReadBufferNumElements<MifareReadBlockParameter>()};
210 std::vector<MifareReadBlockParameter> read_commands(number_of_commands);
211
212 memcpy(read_commands.data(), buffer.data(),
213 number_of_commands * sizeof(MifareReadBlockParameter));
214
215 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
216 device_handle, number_of_commands);
217
218 std::vector<MifareReadBlockData> out_data(number_of_commands);
219 auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
220 result = TranslateResultToServiceError(result);
221
222 if (result.IsSuccess()) {
223 ctx.WriteBuffer(out_data);
224 }
225
226 IPC::ResponseBuilder rb{ctx, 2};
227 rb.Push(result);
228}
229
230void NfcInterface::WriteMifare(HLERequestContext& ctx) {
231 IPC::RequestParser rp{ctx};
232 const auto device_handle{rp.Pop<u64>()};
233 const auto buffer{ctx.ReadBuffer()};
234 const auto number_of_commands{ctx.GetReadBufferNumElements<MifareWriteBlockParameter>()};
235 std::vector<MifareWriteBlockParameter> write_commands(number_of_commands);
236
237 memcpy(write_commands.data(), buffer.data(),
238 number_of_commands * sizeof(MifareWriteBlockParameter));
239
240 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
241 device_handle, number_of_commands);
242
243 auto result = GetManager()->WriteMifare(device_handle, write_commands);
244 result = TranslateResultToServiceError(result);
245
246 IPC::ResponseBuilder rb{ctx, 2};
247 rb.Push(result);
248}
249
250void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
251 IPC::RequestParser rp{ctx};
252 const auto device_handle{rp.Pop<u64>()};
253 const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
254 const auto command_data{ctx.ReadBuffer()};
255 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
256 device_handle, timeout.ToSeconds(), command_data.size());
257
258 std::vector<u8> out_data(1);
259 auto result =
260 GetManager()->SendCommandByPassThrough(device_handle, timeout, command_data, out_data);
261 result = TranslateResultToServiceError(result);
262
263 if (result.IsError()) {
264 IPC::ResponseBuilder rb{ctx, 2};
265 rb.Push(result);
266 return;
267 }
268
269 ctx.WriteBuffer(out_data);
270
271 IPC::ResponseBuilder rb{ctx, 3};
272 rb.Push(ResultSuccess);
273 rb.Push(static_cast<u32>(out_data.size()));
274}
275
276std::shared_ptr<DeviceManager> NfcInterface::GetManager() {
277 if (device_manager == nullptr) {
278 device_manager = std::make_shared<DeviceManager>(system, service_context);
279 }
280 return device_manager;
281}
282
283BackendType NfcInterface::GetBackendType() const {
284 return backend_type;
285}
286
287Result NfcInterface::TranslateResultToServiceError(Result result) const {
288 const auto backend = GetBackendType();
289
290 if (result.IsSuccess()) {
291 return result;
292 }
293
294 if (result.module != ErrorModule::NFC) {
295 return result;
296 }
297
298 switch (backend) {
299 case BackendType::Mifare:
300 return TranslateResultToNfp(result);
301 case BackendType::Nfp: {
302 return TranslateResultToNfp(result);
303 }
304 default:
305 if (result != ResultUnknown216) {
306 return result;
307 }
308 return ResultUnknown74;
309 }
310}
311
312Result NfcInterface::TranslateResultToNfp(Result result) const {
313 if (result == ResultDeviceNotFound) {
314 return NFP::ResultDeviceNotFound;
315 }
316 if (result == ResultInvalidArgument) {
317 return NFP::ResultInvalidArgument;
318 }
319 if (result == ResultWrongApplicationAreaSize) {
320 return NFP::ResultWrongApplicationAreaSize;
321 }
322 if (result == ResultWrongDeviceState) {
323 return NFP::ResultWrongDeviceState;
324 }
325 if (result == ResultUnknown74) {
326 return NFP::ResultUnknown74;
327 }
328 if (result == ResultNfcDisabled) {
329 return NFP::ResultNfcDisabled;
330 }
331 if (result == ResultNfcNotInitialized) {
332 return NFP::ResultNfcDisabled;
333 }
334 if (result == ResultWriteAmiiboFailed) {
335 return NFP::ResultWriteAmiiboFailed;
336 }
337 if (result == ResultTagRemoved) {
338 return NFP::ResultTagRemoved;
339 }
340 if (result == ResultRegistrationIsNotInitialized) {
341 return NFP::ResultRegistrationIsNotInitialized;
342 }
343 if (result == ResultApplicationAreaIsNotInitialized) {
344 return NFP::ResultApplicationAreaIsNotInitialized;
345 }
346 if (result == ResultCorruptedData) {
347 return NFP::ResultCorruptedData;
348 }
349 if (result == ResultWrongApplicationAreaId) {
350 return NFP::ResultWrongApplicationAreaId;
351 }
352 if (result == ResultApplicationAreaExist) {
353 return NFP::ResultApplicationAreaExist;
354 }
355 if (result == ResultNotAnAmiibo) {
356 return NFP::ResultNotAnAmiibo;
357 }
358 LOG_WARNING(Service_NFC, "Result conversion not handled");
359 return result;
360}
361
362Result NfcInterface::TranslateResultToMifare(Result result) const {
363 if (result == ResultDeviceNotFound) {
364 return Mifare::ResultDeviceNotFound;
365 }
366 if (result == ResultInvalidArgument) {
367 return Mifare::ResultInvalidArgument;
368 }
369 if (result == ResultWrongDeviceState) {
370 return Mifare::ResultWrongDeviceState;
371 }
372 if (result == ResultNfcDisabled) {
373 return Mifare::ResultNfcDisabled;
374 }
375 if (result == ResultTagRemoved) {
376 return Mifare::ResultTagRemoved;
377 }
378 LOG_WARNING(Service_NFC, "Result conversion not handled");
379 return result;
380}
381
382} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_user.h b/src/core/hle/service/nfc/nfc_interface.h
index aee046ae8..08be174d8 100644
--- a/src/core/hle/service/nfc/nfc_user.h
+++ b/src/core/hle/service/nfc/nfc_interface.h
@@ -3,26 +3,17 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <array>
7#include <memory>
8#include <optional>
9
10#include "core/hle/service/kernel_helpers.h" 6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/nfc/nfc_types.h"
11#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
12 9
13namespace Service::NFC { 10namespace Service::NFC {
14class NfcDevice; 11class DeviceManager;
15 12
16class IUser final : public ServiceFramework<IUser> { 13class NfcInterface : public ServiceFramework<NfcInterface> {
17public: 14public:
18 explicit IUser(Core::System& system_); 15 explicit NfcInterface(Core::System& system_, const char* name, BackendType service_backend);
19 ~IUser(); 16 ~NfcInterface();
20
21private:
22 enum class State : u32 {
23 NonInitialized,
24 Initialized,
25 };
26 17
27 void Initialize(HLERequestContext& ctx); 18 void Initialize(HLERequestContext& ctx);
28 void Finalize(HLERequestContext& ctx); 19 void Finalize(HLERequestContext& ctx);
@@ -37,16 +28,22 @@ private:
37 void GetTagInfo(HLERequestContext& ctx); 28 void GetTagInfo(HLERequestContext& ctx);
38 void AttachActivateEvent(HLERequestContext& ctx); 29 void AttachActivateEvent(HLERequestContext& ctx);
39 void AttachDeactivateEvent(HLERequestContext& ctx); 30 void AttachDeactivateEvent(HLERequestContext& ctx);
31 void ReadMifare(HLERequestContext& ctx);
32 void WriteMifare(HLERequestContext& ctx);
40 void SendCommandByPassThrough(HLERequestContext& ctx); 33 void SendCommandByPassThrough(HLERequestContext& ctx);
41 34
42 std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle); 35protected:
36 std::shared_ptr<DeviceManager> GetManager();
37 BackendType GetBackendType() const;
38 Result TranslateResultToServiceError(Result result) const;
39 Result TranslateResultToNfp(Result result) const;
40 Result TranslateResultToMifare(Result result) const;
43 41
44 KernelHelpers::ServiceContext service_context; 42 KernelHelpers::ServiceContext service_context;
45 43
46 std::array<std::shared_ptr<NfcDevice>, 10> devices{}; 44 BackendType backend_type;
47
48 State state{State::NonInitialized}; 45 State state{State::NonInitialized};
49 Kernel::KEvent* availability_change_event; 46 std::shared_ptr<DeviceManager> device_manager = nullptr;
50}; 47};
51 48
52} // namespace Service::NFC 49} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h
index 146b8ba61..917d79ef8 100644
--- a/src/core/hle/service/nfc/nfc_result.h
+++ b/src/core/hle/service/nfc/nfc_result.h
@@ -1,5 +1,5 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
@@ -7,17 +7,22 @@
7 7
8namespace Service::NFC { 8namespace Service::NFC {
9 9
10constexpr Result DeviceNotFound(ErrorModule::NFC, 64); 10constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
11constexpr Result InvalidArgument(ErrorModule::NFC, 65); 11constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
12constexpr Result WrongDeviceState(ErrorModule::NFC, 73); 12constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
13constexpr Result NfcDisabled(ErrorModule::NFC, 80); 13constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
14constexpr Result TagRemoved(ErrorModule::NFC, 97); 14constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
15 15constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
16constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64); 16constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
17constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65); 17constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
18constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73); 18constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
19constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80); 19constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
20constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97); 20constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
21constexpr Result MifareReadError(ErrorModule::NFCMifare, 288); 21constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
22constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
23constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
24constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
25constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
26constexpr Result ResultUnknown216(ErrorModule::NFC, 216);
22 27
23} // namespace Service::NFC 28} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_types.h b/src/core/hle/service/nfc/nfc_types.h
new file mode 100644
index 000000000..c7ebd1fdb
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc_types.h
@@ -0,0 +1,90 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11namespace Service::NFC {
12enum class BackendType : u32 {
13 None,
14 Nfc,
15 Nfp,
16 Mifare,
17};
18
19// This is nn::nfc::DeviceState
20enum class DeviceState : u32 {
21 Initialized,
22 SearchingForTag,
23 TagFound,
24 TagRemoved,
25 TagMounted,
26 Unavailable,
27 Finalized,
28};
29
30// This is nn::nfc::State
31enum class State : u32 {
32 NonInitialized,
33 Initialized,
34};
35
36// This is nn::nfc::TagType
37enum class TagType : u32 {
38 None,
39 Type1, // ISO14443A RW 96-2k bytes 106kbit/s
40 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
41 Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
42 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
43 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
44};
45
46enum class PackedTagType : u8 {
47 None,
48 Type1, // ISO14443A RW 96-2k bytes 106kbit/s
49 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
50 Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
51 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
52 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
53};
54
55// This is nn::nfc::NfcProtocol
56// Verify this enum. It might be completely wrong default protocol is 0x48
57enum class NfcProtocol : u32 {
58 None,
59 TypeA = 1U << 0, // ISO14443A
60 TypeB = 1U << 1, // ISO14443B
61 TypeF = 1U << 2, // Sony FeliCa
62 Unknown1 = 1U << 3,
63 Unknown2 = 1U << 5,
64 All = 0xFFFFFFFFU,
65};
66
67// this is nn::nfc::TestWaveType
68enum class TestWaveType : u32 {
69 Unknown,
70};
71
72using UniqueSerialNumber = std::array<u8, 7>;
73using UniqueSerialNumberExtension = std::array<u8, 3>;
74
75// This is nn::nfc::DeviceHandle
76using DeviceHandle = u64;
77
78// This is nn::nfc::TagInfo
79struct TagInfo {
80 UniqueSerialNumber uuid;
81 UniqueSerialNumberExtension uuid_extension;
82 u8 uuid_length;
83 INSERT_PADDING_BYTES(0x15);
84 NfcProtocol protocol;
85 TagType tag_type;
86 INSERT_PADDING_BYTES(0x30);
87};
88static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
89
90} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp
deleted file mode 100644
index 7c162a4f3..000000000
--- a/src/core/hle/service/nfc/nfc_user.cpp
+++ /dev/null
@@ -1,365 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hid/hid_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nfc/nfc_device.h"
10#include "core/hle/service/nfc/nfc_result.h"
11#include "core/hle/service/nfc/nfc_user.h"
12#include "core/hle/service/time/clock_types.h"
13
14namespace Service::NFC {
15
16IUser::IUser(Core::System& system_)
17 : ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} {
18 static const FunctionInfo functions[] = {
19 {0, &IUser::Initialize, "InitializeOld"},
20 {1, &IUser::Finalize, "FinalizeOld"},
21 {2, &IUser::GetState, "GetStateOld"},
22 {3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"},
23 {400, &IUser::Initialize, "Initialize"},
24 {401, &IUser::Finalize, "Finalize"},
25 {402, &IUser::GetState, "GetState"},
26 {403, &IUser::IsNfcEnabled, "IsNfcEnabled"},
27 {404, &IUser::ListDevices, "ListDevices"},
28 {405, &IUser::GetDeviceState, "GetDeviceState"},
29 {406, &IUser::GetNpadId, "GetNpadId"},
30 {407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
31 {408, &IUser::StartDetection, "StartDetection"},
32 {409, &IUser::StopDetection, "StopDetection"},
33 {410, &IUser::GetTagInfo, "GetTagInfo"},
34 {411, &IUser::AttachActivateEvent, "AttachActivateEvent"},
35 {412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
36 {1000, nullptr, "ReadMifare"},
37 {1001, nullptr, "WriteMifare"},
38 {1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"},
39 {1301, nullptr, "KeepPassThroughSession"},
40 {1302, nullptr, "ReleasePassThroughSession"},
41 };
42 RegisterHandlers(functions);
43
44 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
45
46 for (u32 device_index = 0; device_index < 10; device_index++) {
47 devices[device_index] =
48 std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
49 service_context, availability_change_event);
50 }
51}
52
53IUser ::~IUser() {
54 availability_change_event->Close();
55}
56
57void IUser::Initialize(HLERequestContext& ctx) {
58 LOG_INFO(Service_NFC, "called");
59
60 state = State::Initialized;
61
62 for (auto& device : devices) {
63 device->Initialize();
64 }
65
66 IPC::ResponseBuilder rb{ctx, 2, 0};
67 rb.Push(ResultSuccess);
68}
69
70void IUser::Finalize(HLERequestContext& ctx) {
71 LOG_INFO(Service_NFC, "called");
72
73 state = State::NonInitialized;
74
75 for (auto& device : devices) {
76 device->Finalize();
77 }
78
79 IPC::ResponseBuilder rb{ctx, 2};
80 rb.Push(ResultSuccess);
81}
82
83void IUser::GetState(HLERequestContext& ctx) {
84 LOG_DEBUG(Service_NFC, "called");
85
86 IPC::ResponseBuilder rb{ctx, 3};
87 rb.Push(ResultSuccess);
88 rb.PushEnum(state);
89}
90
91void IUser::IsNfcEnabled(HLERequestContext& ctx) {
92 LOG_DEBUG(Service_NFC, "called");
93
94 IPC::ResponseBuilder rb{ctx, 3};
95 rb.Push(ResultSuccess);
96 rb.Push(state != State::NonInitialized);
97}
98
99void IUser::ListDevices(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_NFC, "called");
101
102 if (state == State::NonInitialized) {
103 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(NfcDisabled);
105 return;
106 }
107
108 if (!ctx.CanWriteBuffer()) {
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(InvalidArgument);
111 return;
112 }
113
114 if (ctx.GetWriteBufferSize() == 0) {
115 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(InvalidArgument);
117 return;
118 }
119
120 std::vector<u64> nfp_devices;
121 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
122
123 for (auto& device : devices) {
124 if (nfp_devices.size() >= max_allowed_devices) {
125 continue;
126 }
127 if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
128 nfp_devices.push_back(device->GetHandle());
129 }
130 }
131
132 if (nfp_devices.empty()) {
133 IPC::ResponseBuilder rb{ctx, 2};
134 rb.Push(DeviceNotFound);
135 return;
136 }
137
138 ctx.WriteBuffer(nfp_devices);
139
140 IPC::ResponseBuilder rb{ctx, 3};
141 rb.Push(ResultSuccess);
142 rb.Push(static_cast<s32>(nfp_devices.size()));
143}
144
145void IUser::GetDeviceState(HLERequestContext& ctx) {
146 IPC::RequestParser rp{ctx};
147 const auto device_handle{rp.Pop<u64>()};
148 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
149
150 auto device = GetNfcDevice(device_handle);
151
152 if (!device.has_value()) {
153 IPC::ResponseBuilder rb{ctx, 2};
154 rb.Push(DeviceNotFound);
155 return;
156 }
157
158 IPC::ResponseBuilder rb{ctx, 3};
159 rb.Push(ResultSuccess);
160 rb.PushEnum(device.value()->GetCurrentState());
161}
162
163void IUser::GetNpadId(HLERequestContext& ctx) {
164 IPC::RequestParser rp{ctx};
165 const auto device_handle{rp.Pop<u64>()};
166 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
167
168 if (state == State::NonInitialized) {
169 IPC::ResponseBuilder rb{ctx, 2};
170 rb.Push(NfcDisabled);
171 return;
172 }
173
174 auto device = GetNfcDevice(device_handle);
175
176 if (!device.has_value()) {
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(DeviceNotFound);
179 return;
180 }
181
182 IPC::ResponseBuilder rb{ctx, 3};
183 rb.Push(ResultSuccess);
184 rb.PushEnum(device.value()->GetNpadId());
185}
186
187void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
188 LOG_INFO(Service_NFC, "called");
189
190 if (state == State::NonInitialized) {
191 IPC::ResponseBuilder rb{ctx, 2};
192 rb.Push(NfcDisabled);
193 return;
194 }
195
196 IPC::ResponseBuilder rb{ctx, 2, 1};
197 rb.Push(ResultSuccess);
198 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
199}
200
201void IUser::StartDetection(HLERequestContext& ctx) {
202 IPC::RequestParser rp{ctx};
203 const auto device_handle{rp.Pop<u64>()};
204 const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()};
205 LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
206
207 if (state == State::NonInitialized) {
208 IPC::ResponseBuilder rb{ctx, 2};
209 rb.Push(NfcDisabled);
210 return;
211 }
212
213 auto device = GetNfcDevice(device_handle);
214
215 if (!device.has_value()) {
216 IPC::ResponseBuilder rb{ctx, 2};
217 rb.Push(DeviceNotFound);
218 return;
219 }
220
221 const auto result = device.value()->StartDetection(nfp_protocol);
222 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(result);
224}
225
226void IUser::StopDetection(HLERequestContext& ctx) {
227 IPC::RequestParser rp{ctx};
228 const auto device_handle{rp.Pop<u64>()};
229 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
230
231 if (state == State::NonInitialized) {
232 IPC::ResponseBuilder rb{ctx, 2};
233 rb.Push(NfcDisabled);
234 return;
235 }
236
237 auto device = GetNfcDevice(device_handle);
238
239 if (!device.has_value()) {
240 IPC::ResponseBuilder rb{ctx, 2};
241 rb.Push(DeviceNotFound);
242 return;
243 }
244
245 const auto result = device.value()->StopDetection();
246 IPC::ResponseBuilder rb{ctx, 2};
247 rb.Push(result);
248}
249
250void IUser::GetTagInfo(HLERequestContext& ctx) {
251 IPC::RequestParser rp{ctx};
252 const auto device_handle{rp.Pop<u64>()};
253 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
254
255 if (state == State::NonInitialized) {
256 IPC::ResponseBuilder rb{ctx, 2};
257 rb.Push(NfcDisabled);
258 return;
259 }
260
261 auto device = GetNfcDevice(device_handle);
262
263 if (!device.has_value()) {
264 IPC::ResponseBuilder rb{ctx, 2};
265 rb.Push(DeviceNotFound);
266 return;
267 }
268
269 NFP::TagInfo tag_info{};
270 const auto result = device.value()->GetTagInfo(tag_info, false);
271 ctx.WriteBuffer(tag_info);
272 IPC::ResponseBuilder rb{ctx, 2};
273 rb.Push(result);
274}
275
276void IUser::AttachActivateEvent(HLERequestContext& ctx) {
277 IPC::RequestParser rp{ctx};
278 const auto device_handle{rp.Pop<u64>()};
279 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
280
281 if (state == State::NonInitialized) {
282 IPC::ResponseBuilder rb{ctx, 2};
283 rb.Push(NfcDisabled);
284 return;
285 }
286
287 auto device = GetNfcDevice(device_handle);
288
289 if (!device.has_value()) {
290 IPC::ResponseBuilder rb{ctx, 2};
291 rb.Push(DeviceNotFound);
292 return;
293 }
294
295 IPC::ResponseBuilder rb{ctx, 2, 1};
296 rb.Push(ResultSuccess);
297 rb.PushCopyObjects(device.value()->GetActivateEvent());
298}
299
300void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
301 IPC::RequestParser rp{ctx};
302 const auto device_handle{rp.Pop<u64>()};
303 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
304
305 if (state == State::NonInitialized) {
306 IPC::ResponseBuilder rb{ctx, 2};
307 rb.Push(NfcDisabled);
308 return;
309 }
310
311 auto device = GetNfcDevice(device_handle);
312
313 if (!device.has_value()) {
314 IPC::ResponseBuilder rb{ctx, 2};
315 rb.Push(DeviceNotFound);
316 return;
317 }
318
319 IPC::ResponseBuilder rb{ctx, 2, 1};
320 rb.Push(ResultSuccess);
321 rb.PushCopyObjects(device.value()->GetDeactivateEvent());
322}
323
324void IUser::SendCommandByPassThrough(HLERequestContext& ctx) {
325 IPC::RequestParser rp{ctx};
326 const auto device_handle{rp.Pop<u64>()};
327 const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
328 const auto command_data{ctx.ReadBuffer()};
329
330 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
331 device_handle, timeout.ToSeconds(), command_data.size());
332
333 if (state == State::NonInitialized) {
334 IPC::ResponseBuilder rb{ctx, 2};
335 rb.Push(NfcDisabled);
336 return;
337 }
338
339 auto device = GetNfcDevice(device_handle);
340
341 if (!device.has_value()) {
342 IPC::ResponseBuilder rb{ctx, 2};
343 rb.Push(DeviceNotFound);
344 return;
345 }
346
347 std::vector<u8> out_data(1);
348 // TODO: Request data from nfc device
349 ctx.WriteBuffer(out_data);
350
351 IPC::ResponseBuilder rb{ctx, 3};
352 rb.Push(ResultSuccess);
353 rb.Push(static_cast<u32>(out_data.size()));
354}
355
356std::optional<std::shared_ptr<NfcDevice>> IUser::GetNfcDevice(u64 handle) {
357 for (auto& device : devices) {
358 if (device->GetHandle() == handle) {
359 return device;
360 }
361 }
362 return std::nullopt;
363}
364
365} // namespace Service::NFC
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 2714f4bea..2eeabc138 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -13,7 +13,7 @@ class IUser final : public Interface {
13public: 13public:
14 explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") { 14 explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfoTyped<IUser> functions[] = {
17 {0, &IUser::Initialize, "Initialize"}, 17 {0, &IUser::Initialize, "Initialize"},
18 {1, &IUser::Finalize, "Finalize"}, 18 {1, &IUser::Finalize, "Finalize"},
19 {2, &IUser::ListDevices, "ListDevices"}, 19 {2, &IUser::ListDevices, "ListDevices"},
@@ -50,7 +50,7 @@ class ISystem final : public Interface {
50public: 50public:
51 explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") { 51 explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
52 // clang-format off 52 // clang-format off
53 static const FunctionInfo functions[] = { 53 static const FunctionInfoTyped<ISystem> functions[] = {
54 {0, &ISystem::InitializeSystem, "InitializeSystem"}, 54 {0, &ISystem::InitializeSystem, "InitializeSystem"},
55 {1, &ISystem::FinalizeSystem, "FinalizeSystem"}, 55 {1, &ISystem::FinalizeSystem, "FinalizeSystem"},
56 {2, &ISystem::ListDevices, "ListDevices"}, 56 {2, &ISystem::ListDevices, "ListDevices"},
@@ -89,7 +89,7 @@ class IDebug final : public Interface {
89public: 89public:
90 explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") { 90 explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
91 // clang-format off 91 // clang-format off
92 static const FunctionInfo functions[] = { 92 static const FunctionInfoTyped<IDebug> functions[] = {
93 {0, &IDebug::InitializeDebug, "InitializeDebug"}, 93 {0, &IDebug::InitializeDebug, "InitializeDebug"},
94 {1, &IDebug::FinalizeDebug, "FinalizeDebug"}, 94 {1, &IDebug::FinalizeDebug, "FinalizeDebug"},
95 {2, &IDebug::ListDevices, "ListDevices"}, 95 {2, &IDebug::ListDevices, "ListDevices"},
@@ -126,9 +126,9 @@ public:
126 {201, &IDebug::SetAll, "SetAll"}, 126 {201, &IDebug::SetAll, "SetAll"},
127 {202, &IDebug::FlushDebug, "FlushDebug"}, 127 {202, &IDebug::FlushDebug, "FlushDebug"},
128 {203, &IDebug::BreakTag, "BreakTag"}, 128 {203, &IDebug::BreakTag, "BreakTag"},
129 {204, nullptr, "ReadBackupData"}, 129 {204, &IDebug::ReadBackupData, "ReadBackupData"},
130 {205, nullptr, "WriteBackupData"}, 130 {205, &IDebug::WriteBackupData, "WriteBackupData"},
131 {206, nullptr, "WriteNtf"}, 131 {206, &IDebug::WriteNtf, "WriteNtf"},
132 }; 132 };
133 // clang-format on 133 // clang-format on
134 134
@@ -152,16 +152,10 @@ private:
152 void CreateUserInterface(HLERequestContext& ctx) { 152 void CreateUserInterface(HLERequestContext& ctx) {
153 LOG_DEBUG(Service_NFP, "called"); 153 LOG_DEBUG(Service_NFP, "called");
154 154
155 if (user_interface == nullptr) {
156 user_interface = std::make_shared<IUser>(system);
157 }
158
159 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
160 rb.Push(ResultSuccess); 156 rb.Push(ResultSuccess);
161 rb.PushIpcInterface<IUser>(user_interface); 157 rb.PushIpcInterface<IUser>(system);
162 } 158 }
163
164 std::shared_ptr<IUser> user_interface;
165}; 159};
166 160
167class ISystemManager final : public ServiceFramework<ISystemManager> { 161class ISystemManager final : public ServiceFramework<ISystemManager> {
@@ -180,16 +174,10 @@ private:
180 void CreateSystemInterface(HLERequestContext& ctx) { 174 void CreateSystemInterface(HLERequestContext& ctx) {
181 LOG_DEBUG(Service_NFP, "called"); 175 LOG_DEBUG(Service_NFP, "called");
182 176
183 if (system_interface == nullptr) {
184 system_interface = std::make_shared<ISystem>(system);
185 }
186
187 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 177 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
188 rb.Push(ResultSuccess); 178 rb.Push(ResultSuccess);
189 rb.PushIpcInterface<ISystem>(system_interface); 179 rb.PushIpcInterface<ISystem>(system);
190 } 180 }
191
192 std::shared_ptr<ISystem> system_interface;
193}; 181};
194 182
195class IDebugManager final : public ServiceFramework<IDebugManager> { 183class IDebugManager final : public ServiceFramework<IDebugManager> {
@@ -208,16 +196,10 @@ private:
208 void CreateDebugInterface(HLERequestContext& ctx) { 196 void CreateDebugInterface(HLERequestContext& ctx) {
209 LOG_DEBUG(Service_NFP, "called"); 197 LOG_DEBUG(Service_NFP, "called");
210 198
211 if (system_interface == nullptr) {
212 system_interface = std::make_shared<IDebug>(system);
213 }
214
215 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 199 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
216 rb.Push(ResultSuccess); 200 rb.Push(ResultSuccess);
217 rb.PushIpcInterface<IDebug>(system_interface); 201 rb.PushIpcInterface<IDebug>(system);
218 } 202 }
219
220 std::shared_ptr<IDebug> system_interface;
221}; 203};
222 204
223void LoopProcess(Core::System& system) { 205void LoopProcess(Core::System& system) {
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
deleted file mode 100644
index bab05538a..000000000
--- a/src/core/hle/service/nfp/nfp_device.h
+++ /dev/null
@@ -1,120 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <vector>
8
9#include "common/common_types.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/nfp/nfp_types.h"
12#include "core/hle/service/service.h"
13
14namespace Kernel {
15class KEvent;
16class KReadableEvent;
17} // namespace Kernel
18
19namespace Core {
20class System;
21} // namespace Core
22
23namespace Core::HID {
24class EmulatedController;
25enum class ControllerTriggerType;
26enum class NpadIdType : u32;
27} // namespace Core::HID
28
29namespace Service::NFP {
30class NfpDevice {
31public:
32 NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
33 KernelHelpers::ServiceContext& service_context_,
34 Kernel::KEvent* availability_change_event_);
35 ~NfpDevice();
36
37 void Initialize();
38 void Finalize();
39
40 Result StartDetection(TagProtocol allowed_protocol);
41 Result StopDetection();
42 Result Mount(MountTarget mount_target);
43 Result Unmount();
44
45 Result Flush();
46 Result FlushDebug();
47 Result FlushWithBreak(BreakType break_type);
48
49 Result GetTagInfo(TagInfo& tag_info) const;
50 Result GetCommonInfo(CommonInfo& common_info) const;
51 Result GetModelInfo(ModelInfo& model_info) const;
52 Result GetRegisterInfo(RegisterInfo& register_info) const;
53 Result GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const;
54 Result GetAdminInfo(AdminInfo& admin_info) const;
55
56 Result DeleteRegisterInfo();
57 Result SetRegisterInfoPrivate(const AmiiboName& amiibo_name);
58 Result RestoreAmiibo();
59 Result Format();
60
61 Result OpenApplicationArea(u32 access_id);
62 Result GetApplicationAreaId(u32& application_area_id) const;
63 Result GetApplicationArea(std::vector<u8>& data) const;
64 Result SetApplicationArea(std::span<const u8> data);
65 Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
66 Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
67 Result DeleteApplicationArea();
68 Result ExistApplicationArea(bool& has_application_area);
69
70 Result GetAll(NfpData& data) const;
71 Result SetAll(const NfpData& data);
72 Result BreakTag(BreakType break_type);
73 Result ReadBackupData();
74 Result WriteBackupData();
75 Result WriteNtf();
76
77 u64 GetHandle() const;
78 u32 GetApplicationAreaSize() const;
79 DeviceState GetCurrentState() const;
80 Core::HID::NpadIdType GetNpadId() const;
81
82 Kernel::KReadableEvent& GetActivateEvent() const;
83 Kernel::KReadableEvent& GetDeactivateEvent() const;
84
85private:
86 void NpadUpdate(Core::HID::ControllerTriggerType type);
87 bool LoadAmiibo(std::span<const u8> data);
88 void CloseAmiibo();
89
90 AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
91 void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
92 AmiiboDate GetAmiiboDate(s64 posix_time) const;
93 u64 RemoveVersionByte(u64 application_id) const;
94 void UpdateSettingsCrc();
95 void UpdateRegisterInfoCrc();
96
97 bool is_controller_set{};
98 int callback_key;
99 const Core::HID::NpadIdType npad_id;
100 Core::System& system;
101 Core::HID::EmulatedController* npad_device = nullptr;
102 KernelHelpers::ServiceContext& service_context;
103 Kernel::KEvent* activate_event = nullptr;
104 Kernel::KEvent* deactivate_event = nullptr;
105 Kernel::KEvent* availability_change_event = nullptr;
106
107 bool is_initalized{};
108 bool is_data_moddified{};
109 bool is_app_area_open{};
110 bool is_plain_amiibo{};
111 TagProtocol allowed_protocols{};
112 s64 current_posix_time{};
113 MountTarget mount_target{MountTarget::None};
114 DeviceState device_state{DeviceState::Unavailable};
115
116 NTAG215File tag_data{};
117 EncryptedNTAG215File encrypted_tag_data{};
118};
119
120} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp
index 2ed8bb1ba..21d159154 100644
--- a/src/core/hle/service/nfp/nfp_interface.cpp
+++ b/src/core/hle/service/nfp/nfp_interface.cpp
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 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 "common/logging/log.h" 4#include "common/logging/log.h"
@@ -6,198 +6,34 @@
6#include "core/hid/hid_types.h" 6#include "core/hid/hid_types.h"
7#include "core/hle/kernel/k_event.h" 7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nfp/nfp_device.h" 9#include "core/hle/service/nfc/common/device.h"
10#include "core/hle/service/nfc/common/device_manager.h"
11#include "core/hle/service/nfc/nfc_types.h"
10#include "core/hle/service/nfp/nfp_interface.h" 12#include "core/hle/service/nfp/nfp_interface.h"
11#include "core/hle/service/nfp/nfp_result.h" 13#include "core/hle/service/nfp/nfp_result.h"
14#include "core/hle/service/nfp/nfp_types.h"
12 15
13namespace Service::NFP { 16namespace Service::NFP {
14 17
15Interface::Interface(Core::System& system_, const char* name) 18Interface::Interface(Core::System& system_, const char* name)
16 : ServiceFramework{system_, name}, service_context{system_, service_name} { 19 : NfcInterface{system_, name, NFC::BackendType::Nfp} {}
17 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
18 20
19 for (u32 device_index = 0; device_index < 10; device_index++) { 21Interface::~Interface() = default;
20 devices[device_index] =
21 std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system,
22 service_context, availability_change_event);
23 }
24}
25
26Interface::~Interface() {
27 availability_change_event->Close();
28}
29
30void Interface::Initialize(HLERequestContext& ctx) {
31 LOG_INFO(Service_NFP, "called");
32
33 state = State::Initialized;
34
35 for (auto& device : devices) {
36 device->Initialize();
37 }
38
39 IPC::ResponseBuilder rb{ctx, 2};
40 rb.Push(ResultSuccess);
41}
42 22
43void Interface::InitializeSystem(HLERequestContext& ctx) { 23void Interface::InitializeSystem(HLERequestContext& ctx) {
44 LOG_INFO(Service_NFP, "called"); 24 Initialize(ctx);
45
46 state = State::Initialized;
47
48 for (auto& device : devices) {
49 device->Initialize();
50 }
51
52 IPC::ResponseBuilder rb{ctx, 2};
53 rb.Push(ResultSuccess);
54} 25}
55 26
56void Interface::InitializeDebug(HLERequestContext& ctx) { 27void Interface::InitializeDebug(HLERequestContext& ctx) {
57 LOG_INFO(Service_NFP, "called"); 28 Initialize(ctx);
58
59 state = State::Initialized;
60
61 for (auto& device : devices) {
62 device->Initialize();
63 }
64
65 IPC::ResponseBuilder rb{ctx, 2};
66 rb.Push(ResultSuccess);
67}
68
69void Interface::Finalize(HLERequestContext& ctx) {
70 LOG_INFO(Service_NFP, "called");
71
72 state = State::NonInitialized;
73
74 for (auto& device : devices) {
75 device->Finalize();
76 }
77
78 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(ResultSuccess);
80} 29}
81 30
82void Interface::FinalizeSystem(HLERequestContext& ctx) { 31void Interface::FinalizeSystem(HLERequestContext& ctx) {
83 LOG_INFO(Service_NFP, "called"); 32 Finalize(ctx);
84
85 state = State::NonInitialized;
86
87 for (auto& device : devices) {
88 device->Finalize();
89 }
90
91 IPC::ResponseBuilder rb{ctx, 2};
92 rb.Push(ResultSuccess);
93} 33}
94 34
95void Interface::FinalizeDebug(HLERequestContext& ctx) { 35void Interface::FinalizeDebug(HLERequestContext& ctx) {
96 LOG_INFO(Service_NFP, "called"); 36 Finalize(ctx);
97
98 state = State::NonInitialized;
99
100 for (auto& device : devices) {
101 device->Finalize();
102 }
103
104 IPC::ResponseBuilder rb{ctx, 2};
105 rb.Push(ResultSuccess);
106}
107
108void Interface::ListDevices(HLERequestContext& ctx) {
109 LOG_DEBUG(Service_NFP, "called");
110
111 if (state == State::NonInitialized) {
112 IPC::ResponseBuilder rb{ctx, 2};
113 rb.Push(NfcDisabled);
114 return;
115 }
116
117 if (!ctx.CanWriteBuffer()) {
118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(InvalidArgument);
120 return;
121 }
122
123 if (ctx.GetWriteBufferSize() == 0) {
124 IPC::ResponseBuilder rb{ctx, 2};
125 rb.Push(InvalidArgument);
126 return;
127 }
128
129 std::vector<u64> nfp_devices;
130 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
131
132 for (const auto& device : devices) {
133 if (nfp_devices.size() >= max_allowed_devices) {
134 continue;
135 }
136 if (device->GetCurrentState() != DeviceState::Unavailable) {
137 nfp_devices.push_back(device->GetHandle());
138 }
139 }
140
141 if (nfp_devices.empty()) {
142 IPC::ResponseBuilder rb{ctx, 2};
143 rb.Push(DeviceNotFound);
144 return;
145 }
146
147 ctx.WriteBuffer(nfp_devices);
148
149 IPC::ResponseBuilder rb{ctx, 3};
150 rb.Push(ResultSuccess);
151 rb.Push(static_cast<s32>(nfp_devices.size()));
152}
153
154void Interface::StartDetection(HLERequestContext& ctx) {
155 IPC::RequestParser rp{ctx};
156 const auto device_handle{rp.Pop<u64>()};
157 const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
158 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
159
160 if (state == State::NonInitialized) {
161 IPC::ResponseBuilder rb{ctx, 2};
162 rb.Push(NfcDisabled);
163 return;
164 }
165
166 auto device = GetNfpDevice(device_handle);
167
168 if (!device.has_value()) {
169 IPC::ResponseBuilder rb{ctx, 2};
170 rb.Push(DeviceNotFound);
171 return;
172 }
173
174 const auto result = device.value()->StartDetection(nfp_protocol);
175 IPC::ResponseBuilder rb{ctx, 2};
176 rb.Push(result);
177}
178
179void Interface::StopDetection(HLERequestContext& ctx) {
180 IPC::RequestParser rp{ctx};
181 const auto device_handle{rp.Pop<u64>()};
182 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
183
184 if (state == State::NonInitialized) {
185 IPC::ResponseBuilder rb{ctx, 2};
186 rb.Push(NfcDisabled);
187 return;
188 }
189
190 auto device = GetNfpDevice(device_handle);
191
192 if (!device.has_value()) {
193 IPC::ResponseBuilder rb{ctx, 2};
194 rb.Push(DeviceNotFound);
195 return;
196 }
197
198 const auto result = device.value()->StopDetection();
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(result);
201} 37}
202 38
203void Interface::Mount(HLERequestContext& ctx) { 39void Interface::Mount(HLERequestContext& ctx) {
@@ -208,21 +44,9 @@ void Interface::Mount(HLERequestContext& ctx) {
208 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, 44 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
209 model_type, mount_target); 45 model_type, mount_target);
210 46
211 if (state == State::NonInitialized) { 47 auto result = GetManager()->Mount(device_handle, model_type, mount_target);
212 IPC::ResponseBuilder rb{ctx, 2}; 48 result = TranslateResultToServiceError(result);
213 rb.Push(NfcDisabled);
214 return;
215 }
216
217 auto device = GetNfpDevice(device_handle);
218
219 if (!device.has_value()) {
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(DeviceNotFound);
222 return;
223 }
224 49
225 const auto result = device.value()->Mount(mount_target);
226 IPC::ResponseBuilder rb{ctx, 2}; 50 IPC::ResponseBuilder rb{ctx, 2};
227 rb.Push(result); 51 rb.Push(result);
228} 52}
@@ -232,21 +56,9 @@ void Interface::Unmount(HLERequestContext& ctx) {
232 const auto device_handle{rp.Pop<u64>()}; 56 const auto device_handle{rp.Pop<u64>()};
233 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 57 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
234 58
235 if (state == State::NonInitialized) { 59 auto result = GetManager()->Unmount(device_handle);
236 IPC::ResponseBuilder rb{ctx, 2}; 60 result = TranslateResultToServiceError(result);
237 rb.Push(NfcDisabled);
238 return;
239 }
240
241 auto device = GetNfpDevice(device_handle);
242
243 if (!device.has_value()) {
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(DeviceNotFound);
246 return;
247 }
248 61
249 const auto result = device.value()->Unmount();
250 IPC::ResponseBuilder rb{ctx, 2}; 62 IPC::ResponseBuilder rb{ctx, 2};
251 rb.Push(result); 63 rb.Push(result);
252} 64}
@@ -257,21 +69,9 @@ void Interface::OpenApplicationArea(HLERequestContext& ctx) {
257 const auto access_id{rp.Pop<u32>()}; 69 const auto access_id{rp.Pop<u32>()};
258 LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); 70 LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id);
259 71
260 if (state == State::NonInitialized) { 72 auto result = GetManager()->OpenApplicationArea(device_handle, access_id);
261 IPC::ResponseBuilder rb{ctx, 2}; 73 result = TranslateResultToServiceError(result);
262 rb.Push(NfcDisabled);
263 return;
264 }
265
266 auto device = GetNfpDevice(device_handle);
267
268 if (!device.has_value()) {
269 IPC::ResponseBuilder rb{ctx, 2};
270 rb.Push(DeviceNotFound);
271 return;
272 }
273 74
274 const auto result = device.value()->OpenApplicationArea(access_id);
275 IPC::ResponseBuilder rb{ctx, 2}; 75 IPC::ResponseBuilder rb{ctx, 2};
276 rb.Push(result); 76 rb.Push(result);
277} 77}
@@ -282,28 +82,16 @@ void Interface::GetApplicationArea(HLERequestContext& ctx) {
282 const auto data_size = ctx.GetWriteBufferSize(); 82 const auto data_size = ctx.GetWriteBufferSize();
283 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 83 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
284 84
285 if (state == State::NonInitialized) { 85 std::vector<u8> data(data_size);
286 IPC::ResponseBuilder rb{ctx, 2}; 86 auto result = GetManager()->GetApplicationArea(device_handle, data);
287 rb.Push(NfcDisabled); 87 result = TranslateResultToServiceError(result);
288 return;
289 }
290
291 if (!ctx.CanWriteBuffer()) {
292 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(InvalidArgument);
294 return;
295 }
296
297 auto device = GetNfpDevice(device_handle);
298 88
299 if (!device.has_value()) { 89 if (result.IsError()) {
300 IPC::ResponseBuilder rb{ctx, 2}; 90 IPC::ResponseBuilder rb{ctx, 2};
301 rb.Push(DeviceNotFound); 91 rb.Push(result);
302 return; 92 return;
303 } 93 }
304 94
305 std::vector<u8> data(data_size);
306 const auto result = device.value()->GetApplicationArea(data);
307 ctx.WriteBuffer(data); 95 ctx.WriteBuffer(data);
308 IPC::ResponseBuilder rb{ctx, 3}; 96 IPC::ResponseBuilder rb{ctx, 3};
309 rb.Push(result); 97 rb.Push(result);
@@ -316,27 +104,9 @@ void Interface::SetApplicationArea(HLERequestContext& ctx) {
316 const auto data{ctx.ReadBuffer()}; 104 const auto data{ctx.ReadBuffer()};
317 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); 105 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size());
318 106
319 if (state == State::NonInitialized) { 107 auto result = GetManager()->SetApplicationArea(device_handle, data);
320 IPC::ResponseBuilder rb{ctx, 2}; 108 result = TranslateResultToServiceError(result);
321 rb.Push(NfcDisabled);
322 return;
323 }
324
325 if (!ctx.CanReadBuffer()) {
326 IPC::ResponseBuilder rb{ctx, 2};
327 rb.Push(InvalidArgument);
328 return;
329 }
330
331 auto device = GetNfpDevice(device_handle);
332
333 if (!device.has_value()) {
334 IPC::ResponseBuilder rb{ctx, 2};
335 rb.Push(DeviceNotFound);
336 return;
337 }
338 109
339 const auto result = device.value()->SetApplicationArea(data);
340 IPC::ResponseBuilder rb{ctx, 2}; 110 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(result); 111 rb.Push(result);
342} 112}
@@ -346,21 +116,9 @@ void Interface::Flush(HLERequestContext& ctx) {
346 const auto device_handle{rp.Pop<u64>()}; 116 const auto device_handle{rp.Pop<u64>()};
347 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 117 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
348 118
349 if (state == State::NonInitialized) { 119 auto result = GetManager()->Flush(device_handle);
350 IPC::ResponseBuilder rb{ctx, 2}; 120 result = TranslateResultToServiceError(result);
351 rb.Push(NfcDisabled);
352 return;
353 }
354
355 auto device = GetNfpDevice(device_handle);
356 121
357 if (!device.has_value()) {
358 IPC::ResponseBuilder rb{ctx, 2};
359 rb.Push(DeviceNotFound);
360 return;
361 }
362
363 const auto result = device.value()->Flush();
364 IPC::ResponseBuilder rb{ctx, 2}; 122 IPC::ResponseBuilder rb{ctx, 2};
365 rb.Push(result); 123 rb.Push(result);
366} 124}
@@ -370,21 +128,9 @@ void Interface::Restore(HLERequestContext& ctx) {
370 const auto device_handle{rp.Pop<u64>()}; 128 const auto device_handle{rp.Pop<u64>()};
371 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); 129 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
372 130
373 if (state == State::NonInitialized) { 131 auto result = GetManager()->Restore(device_handle);
374 IPC::ResponseBuilder rb{ctx, 2}; 132 result = TranslateResultToServiceError(result);
375 rb.Push(NfcDisabled);
376 return;
377 }
378 133
379 auto device = GetNfpDevice(device_handle);
380
381 if (!device.has_value()) {
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(DeviceNotFound);
384 return;
385 }
386
387 const auto result = device.value()->RestoreAmiibo();
388 IPC::ResponseBuilder rb{ctx, 2}; 134 IPC::ResponseBuilder rb{ctx, 2};
389 rb.Push(result); 135 rb.Push(result);
390} 136}
@@ -397,53 +143,9 @@ void Interface::CreateApplicationArea(HLERequestContext& ctx) {
397 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, 143 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
398 access_id, data.size()); 144 access_id, data.size());
399 145
400 if (state == State::NonInitialized) { 146 auto result = GetManager()->CreateApplicationArea(device_handle, access_id, data);
401 IPC::ResponseBuilder rb{ctx, 2}; 147 result = TranslateResultToServiceError(result);
402 rb.Push(NfcDisabled);
403 return;
404 }
405
406 if (!ctx.CanReadBuffer()) {
407 IPC::ResponseBuilder rb{ctx, 2};
408 rb.Push(InvalidArgument);
409 return;
410 }
411
412 auto device = GetNfpDevice(device_handle);
413
414 if (!device.has_value()) {
415 IPC::ResponseBuilder rb{ctx, 2};
416 rb.Push(DeviceNotFound);
417 return;
418 }
419
420 const auto result = device.value()->CreateApplicationArea(access_id, data);
421 IPC::ResponseBuilder rb{ctx, 2};
422 rb.Push(result);
423}
424
425void Interface::GetTagInfo(HLERequestContext& ctx) {
426 IPC::RequestParser rp{ctx};
427 const auto device_handle{rp.Pop<u64>()};
428 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
429
430 if (state == State::NonInitialized) {
431 IPC::ResponseBuilder rb{ctx, 2};
432 rb.Push(NfcDisabled);
433 return;
434 }
435
436 auto device = GetNfpDevice(device_handle);
437
438 if (!device.has_value()) {
439 IPC::ResponseBuilder rb{ctx, 2};
440 rb.Push(DeviceNotFound);
441 return;
442 }
443 148
444 TagInfo tag_info{};
445 const auto result = device.value()->GetTagInfo(tag_info);
446 ctx.WriteBuffer(tag_info);
447 IPC::ResponseBuilder rb{ctx, 2}; 149 IPC::ResponseBuilder rb{ctx, 2};
448 rb.Push(result); 150 rb.Push(result);
449} 151}
@@ -453,23 +155,14 @@ void Interface::GetRegisterInfo(HLERequestContext& ctx) {
453 const auto device_handle{rp.Pop<u64>()}; 155 const auto device_handle{rp.Pop<u64>()};
454 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 156 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
455 157
456 if (state == State::NonInitialized) { 158 RegisterInfo register_info{};
457 IPC::ResponseBuilder rb{ctx, 2}; 159 auto result = GetManager()->GetRegisterInfo(device_handle, register_info);
458 rb.Push(NfcDisabled); 160 result = TranslateResultToServiceError(result);
459 return;
460 }
461
462 auto device = GetNfpDevice(device_handle);
463 161
464 if (!device.has_value()) { 162 if (result.IsSuccess()) {
465 IPC::ResponseBuilder rb{ctx, 2}; 163 ctx.WriteBuffer(register_info);
466 rb.Push(DeviceNotFound);
467 return;
468 } 164 }
469 165
470 RegisterInfo register_info{};
471 const auto result = device.value()->GetRegisterInfo(register_info);
472 ctx.WriteBuffer(register_info);
473 IPC::ResponseBuilder rb{ctx, 2}; 166 IPC::ResponseBuilder rb{ctx, 2};
474 rb.Push(result); 167 rb.Push(result);
475} 168}
@@ -479,23 +172,14 @@ void Interface::GetCommonInfo(HLERequestContext& ctx) {
479 const auto device_handle{rp.Pop<u64>()}; 172 const auto device_handle{rp.Pop<u64>()};
480 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 173 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
481 174
482 if (state == State::NonInitialized) { 175 CommonInfo common_info{};
483 IPC::ResponseBuilder rb{ctx, 2}; 176 auto result = GetManager()->GetCommonInfo(device_handle, common_info);
484 rb.Push(NfcDisabled); 177 result = TranslateResultToServiceError(result);
485 return;
486 }
487
488 auto device = GetNfpDevice(device_handle);
489 178
490 if (!device.has_value()) { 179 if (result.IsSuccess()) {
491 IPC::ResponseBuilder rb{ctx, 2}; 180 ctx.WriteBuffer(common_info);
492 rb.Push(DeviceNotFound);
493 return;
494 } 181 }
495 182
496 CommonInfo common_info{};
497 const auto result = device.value()->GetCommonInfo(common_info);
498 ctx.WriteBuffer(common_info);
499 IPC::ResponseBuilder rb{ctx, 2}; 183 IPC::ResponseBuilder rb{ctx, 2};
500 rb.Push(result); 184 rb.Push(result);
501} 185}
@@ -505,155 +189,26 @@ void Interface::GetModelInfo(HLERequestContext& ctx) {
505 const auto device_handle{rp.Pop<u64>()}; 189 const auto device_handle{rp.Pop<u64>()};
506 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 190 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
507 191
508 if (state == State::NonInitialized) { 192 ModelInfo model_info{};
509 IPC::ResponseBuilder rb{ctx, 2}; 193 auto result = GetManager()->GetModelInfo(device_handle, model_info);
510 rb.Push(NfcDisabled); 194 result = TranslateResultToServiceError(result);
511 return;
512 }
513
514 auto device = GetNfpDevice(device_handle);
515 195
516 if (!device.has_value()) { 196 if (result.IsSuccess()) {
517 IPC::ResponseBuilder rb{ctx, 2}; 197 ctx.WriteBuffer(model_info);
518 rb.Push(DeviceNotFound);
519 return;
520 } 198 }
521 199
522 ModelInfo model_info{};
523 const auto result = device.value()->GetModelInfo(model_info);
524 ctx.WriteBuffer(model_info);
525 IPC::ResponseBuilder rb{ctx, 2}; 200 IPC::ResponseBuilder rb{ctx, 2};
526 rb.Push(result); 201 rb.Push(result);
527} 202}
528 203
529void Interface::AttachActivateEvent(HLERequestContext& ctx) {
530 IPC::RequestParser rp{ctx};
531 const auto device_handle{rp.Pop<u64>()};
532 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
533
534 if (state == State::NonInitialized) {
535 IPC::ResponseBuilder rb{ctx, 2};
536 rb.Push(NfcDisabled);
537 return;
538 }
539
540 auto device = GetNfpDevice(device_handle);
541
542 if (!device.has_value()) {
543 IPC::ResponseBuilder rb{ctx, 2};
544 rb.Push(DeviceNotFound);
545 return;
546 }
547
548 IPC::ResponseBuilder rb{ctx, 2, 1};
549 rb.Push(ResultSuccess);
550 rb.PushCopyObjects(device.value()->GetActivateEvent());
551}
552
553void Interface::AttachDeactivateEvent(HLERequestContext& ctx) {
554 IPC::RequestParser rp{ctx};
555 const auto device_handle{rp.Pop<u64>()};
556 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
557
558 if (state == State::NonInitialized) {
559 IPC::ResponseBuilder rb{ctx, 2};
560 rb.Push(NfcDisabled);
561 return;
562 }
563
564 auto device = GetNfpDevice(device_handle);
565
566 if (!device.has_value()) {
567 IPC::ResponseBuilder rb{ctx, 2};
568 rb.Push(DeviceNotFound);
569 return;
570 }
571
572 IPC::ResponseBuilder rb{ctx, 2, 1};
573 rb.Push(ResultSuccess);
574 rb.PushCopyObjects(device.value()->GetDeactivateEvent());
575}
576
577void Interface::GetState(HLERequestContext& ctx) {
578 LOG_DEBUG(Service_NFP, "called");
579
580 IPC::ResponseBuilder rb{ctx, 3};
581 rb.Push(ResultSuccess);
582 rb.PushEnum(state);
583}
584
585void Interface::GetDeviceState(HLERequestContext& ctx) {
586 IPC::RequestParser rp{ctx};
587 const auto device_handle{rp.Pop<u64>()};
588 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
589
590 auto device = GetNfpDevice(device_handle);
591
592 if (!device.has_value()) {
593 IPC::ResponseBuilder rb{ctx, 2};
594 rb.Push(DeviceNotFound);
595 return;
596 }
597
598 IPC::ResponseBuilder rb{ctx, 3};
599 rb.Push(ResultSuccess);
600 rb.PushEnum(device.value()->GetCurrentState());
601}
602
603void Interface::GetNpadId(HLERequestContext& ctx) {
604 IPC::RequestParser rp{ctx};
605 const auto device_handle{rp.Pop<u64>()};
606 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
607
608 if (state == State::NonInitialized) {
609 IPC::ResponseBuilder rb{ctx, 2};
610 rb.Push(NfcDisabled);
611 return;
612 }
613
614 auto device = GetNfpDevice(device_handle);
615
616 if (!device.has_value()) {
617 IPC::ResponseBuilder rb{ctx, 2};
618 rb.Push(DeviceNotFound);
619 return;
620 }
621
622 IPC::ResponseBuilder rb{ctx, 3};
623 rb.Push(ResultSuccess);
624 rb.PushEnum(device.value()->GetNpadId());
625}
626
627void Interface::GetApplicationAreaSize(HLERequestContext& ctx) { 204void Interface::GetApplicationAreaSize(HLERequestContext& ctx) {
628 IPC::RequestParser rp{ctx}; 205 IPC::RequestParser rp{ctx};
629 const auto device_handle{rp.Pop<u64>()}; 206 const auto device_handle{rp.Pop<u64>()};
630 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 207 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
631 208
632 auto device = GetNfpDevice(device_handle);
633
634 if (!device.has_value()) {
635 IPC::ResponseBuilder rb{ctx, 2};
636 rb.Push(DeviceNotFound);
637 return;
638 }
639
640 IPC::ResponseBuilder rb{ctx, 3}; 209 IPC::ResponseBuilder rb{ctx, 3};
641 rb.Push(ResultSuccess); 210 rb.Push(ResultSuccess);
642 rb.Push(device.value()->GetApplicationAreaSize()); 211 rb.Push(GetManager()->GetApplicationAreaSize());
643}
644
645void Interface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
646 LOG_INFO(Service_NFP, "called");
647
648 if (state == State::NonInitialized) {
649 IPC::ResponseBuilder rb{ctx, 2};
650 rb.Push(NfcDisabled);
651 return;
652 }
653
654 IPC::ResponseBuilder rb{ctx, 2, 1};
655 rb.Push(ResultSuccess);
656 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
657} 212}
658 213
659void Interface::RecreateApplicationArea(HLERequestContext& ctx) { 214void Interface::RecreateApplicationArea(HLERequestContext& ctx) {
@@ -664,21 +219,9 @@ void Interface::RecreateApplicationArea(HLERequestContext& ctx) {
664 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, 219 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
665 access_id, data.size()); 220 access_id, data.size());
666 221
667 if (state == State::NonInitialized) { 222 auto result = GetManager()->RecreateApplicationArea(device_handle, access_id, data);
668 IPC::ResponseBuilder rb{ctx, 2}; 223 result = TranslateResultToServiceError(result);
669 rb.Push(NfcDisabled);
670 return;
671 }
672 224
673 auto device = GetNfpDevice(device_handle);
674
675 if (!device.has_value()) {
676 IPC::ResponseBuilder rb{ctx, 2};
677 rb.Push(DeviceNotFound);
678 return;
679 }
680
681 const auto result = device.value()->RecreateApplicationArea(access_id, data);
682 IPC::ResponseBuilder rb{ctx, 2}; 225 IPC::ResponseBuilder rb{ctx, 2};
683 rb.Push(result); 226 rb.Push(result);
684} 227}
@@ -686,23 +229,11 @@ void Interface::RecreateApplicationArea(HLERequestContext& ctx) {
686void Interface::Format(HLERequestContext& ctx) { 229void Interface::Format(HLERequestContext& ctx) {
687 IPC::RequestParser rp{ctx}; 230 IPC::RequestParser rp{ctx};
688 const auto device_handle{rp.Pop<u64>()}; 231 const auto device_handle{rp.Pop<u64>()};
689 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 232 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
690
691 if (state == State::NonInitialized) {
692 IPC::ResponseBuilder rb{ctx, 2};
693 rb.Push(NfcDisabled);
694 return;
695 }
696 233
697 auto device = GetNfpDevice(device_handle); 234 auto result = GetManager()->Format(device_handle);
235 result = TranslateResultToServiceError(result);
698 236
699 if (!device.has_value()) {
700 IPC::ResponseBuilder rb{ctx, 2};
701 rb.Push(DeviceNotFound);
702 return;
703 }
704
705 const auto result = device.value()->Format();
706 IPC::ResponseBuilder rb{ctx, 2}; 237 IPC::ResponseBuilder rb{ctx, 2};
707 rb.Push(result); 238 rb.Push(result);
708} 239}
@@ -712,23 +243,14 @@ void Interface::GetAdminInfo(HLERequestContext& ctx) {
712 const auto device_handle{rp.Pop<u64>()}; 243 const auto device_handle{rp.Pop<u64>()};
713 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 244 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
714 245
715 if (state == State::NonInitialized) { 246 AdminInfo admin_info{};
716 IPC::ResponseBuilder rb{ctx, 2}; 247 auto result = GetManager()->GetAdminInfo(device_handle, admin_info);
717 rb.Push(NfcDisabled); 248 result = TranslateResultToServiceError(result);
718 return;
719 }
720
721 auto device = GetNfpDevice(device_handle);
722 249
723 if (!device.has_value()) { 250 if (result.IsSuccess()) {
724 IPC::ResponseBuilder rb{ctx, 2}; 251 ctx.WriteBuffer(admin_info);
725 rb.Push(DeviceNotFound);
726 return;
727 } 252 }
728 253
729 AdminInfo admin_info{};
730 const auto result = device.value()->GetAdminInfo(admin_info);
731 ctx.WriteBuffer(admin_info);
732 IPC::ResponseBuilder rb{ctx, 2}; 254 IPC::ResponseBuilder rb{ctx, 2};
733 rb.Push(result); 255 rb.Push(result);
734} 256}
@@ -738,23 +260,14 @@ void Interface::GetRegisterInfoPrivate(HLERequestContext& ctx) {
738 const auto device_handle{rp.Pop<u64>()}; 260 const auto device_handle{rp.Pop<u64>()};
739 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 261 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
740 262
741 if (state == State::NonInitialized) { 263 RegisterInfoPrivate register_info{};
742 IPC::ResponseBuilder rb{ctx, 2}; 264 auto result = GetManager()->GetRegisterInfoPrivate(device_handle, register_info);
743 rb.Push(NfcDisabled); 265 result = TranslateResultToServiceError(result);
744 return;
745 }
746
747 auto device = GetNfpDevice(device_handle);
748 266
749 if (!device.has_value()) { 267 if (result.IsSuccess()) {
750 IPC::ResponseBuilder rb{ctx, 2}; 268 ctx.WriteBuffer(register_info);
751 rb.Push(DeviceNotFound);
752 return;
753 } 269 }
754 270
755 RegisterInfoPrivate register_info{};
756 const auto result = device.value()->GetRegisterInfoPrivate(register_info);
757 ctx.WriteBuffer(register_info);
758 IPC::ResponseBuilder rb{ctx, 2}; 271 IPC::ResponseBuilder rb{ctx, 2};
759 rb.Push(result); 272 rb.Push(result);
760} 273}
@@ -762,25 +275,15 @@ void Interface::GetRegisterInfoPrivate(HLERequestContext& ctx) {
762void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) { 275void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) {
763 IPC::RequestParser rp{ctx}; 276 IPC::RequestParser rp{ctx};
764 const auto device_handle{rp.Pop<u64>()}; 277 const auto device_handle{rp.Pop<u64>()};
765 const auto buffer{ctx.ReadBuffer()}; 278 const auto register_info_buffer{ctx.ReadBuffer()};
766 LOG_DEBUG(Service_NFP, "called, device_handle={}, buffer_size={}", device_handle, 279 LOG_INFO(Service_NFP, "called, device_handle={}, buffer_size={}", device_handle,
767 buffer.size()); 280 register_info_buffer.size());
768 281
769 if (state == State::NonInitialized) { 282 RegisterInfoPrivate register_info{};
770 IPC::ResponseBuilder rb{ctx, 2}; 283 memcpy(&register_info, register_info_buffer.data(), sizeof(RegisterInfoPrivate));
771 rb.Push(NfcDisabled); 284 auto result = GetManager()->SetRegisterInfoPrivate(device_handle, register_info);
772 return; 285 result = TranslateResultToServiceError(result);
773 }
774
775 auto device = GetNfpDevice(device_handle);
776
777 if (!device.has_value()) {
778 IPC::ResponseBuilder rb{ctx, 2};
779 rb.Push(DeviceNotFound);
780 return;
781 }
782 286
783 const auto result = device.value()->SetRegisterInfoPrivate({});
784 IPC::ResponseBuilder rb{ctx, 2}; 287 IPC::ResponseBuilder rb{ctx, 2};
785 rb.Push(result); 288 rb.Push(result);
786} 289}
@@ -788,23 +291,11 @@ void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) {
788void Interface::DeleteRegisterInfo(HLERequestContext& ctx) { 291void Interface::DeleteRegisterInfo(HLERequestContext& ctx) {
789 IPC::RequestParser rp{ctx}; 292 IPC::RequestParser rp{ctx};
790 const auto device_handle{rp.Pop<u64>()}; 293 const auto device_handle{rp.Pop<u64>()};
791 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 294 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
792
793 if (state == State::NonInitialized) {
794 IPC::ResponseBuilder rb{ctx, 2};
795 rb.Push(NfcDisabled);
796 return;
797 }
798 295
799 auto device = GetNfpDevice(device_handle); 296 auto result = GetManager()->DeleteRegisterInfo(device_handle);
297 result = TranslateResultToServiceError(result);
800 298
801 if (!device.has_value()) {
802 IPC::ResponseBuilder rb{ctx, 2};
803 rb.Push(DeviceNotFound);
804 return;
805 }
806
807 const auto result = device.value()->DeleteRegisterInfo();
808 IPC::ResponseBuilder rb{ctx, 2}; 299 IPC::ResponseBuilder rb{ctx, 2};
809 rb.Push(result); 300 rb.Push(result);
810} 301}
@@ -812,23 +303,11 @@ void Interface::DeleteRegisterInfo(HLERequestContext& ctx) {
812void Interface::DeleteApplicationArea(HLERequestContext& ctx) { 303void Interface::DeleteApplicationArea(HLERequestContext& ctx) {
813 IPC::RequestParser rp{ctx}; 304 IPC::RequestParser rp{ctx};
814 const auto device_handle{rp.Pop<u64>()}; 305 const auto device_handle{rp.Pop<u64>()};
815 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 306 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
816
817 if (state == State::NonInitialized) {
818 IPC::ResponseBuilder rb{ctx, 2};
819 rb.Push(NfcDisabled);
820 return;
821 }
822 307
823 auto device = GetNfpDevice(device_handle); 308 auto result = GetManager()->DeleteApplicationArea(device_handle);
309 result = TranslateResultToServiceError(result);
824 310
825 if (!device.has_value()) {
826 IPC::ResponseBuilder rb{ctx, 2};
827 rb.Push(DeviceNotFound);
828 return;
829 }
830
831 const auto result = device.value()->DeleteApplicationArea();
832 IPC::ResponseBuilder rb{ctx, 2}; 311 IPC::ResponseBuilder rb{ctx, 2};
833 rb.Push(result); 312 rb.Push(result);
834} 313}
@@ -836,24 +315,18 @@ void Interface::DeleteApplicationArea(HLERequestContext& ctx) {
836void Interface::ExistsApplicationArea(HLERequestContext& ctx) { 315void Interface::ExistsApplicationArea(HLERequestContext& ctx) {
837 IPC::RequestParser rp{ctx}; 316 IPC::RequestParser rp{ctx};
838 const auto device_handle{rp.Pop<u64>()}; 317 const auto device_handle{rp.Pop<u64>()};
839 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 318 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
840
841 if (state == State::NonInitialized) {
842 IPC::ResponseBuilder rb{ctx, 2};
843 rb.Push(NfcDisabled);
844 return;
845 }
846 319
847 auto device = GetNfpDevice(device_handle); 320 bool has_application_area = false;
321 auto result = GetManager()->ExistsApplicationArea(device_handle, has_application_area);
322 result = TranslateResultToServiceError(result);
848 323
849 if (!device.has_value()) { 324 if (result.IsError()) {
850 IPC::ResponseBuilder rb{ctx, 2}; 325 IPC::ResponseBuilder rb{ctx, 2};
851 rb.Push(DeviceNotFound); 326 rb.Push(result);
852 return; 327 return;
853 } 328 }
854 329
855 bool has_application_area = false;
856 const auto result = device.value()->ExistApplicationArea(has_application_area);
857 IPC::ResponseBuilder rb{ctx, 3}; 330 IPC::ResponseBuilder rb{ctx, 3};
858 rb.Push(result); 331 rb.Push(result);
859 rb.Push(has_application_area); 332 rb.Push(has_application_area);
@@ -862,27 +335,16 @@ void Interface::ExistsApplicationArea(HLERequestContext& ctx) {
862void Interface::GetAll(HLERequestContext& ctx) { 335void Interface::GetAll(HLERequestContext& ctx) {
863 IPC::RequestParser rp{ctx}; 336 IPC::RequestParser rp{ctx};
864 const auto device_handle{rp.Pop<u64>()}; 337 const auto device_handle{rp.Pop<u64>()};
865 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 338 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
866
867 if (state == State::NonInitialized) {
868 IPC::ResponseBuilder rb{ctx, 2};
869 rb.Push(NfcDisabled);
870 return;
871 }
872 339
873 auto device = GetNfpDevice(device_handle); 340 NfpData nfp_data{};
341 auto result = GetManager()->GetAll(device_handle, nfp_data);
342 result = TranslateResultToServiceError(result);
874 343
875 if (!device.has_value()) { 344 if (result.IsSuccess()) {
876 IPC::ResponseBuilder rb{ctx, 2}; 345 ctx.WriteBuffer(nfp_data);
877 rb.Push(DeviceNotFound);
878 return;
879 } 346 }
880 347
881 NfpData data{};
882 const auto result = device.value()->GetAll(data);
883
884 ctx.WriteBuffer(data);
885
886 IPC::ResponseBuilder rb{ctx, 2}; 348 IPC::ResponseBuilder rb{ctx, 2};
887 rb.Push(result); 349 rb.Push(result);
888} 350}
@@ -890,28 +352,15 @@ void Interface::GetAll(HLERequestContext& ctx) {
890void Interface::SetAll(HLERequestContext& ctx) { 352void Interface::SetAll(HLERequestContext& ctx) {
891 IPC::RequestParser rp{ctx}; 353 IPC::RequestParser rp{ctx};
892 const auto device_handle{rp.Pop<u64>()}; 354 const auto device_handle{rp.Pop<u64>()};
893 const auto nfp_data{ctx.ReadBuffer()}; 355 const auto nfp_data_buffer{ctx.ReadBuffer()};
894
895 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
896
897 if (state == State::NonInitialized) {
898 IPC::ResponseBuilder rb{ctx, 2};
899 rb.Push(NfcDisabled);
900 return;
901 }
902 356
903 auto device = GetNfpDevice(device_handle); 357 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
904
905 if (!device.has_value()) {
906 IPC::ResponseBuilder rb{ctx, 2};
907 rb.Push(DeviceNotFound);
908 return;
909 }
910 358
911 NfpData data{}; 359 NfpData nfp_data{};
912 memcpy(&data, nfp_data.data(), sizeof(NfpData)); 360 memcpy(&nfp_data, nfp_data_buffer.data(), sizeof(NfpData));
361 auto result = GetManager()->SetAll(device_handle, nfp_data);
362 result = TranslateResultToServiceError(result);
913 363
914 const auto result = device.value()->SetAll(data);
915 IPC::ResponseBuilder rb{ctx, 2}; 364 IPC::ResponseBuilder rb{ctx, 2};
916 rb.Push(result); 365 rb.Push(result);
917} 366}
@@ -919,23 +368,11 @@ void Interface::SetAll(HLERequestContext& ctx) {
919void Interface::FlushDebug(HLERequestContext& ctx) { 368void Interface::FlushDebug(HLERequestContext& ctx) {
920 IPC::RequestParser rp{ctx}; 369 IPC::RequestParser rp{ctx};
921 const auto device_handle{rp.Pop<u64>()}; 370 const auto device_handle{rp.Pop<u64>()};
922 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 371 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
923
924 if (state == State::NonInitialized) {
925 IPC::ResponseBuilder rb{ctx, 2};
926 rb.Push(NfcDisabled);
927 return;
928 }
929 372
930 auto device = GetNfpDevice(device_handle); 373 auto result = GetManager()->FlushDebug(device_handle);
374 result = TranslateResultToServiceError(result);
931 375
932 if (!device.has_value()) {
933 IPC::ResponseBuilder rb{ctx, 2};
934 rb.Push(DeviceNotFound);
935 return;
936 }
937
938 const auto result = device.value()->FlushDebug();
939 IPC::ResponseBuilder rb{ctx, 2}; 376 IPC::ResponseBuilder rb{ctx, 2};
940 rb.Push(result); 377 rb.Push(result);
941} 378}
@@ -944,23 +381,12 @@ void Interface::BreakTag(HLERequestContext& ctx) {
944 IPC::RequestParser rp{ctx}; 381 IPC::RequestParser rp{ctx};
945 const auto device_handle{rp.Pop<u64>()}; 382 const auto device_handle{rp.Pop<u64>()};
946 const auto break_type{rp.PopEnum<BreakType>()}; 383 const auto break_type{rp.PopEnum<BreakType>()};
947 LOG_DEBUG(Service_NFP, "called, device_handle={}, break_type={}", device_handle, break_type); 384 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, break_type={}", device_handle,
385 break_type);
948 386
949 if (state == State::NonInitialized) { 387 auto result = GetManager()->BreakTag(device_handle, break_type);
950 IPC::ResponseBuilder rb{ctx, 2}; 388 result = TranslateResultToServiceError(result);
951 rb.Push(NfcDisabled);
952 return;
953 }
954 389
955 auto device = GetNfpDevice(device_handle);
956
957 if (!device.has_value()) {
958 IPC::ResponseBuilder rb{ctx, 2};
959 rb.Push(DeviceNotFound);
960 return;
961 }
962
963 const auto result = device.value()->BreakTag(break_type);
964 IPC::ResponseBuilder rb{ctx, 2}; 390 IPC::ResponseBuilder rb{ctx, 2};
965 rb.Push(result); 391 rb.Push(result);
966} 392}
@@ -968,23 +394,16 @@ void Interface::BreakTag(HLERequestContext& ctx) {
968void Interface::ReadBackupData(HLERequestContext& ctx) { 394void Interface::ReadBackupData(HLERequestContext& ctx) {
969 IPC::RequestParser rp{ctx}; 395 IPC::RequestParser rp{ctx};
970 const auto device_handle{rp.Pop<u64>()}; 396 const auto device_handle{rp.Pop<u64>()};
971 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 397 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
972
973 if (state == State::NonInitialized) {
974 IPC::ResponseBuilder rb{ctx, 2};
975 rb.Push(NfcDisabled);
976 return;
977 }
978 398
979 auto device = GetNfpDevice(device_handle); 399 std::vector<u8> backup_data{};
400 auto result = GetManager()->ReadBackupData(device_handle, backup_data);
401 result = TranslateResultToServiceError(result);
980 402
981 if (!device.has_value()) { 403 if (result.IsSuccess()) {
982 IPC::ResponseBuilder rb{ctx, 2}; 404 ctx.WriteBuffer(backup_data);
983 rb.Push(DeviceNotFound);
984 return;
985 } 405 }
986 406
987 const auto result = device.value()->ReadBackupData();
988 IPC::ResponseBuilder rb{ctx, 2}; 407 IPC::ResponseBuilder rb{ctx, 2};
989 rb.Push(result); 408 rb.Push(result);
990} 409}
@@ -992,23 +411,12 @@ void Interface::ReadBackupData(HLERequestContext& ctx) {
992void Interface::WriteBackupData(HLERequestContext& ctx) { 411void Interface::WriteBackupData(HLERequestContext& ctx) {
993 IPC::RequestParser rp{ctx}; 412 IPC::RequestParser rp{ctx};
994 const auto device_handle{rp.Pop<u64>()}; 413 const auto device_handle{rp.Pop<u64>()};
995 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 414 const auto backup_data_buffer{ctx.ReadBuffer()};
996 415 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
997 if (state == State::NonInitialized) {
998 IPC::ResponseBuilder rb{ctx, 2};
999 rb.Push(NfcDisabled);
1000 return;
1001 }
1002
1003 auto device = GetNfpDevice(device_handle);
1004 416
1005 if (!device.has_value()) { 417 auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer);
1006 IPC::ResponseBuilder rb{ctx, 2}; 418 result = TranslateResultToServiceError(result);
1007 rb.Push(DeviceNotFound);
1008 return;
1009 }
1010 419
1011 const auto result = device.value()->WriteBackupData();
1012 IPC::ResponseBuilder rb{ctx, 2}; 420 IPC::ResponseBuilder rb{ctx, 2};
1013 rb.Push(result); 421 rb.Push(result);
1014} 422}
@@ -1016,34 +424,15 @@ void Interface::WriteBackupData(HLERequestContext& ctx) {
1016void Interface::WriteNtf(HLERequestContext& ctx) { 424void Interface::WriteNtf(HLERequestContext& ctx) {
1017 IPC::RequestParser rp{ctx}; 425 IPC::RequestParser rp{ctx};
1018 const auto device_handle{rp.Pop<u64>()}; 426 const auto device_handle{rp.Pop<u64>()};
1019 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 427 const auto write_type{rp.PopEnum<WriteType>()};
1020 428 const auto ntf_data_buffer{ctx.ReadBuffer()};
1021 if (state == State::NonInitialized) { 429 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
1022 IPC::ResponseBuilder rb{ctx, 2};
1023 rb.Push(NfcDisabled);
1024 return;
1025 }
1026 430
1027 auto device = GetNfpDevice(device_handle); 431 auto result = GetManager()->WriteNtf(device_handle, write_type, ntf_data_buffer);
432 result = TranslateResultToServiceError(result);
1028 433
1029 if (!device.has_value()) {
1030 IPC::ResponseBuilder rb{ctx, 2};
1031 rb.Push(DeviceNotFound);
1032 return;
1033 }
1034
1035 const auto result = device.value()->WriteNtf();
1036 IPC::ResponseBuilder rb{ctx, 2}; 434 IPC::ResponseBuilder rb{ctx, 2};
1037 rb.Push(result); 435 rb.Push(result);
1038} 436}
1039 437
1040std::optional<std::shared_ptr<NfpDevice>> Interface::GetNfpDevice(u64 handle) {
1041 for (auto& device : devices) {
1042 if (device->GetHandle() == handle) {
1043 return device;
1044 }
1045 }
1046 return std::nullopt;
1047}
1048
1049} // namespace Service::NFP 438} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_interface.h b/src/core/hle/service/nfp/nfp_interface.h
index 616c94b06..fa985b068 100644
--- a/src/core/hle/service/nfp/nfp_interface.h
+++ b/src/core/hle/service/nfp/nfp_interface.h
@@ -1,32 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <array>
7#include <memory>
8#include <optional>
9
10#include "core/hle/service/kernel_helpers.h" 6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/nfc/nfc_interface.h"
11#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
12 9
13namespace Service::NFP { 10namespace Service::NFP {
14class NfpDevice;
15 11
16class Interface : public ServiceFramework<Interface> { 12class Interface : public NFC::NfcInterface {
17public: 13public:
18 explicit Interface(Core::System& system_, const char* name); 14 explicit Interface(Core::System& system_, const char* name);
19 ~Interface() override; 15 ~Interface() override;
20 16
21 void Initialize(HLERequestContext& ctx);
22 void InitializeSystem(HLERequestContext& ctx); 17 void InitializeSystem(HLERequestContext& ctx);
23 void InitializeDebug(HLERequestContext& ctx); 18 void InitializeDebug(HLERequestContext& ctx);
24 void Finalize(HLERequestContext& ctx);
25 void FinalizeSystem(HLERequestContext& ctx); 19 void FinalizeSystem(HLERequestContext& ctx);
26 void FinalizeDebug(HLERequestContext& ctx); 20 void FinalizeDebug(HLERequestContext& ctx);
27 void ListDevices(HLERequestContext& ctx);
28 void StartDetection(HLERequestContext& ctx);
29 void StopDetection(HLERequestContext& ctx);
30 void Mount(HLERequestContext& ctx); 21 void Mount(HLERequestContext& ctx);
31 void Unmount(HLERequestContext& ctx); 22 void Unmount(HLERequestContext& ctx);
32 void OpenApplicationArea(HLERequestContext& ctx); 23 void OpenApplicationArea(HLERequestContext& ctx);
@@ -35,17 +26,10 @@ public:
35 void Flush(HLERequestContext& ctx); 26 void Flush(HLERequestContext& ctx);
36 void Restore(HLERequestContext& ctx); 27 void Restore(HLERequestContext& ctx);
37 void CreateApplicationArea(HLERequestContext& ctx); 28 void CreateApplicationArea(HLERequestContext& ctx);
38 void GetTagInfo(HLERequestContext& ctx);
39 void GetRegisterInfo(HLERequestContext& ctx); 29 void GetRegisterInfo(HLERequestContext& ctx);
40 void GetCommonInfo(HLERequestContext& ctx); 30 void GetCommonInfo(HLERequestContext& ctx);
41 void GetModelInfo(HLERequestContext& ctx); 31 void GetModelInfo(HLERequestContext& ctx);
42 void AttachActivateEvent(HLERequestContext& ctx);
43 void AttachDeactivateEvent(HLERequestContext& ctx);
44 void GetState(HLERequestContext& ctx);
45 void GetDeviceState(HLERequestContext& ctx);
46 void GetNpadId(HLERequestContext& ctx);
47 void GetApplicationAreaSize(HLERequestContext& ctx); 32 void GetApplicationAreaSize(HLERequestContext& ctx);
48 void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
49 void RecreateApplicationArea(HLERequestContext& ctx); 33 void RecreateApplicationArea(HLERequestContext& ctx);
50 void Format(HLERequestContext& ctx); 34 void Format(HLERequestContext& ctx);
51 void GetAdminInfo(HLERequestContext& ctx); 35 void GetAdminInfo(HLERequestContext& ctx);
@@ -61,21 +45,6 @@ public:
61 void ReadBackupData(HLERequestContext& ctx); 45 void ReadBackupData(HLERequestContext& ctx);
62 void WriteBackupData(HLERequestContext& ctx); 46 void WriteBackupData(HLERequestContext& ctx);
63 void WriteNtf(HLERequestContext& ctx); 47 void WriteNtf(HLERequestContext& ctx);
64
65private:
66 enum class State : u32 {
67 NonInitialized,
68 Initialized,
69 };
70
71 std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
72
73 KernelHelpers::ServiceContext service_context;
74
75 std::array<std::shared_ptr<NfpDevice>, 10> devices{};
76
77 State state{State::NonInitialized};
78 Kernel::KEvent* availability_change_event;
79}; 48};
80 49
81} // namespace Service::NFP 50} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h
index d8e4cf094..4c126cd81 100644
--- a/src/core/hle/service/nfp/nfp_result.h
+++ b/src/core/hle/service/nfp/nfp_result.h
@@ -1,5 +1,5 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
5 5
@@ -7,18 +7,19 @@
7 7
8namespace Service::NFP { 8namespace Service::NFP {
9 9
10constexpr Result DeviceNotFound(ErrorModule::NFP, 64); 10constexpr Result ResultDeviceNotFound(ErrorModule::NFP, 64);
11constexpr Result InvalidArgument(ErrorModule::NFP, 65); 11constexpr Result ResultInvalidArgument(ErrorModule::NFP, 65);
12constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68); 12constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
13constexpr Result WrongDeviceState(ErrorModule::NFP, 73); 13constexpr Result ResultWrongDeviceState(ErrorModule::NFP, 73);
14constexpr Result NfcDisabled(ErrorModule::NFP, 80); 14constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
15constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); 15constexpr Result ResultNfcDisabled(ErrorModule::NFP, 80);
16constexpr Result TagRemoved(ErrorModule::NFP, 97); 16constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
17constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); 17constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
18constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); 18constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
19constexpr Result CorruptedData(ErrorModule::NFP, 144); 19constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
20constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); 20constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
21constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); 21constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
22constexpr Result NotAnAmiibo(ErrorModule::NFP, 178); 22constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
23constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
23 24
24} // namespace Service::NFP 25} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 1ef047cee..7d36d5ee6 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -7,32 +7,19 @@
7 7
8#include "common/swap.h" 8#include "common/swap.h"
9#include "core/hle/service/mii/types.h" 9#include "core/hle/service/mii/types.h"
10#include "core/hle/service/nfc/nfc_types.h"
10 11
11namespace Service::NFP { 12namespace Service::NFP {
12static constexpr std::size_t amiibo_name_length = 0xA; 13static constexpr std::size_t amiibo_name_length = 0xA;
13static constexpr std::size_t application_id_version_offset = 0x1c; 14static constexpr std::size_t application_id_version_offset = 0x1c;
14static constexpr std::size_t counter_limit = 0xffff; 15static constexpr std::size_t counter_limit = 0xffff;
15 16
16enum class ServiceType : u32 { 17// This is nn::nfp::ModelType
17 User,
18 Debug,
19 System,
20};
21
22enum class DeviceState : u32 {
23 Initialized,
24 SearchingForTag,
25 TagFound,
26 TagRemoved,
27 TagMounted,
28 Unavailable,
29 Finalized,
30};
31
32enum class ModelType : u32 { 18enum class ModelType : u32 {
33 Amiibo, 19 Amiibo,
34}; 20};
35 21
22// This is nn::nfp::MountTarget
36enum class MountTarget : u32 { 23enum class MountTarget : u32 {
37 None, 24 None,
38 Rom, 25 Rom,
@@ -72,35 +59,6 @@ enum class AmiiboSeries : u8 {
72 Diablo, 59 Diablo,
73}; 60};
74 61
75enum class TagType : u32 {
76 None,
77 Type1, // ISO14443A RW 96-2k bytes 106kbit/s
78 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
79 Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
80 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
81 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
82};
83
84enum class PackedTagType : u8 {
85 None,
86 Type1, // ISO14443A RW 96-2k bytes 106kbit/s
87 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
88 Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
89 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
90 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
91};
92
93// Verify this enum. It might be completely wrong default protocol is 0x48
94enum class TagProtocol : u32 {
95 None,
96 TypeA = 1U << 0, // ISO14443A
97 TypeB = 1U << 1, // ISO14443B
98 TypeF = 1U << 2, // Sony Felica
99 Unknown1 = 1U << 3,
100 Unknown2 = 1U << 5,
101 All = 0xFFFFFFFFU,
102};
103
104enum class AppAreaVersion : u8 { 62enum class AppAreaVersion : u8 {
105 Nintendo3DS = 0, 63 Nintendo3DS = 0,
106 NintendoWiiU = 1, 64 NintendoWiiU = 1,
@@ -115,6 +73,11 @@ enum class BreakType : u32 {
115 Unknown2, 73 Unknown2,
116}; 74};
117 75
76enum class WriteType : u32 {
77 Unknown0,
78 Unknown1,
79};
80
118enum class CabinetMode : u8 { 81enum class CabinetMode : u8 {
119 StartNicknameAndOwnerSettings, 82 StartNicknameAndOwnerSettings,
120 StartGameDataEraser, 83 StartGameDataEraser,
@@ -122,27 +85,16 @@ enum class CabinetMode : u8 {
122 StartFormatter, 85 StartFormatter,
123}; 86};
124 87
125enum class MifareCmd : u8 {
126 AuthA = 0x60,
127 AuthB = 0x61,
128 Read = 0x30,
129 Write = 0xA0,
130 Transfer = 0xB0,
131 Decrement = 0xC0,
132 Increment = 0xC1,
133 Store = 0xC2
134};
135
136using UniqueSerialNumber = std::array<u8, 7>;
137using LockBytes = std::array<u8, 2>; 88using LockBytes = std::array<u8, 2>;
138using HashData = std::array<u8, 0x20>; 89using HashData = std::array<u8, 0x20>;
139using ApplicationArea = std::array<u8, 0xD8>; 90using ApplicationArea = std::array<u8, 0xD8>;
140using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; 91using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
141using DataBlock = std::array<u8, 0x10>; 92
142using KeyData = std::array<u8, 0x6>; 93// This is nn::nfp::TagInfo
94using TagInfo = NFC::TagInfo;
143 95
144struct TagUuid { 96struct TagUuid {
145 UniqueSerialNumber uid; 97 NFC::UniqueSerialNumber uid;
146 u8 nintendo_id; 98 u8 nintendo_id;
147 LockBytes lock_bytes; 99 LockBytes lock_bytes;
148}; 100};
@@ -243,7 +195,7 @@ struct AmiiboModelInfo {
243 AmiiboType amiibo_type; 195 AmiiboType amiibo_type;
244 u16_be model_number; 196 u16_be model_number;
245 AmiiboSeries series; 197 AmiiboSeries series;
246 PackedTagType tag_type; 198 NFC::PackedTagType tag_type;
247 INSERT_PADDING_BYTES(0x4); // Unknown 199 INSERT_PADDING_BYTES(0x4); // Unknown
248}; 200};
249static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); 201static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");
@@ -298,7 +250,7 @@ struct NTAG215File {
298 u32_be register_info_crc; 250 u32_be register_info_crc;
299 ApplicationArea application_area; // Encrypted Game data 251 ApplicationArea application_area; // Encrypted Game data
300 HashData hmac_tag; // Hash 252 HashData hmac_tag; // Hash
301 UniqueSerialNumber uid; // Unique serial number 253 NFC::UniqueSerialNumber uid; // Unique serial number
302 u8 nintendo_id; // Tag UUID 254 u8 nintendo_id; // Tag UUID
303 AmiiboModelInfo model_info; 255 AmiiboModelInfo model_info;
304 HashData keygen_salt; // Salt 256 HashData keygen_salt; // Salt
@@ -326,17 +278,7 @@ static_assert(sizeof(EncryptedNTAG215File) == sizeof(NTAG215File),
326static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, 278static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
327 "EncryptedNTAG215File must be trivially copyable."); 279 "EncryptedNTAG215File must be trivially copyable.");
328 280
329struct TagInfo { 281// This is nn::nfp::CommonInfo
330 UniqueSerialNumber uuid;
331 INSERT_PADDING_BYTES(0x3);
332 u8 uuid_length;
333 INSERT_PADDING_BYTES(0x15);
334 TagProtocol protocol;
335 TagType tag_type;
336 INSERT_PADDING_BYTES(0x30);
337};
338static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
339
340struct CommonInfo { 282struct CommonInfo {
341 WriteDate last_write_date; 283 WriteDate last_write_date;
342 u16 write_counter; 284 u16 write_counter;
@@ -347,6 +289,7 @@ struct CommonInfo {
347}; 289};
348static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); 290static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
349 291
292// This is nn::nfp::ModelInfo
350struct ModelInfo { 293struct ModelInfo {
351 u16 character_id; 294 u16 character_id;
352 u8 character_variant; 295 u8 character_variant;
@@ -357,6 +300,7 @@ struct ModelInfo {
357}; 300};
358static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); 301static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
359 302
303// This is nn::nfp::RegisterInfo
360struct RegisterInfo { 304struct RegisterInfo {
361 Service::Mii::CharInfo mii_char_info; 305 Service::Mii::CharInfo mii_char_info;
362 WriteDate creation_date; 306 WriteDate creation_date;
@@ -366,6 +310,7 @@ struct RegisterInfo {
366}; 310};
367static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); 311static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
368 312
313// This is nn::nfp::RegisterInfoPrivate
369struct RegisterInfoPrivate { 314struct RegisterInfoPrivate {
370 Service::Mii::MiiStoreData mii_store_data; 315 Service::Mii::MiiStoreData mii_store_data;
371 WriteDate creation_date; 316 WriteDate creation_date;
@@ -375,12 +320,13 @@ struct RegisterInfoPrivate {
375}; 320};
376static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size"); 321static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");
377 322
323// This is nn::nfp::AdminInfo
378struct AdminInfo { 324struct AdminInfo {
379 u64 application_id; 325 u64 application_id;
380 u32 application_area_id; 326 u32 application_area_id;
381 u16 crc_change_counter; 327 u16 crc_change_counter;
382 u8 flags; 328 u8 flags;
383 PackedTagType tag_type; 329 NFC::PackedTagType tag_type;
384 AppAreaVersion app_area_version; 330 AppAreaVersion app_area_version;
385 INSERT_PADDING_BYTES(0x7); 331 INSERT_PADDING_BYTES(0x7);
386 INSERT_PADDING_BYTES(0x28); 332 INSERT_PADDING_BYTES(0x28);
@@ -411,7 +357,7 @@ struct NfpData {
411 u32 access_id; 357 u32 access_id;
412 u16 settings_crc_counter; 358 u16 settings_crc_counter;
413 u8 font_region; 359 u8 font_region;
414 PackedTagType tag_type; 360 NFC::PackedTagType tag_type;
415 AppAreaVersion console_type; 361 AppAreaVersion console_type;
416 u8 application_id_byte; 362 u8 application_id_byte;
417 INSERT_PADDING_BYTES(0x2E); 363 INSERT_PADDING_BYTES(0x2E);
@@ -420,37 +366,4 @@ struct NfpData {
420static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size"); 366static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
421#pragma pack() 367#pragma pack()
422 368
423struct SectorKey {
424 MifareCmd command;
425 u8 unknown; // Usually 1
426 INSERT_PADDING_BYTES(0x6);
427 KeyData sector_key;
428 INSERT_PADDING_BYTES(0x2);
429};
430static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
431
432struct MifareReadBlockParameter {
433 u8 sector_number;
434 INSERT_PADDING_BYTES(0x7);
435 SectorKey sector_key;
436};
437static_assert(sizeof(MifareReadBlockParameter) == 0x18,
438 "MifareReadBlockParameter is an invalid size");
439
440struct MifareReadBlockData {
441 DataBlock data;
442 u8 sector_number;
443 INSERT_PADDING_BYTES(0x7);
444};
445static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
446
447struct MifareWriteBlockParameter {
448 DataBlock data;
449 u8 sector_number;
450 INSERT_PADDING_BYTES(0x7);
451 SectorKey sector_key;
452};
453static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
454 "MifareWriteBlockParameter is an invalid size");
455
456} // namespace Service::NFP 369} // namespace Service::NFP
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 0c042f412..91d42853e 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -218,7 +218,7 @@ public:
218 218
219private: 219private:
220 void Submit(HLERequestContext& ctx) { 220 void Submit(HLERequestContext& ctx) {
221 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 221 LOG_DEBUG(Service_NIFM, "(STUBBED) called");
222 222
223 if (state == RequestState::NotSubmitted) { 223 if (state == RequestState::NotSubmitted) {
224 UpdateState(RequestState::OnHold); 224 UpdateState(RequestState::OnHold);
@@ -229,7 +229,7 @@ private:
229 } 229 }
230 230
231 void GetRequestState(HLERequestContext& ctx) { 231 void GetRequestState(HLERequestContext& ctx) {
232 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 232 LOG_DEBUG(Service_NIFM, "(STUBBED) called");
233 233
234 IPC::ResponseBuilder rb{ctx, 3}; 234 IPC::ResponseBuilder rb{ctx, 3};
235 rb.Push(ResultSuccess); 235 rb.Push(ResultSuccess);
@@ -237,7 +237,7 @@ private:
237 } 237 }
238 238
239 void GetResult(HLERequestContext& ctx) { 239 void GetResult(HLERequestContext& ctx) {
240 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 240 LOG_DEBUG(Service_NIFM, "(STUBBED) called");
241 241
242 const auto result = [this] { 242 const auto result = [this] {
243 const auto has_connection = Network::GetHostIPv4Address().has_value(); 243 const auto has_connection = Network::GetHostIPv4Address().has_value();
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 6b4a1291e..156bc27d8 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -33,6 +33,9 @@ ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_m
33 // Initialize event. 33 // Initialize event.
34 m_event = Kernel::KEvent::Create(system.Kernel()); 34 m_event = Kernel::KEvent::Create(system.Kernel());
35 m_event->Initialize(nullptr); 35 m_event->Initialize(nullptr);
36
37 // Register event.
38 Kernel::KEvent::Register(system.Kernel(), m_event);
36} 39}
37 40
38ServerManager::~ServerManager() { 41ServerManager::~ServerManager() {
@@ -160,6 +163,9 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
160 // Initialize the event. 163 // Initialize the event.
161 m_deferral_event->Initialize(nullptr); 164 m_deferral_event->Initialize(nullptr);
162 165
166 // Register the event.
167 Kernel::KEvent::Register(m_system.Kernel(), m_deferral_event);
168
163 // Set the output. 169 // Set the output.
164 *out_event = m_deferral_event; 170 *out_event = m_deferral_event;
165 171
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 0f79a1b7e..45b2c43b7 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -142,7 +142,8 @@ template <typename Self>
142class ServiceFramework : public ServiceFrameworkBase { 142class ServiceFramework : public ServiceFrameworkBase {
143protected: 143protected:
144 /// Contains information about a request type which is handled by the service. 144 /// Contains information about a request type which is handled by the service.
145 struct FunctionInfo : FunctionInfoBase { 145 template <typename T>
146 struct FunctionInfoTyped : FunctionInfoBase {
146 // TODO(yuriks): This function could be constexpr, but clang is the only compiler that 147 // TODO(yuriks): This function could be constexpr, but clang is the only compiler that
147 // doesn't emit an ICE or a wrong diagnostic because of the static_cast. 148 // doesn't emit an ICE or a wrong diagnostic because of the static_cast.
148 149
@@ -155,12 +156,13 @@ protected:
155 * the request 156 * the request
156 * @param name_ human-friendly name for the request. Used mostly for logging purposes. 157 * @param name_ human-friendly name for the request. Used mostly for logging purposes.
157 */ 158 */
158 FunctionInfo(u32 expected_header_, HandlerFnP<Self> handler_callback_, const char* name_) 159 FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_)
159 : FunctionInfoBase{ 160 : FunctionInfoBase{
160 expected_header_, 161 expected_header_,
161 // Type-erase member function pointer by casting it down to the base class. 162 // Type-erase member function pointer by casting it down to the base class.
162 static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback_), name_} {} 163 static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback_), name_} {}
163 }; 164 };
165 using FunctionInfo = FunctionInfoTyped<Self>;
164 166
165 /** 167 /**
166 * Initializes the handler with no functions installed. 168 * Initializes the handler with no functions installed.
@@ -175,8 +177,8 @@ protected:
175 : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} 177 : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
176 178
177 /// Registers handlers in the service. 179 /// Registers handlers in the service.
178 template <std::size_t N> 180 template <typename T = Self, std::size_t N>
179 void RegisterHandlers(const FunctionInfo (&functions)[N]) { 181 void RegisterHandlers(const FunctionInfoTyped<T> (&functions)[N]) {
180 RegisterHandlers(functions, N); 182 RegisterHandlers(functions, N);
181 } 183 }
182 184
@@ -184,13 +186,14 @@ protected:
184 * Registers handlers in the service. Usually prefer using the other RegisterHandlers 186 * Registers handlers in the service. Usually prefer using the other RegisterHandlers
185 * overload in order to avoid needing to specify the array size. 187 * overload in order to avoid needing to specify the array size.
186 */ 188 */
187 void RegisterHandlers(const FunctionInfo* functions, std::size_t n) { 189 template <typename T = Self>
190 void RegisterHandlers(const FunctionInfoTyped<T>* functions, std::size_t n) {
188 RegisterHandlersBase(functions, n); 191 RegisterHandlersBase(functions, n);
189 } 192 }
190 193
191 /// Registers handlers in the service. 194 /// Registers handlers in the service.
192 template <std::size_t N> 195 template <typename T = Self, std::size_t N>
193 void RegisterHandlersTipc(const FunctionInfo (&functions)[N]) { 196 void RegisterHandlersTipc(const FunctionInfoTyped<T> (&functions)[N]) {
194 RegisterHandlersTipc(functions, N); 197 RegisterHandlersTipc(functions, N);
195 } 198 }
196 199
@@ -198,7 +201,8 @@ protected:
198 * Registers handlers in the service. Usually prefer using the other RegisterHandlers 201 * Registers handlers in the service. Usually prefer using the other RegisterHandlers
199 * overload in order to avoid needing to specify the array size. 202 * overload in order to avoid needing to specify the array size.
200 */ 203 */
201 void RegisterHandlersTipc(const FunctionInfo* functions, std::size_t n) { 204 template <typename T = Self>
205 void RegisterHandlersTipc(const FunctionInfoTyped<T>* functions, std::size_t n) {
202 RegisterHandlersBaseTipc(functions, n); 206 RegisterHandlersBaseTipc(functions, n);
203 } 207 }
204 208
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index c45be5726..1608fa24c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -64,6 +64,9 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
64 auto* port = Kernel::KPort::Create(kernel); 64 auto* port = Kernel::KPort::Create(kernel);
65 port->Initialize(ServerSessionCountMax, false, 0); 65 port->Initialize(ServerSessionCountMax, false, 0);
66 66
67 // Register the port.
68 Kernel::KPort::Register(kernel, port);
69
67 service_ports.emplace(name, port); 70 service_ports.emplace(name, port);
68 registered_services.emplace(name, handler); 71 registered_services.emplace(name, handler);
69 if (deferral_event) { 72 if (deferral_event) {
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 419c1df2b..7dce28fe0 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -49,6 +49,9 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
49 // Commit the session reservation. 49 // Commit the session reservation.
50 session_reservation.Commit(); 50 session_reservation.Commit();
51 51
52 // Register the session.
53 Kernel::KSession::Register(system.Kernel(), session);
54
52 // Register with server manager. 55 // Register with server manager.
53 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(), 56 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
54 session_manager); 57 session_manager);
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index bf97b0ebc..75ac10a9c 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -356,7 +356,7 @@ NetworkInstance::~NetworkInstance() {
356std::optional<IPv4Address> GetHostIPv4Address() { 356std::optional<IPv4Address> GetHostIPv4Address() {
357 const auto network_interface = Network::GetSelectedNetworkInterface(); 357 const auto network_interface = Network::GetSelectedNetworkInterface();
358 if (!network_interface.has_value()) { 358 if (!network_interface.has_value()) {
359 LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface"); 359 LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface");
360 return {}; 360 return {};
361 } 361 }
362 362
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 7b8e510a2..4c909a6d3 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -200,7 +200,7 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
200 }); 200 });
201 201
202 if (res == network_interfaces.end()) { 202 if (res == network_interfaces.end()) {
203 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); 203 LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
204 return std::nullopt; 204 return std::nullopt;
205 } 205 }
206 206
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 432310632..514ba0d66 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -13,10 +13,12 @@
13#include "common/swap.h" 13#include "common/swap.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/device_memory.h" 15#include "core/device_memory.h"
16#include "core/hardware_properties.h"
16#include "core/hle/kernel/k_page_table.h" 17#include "core/hle/kernel/k_page_table.h"
17#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
18#include "core/memory.h" 19#include "core/memory.h"
19#include "video_core/gpu.h" 20#include "video_core/gpu.h"
21#include "video_core/rasterizer_download_area.h"
20 22
21namespace Core::Memory { 23namespace Core::Memory {
22 24
@@ -243,7 +245,7 @@ struct Memory::Impl {
243 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, 245 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
244 const u8* const host_ptr) { 246 const u8* const host_ptr) {
245 if constexpr (!UNSAFE) { 247 if constexpr (!UNSAFE) {
246 system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount); 248 HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
247 } 249 }
248 std::memcpy(dest_buffer, host_ptr, copy_amount); 250 std::memcpy(dest_buffer, host_ptr, copy_amount);
249 }, 251 },
@@ -334,7 +336,7 @@ struct Memory::Impl {
334 }, 336 },
335 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, 337 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
336 u8* const host_ptr) { 338 u8* const host_ptr) {
337 system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount); 339 HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
338 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount); 340 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
339 }, 341 },
340 [&](const std::size_t copy_amount) { 342 [&](const std::size_t copy_amount) {
@@ -373,7 +375,7 @@ struct Memory::Impl {
373 const std::size_t block_size) { 375 const std::size_t block_size) {
374 // dc ivac: Invalidate to point of coherency 376 // dc ivac: Invalidate to point of coherency
375 // GPU flush -> CPU invalidate 377 // GPU flush -> CPU invalidate
376 system.GPU().FlushRegion(GetInteger(current_vaddr), block_size); 378 HandleRasterizerDownload(GetInteger(current_vaddr), block_size);
377 }; 379 };
378 return PerformCacheOperation(process, dest_addr, size, on_rasterizer); 380 return PerformCacheOperation(process, dest_addr, size, on_rasterizer);
379 } 381 }
@@ -462,7 +464,8 @@ struct Memory::Impl {
462 } 464 }
463 465
464 if (Settings::IsFastmemEnabled()) { 466 if (Settings::IsFastmemEnabled()) {
465 const bool is_read_enable = Settings::IsGPULevelHigh() || !cached; 467 const bool is_read_enable =
468 !Settings::values.use_reactive_flushing.GetValue() || !cached;
466 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 469 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
467 } 470 }
468 471
@@ -651,7 +654,7 @@ struct Memory::Impl {
651 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, 654 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8,
652 GetInteger(vaddr)); 655 GetInteger(vaddr));
653 }, 656 },
654 [&]() { system.GPU().FlushRegion(GetInteger(vaddr), sizeof(T)); }); 657 [&]() { HandleRasterizerDownload(GetInteger(vaddr), sizeof(T)); });
655 if (ptr) { 658 if (ptr) {
656 std::memcpy(&result, ptr, sizeof(T)); 659 std::memcpy(&result, ptr, sizeof(T));
657 } 660 }
@@ -712,7 +715,19 @@ struct Memory::Impl {
712 return true; 715 return true;
713 } 716 }
714 717
718 void HandleRasterizerDownload(VAddr address, size_t size) {
719 const size_t core = system.GetCurrentHostThreadID();
720 auto& current_area = rasterizer_areas[core];
721 const VAddr end_address = address + size;
722 if (current_area.start_address <= address && end_address <= current_area.end_address)
723 [[likely]] {
724 return;
725 }
726 current_area = system.GPU().OnCPURead(address, size);
727 }
728
715 Common::PageTable* current_page_table = nullptr; 729 Common::PageTable* current_page_table = nullptr;
730 std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_areas{};
716 Core::System& system; 731 Core::System& system;
717}; 732};
718 733
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 9178b00ca..7a2f3c90a 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -85,6 +85,20 @@ static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
85 return "Unknown"; 85 return "Unknown";
86} 86}
87 87
88static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
89 switch (mode) {
90 case Settings::VSyncMode::Immediate:
91 return "Immediate";
92 case Settings::VSyncMode::Mailbox:
93 return "Mailbox";
94 case Settings::VSyncMode::FIFO:
95 return "FIFO";
96 case Settings::VSyncMode::FIFORelaxed:
97 return "FIFO Relaxed";
98 }
99 return "Unknown";
100}
101
88u64 GetTelemetryId() { 102u64 GetTelemetryId() {
89 u64 telemetry_id{}; 103 u64 telemetry_id{};
90 const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; 104 const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@@ -241,7 +255,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
241 AddField(field_type, "Renderer_NvdecEmulation", 255 AddField(field_type, "Renderer_NvdecEmulation",
242 TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue())); 256 TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
243 AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue()); 257 AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
244 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); 258 AddField(field_type, "Renderer_UseVsync",
259 TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
245 AddField(field_type, "Renderer_ShaderBackend", 260 AddField(field_type, "Renderer_ShaderBackend",
246 static_cast<u32>(Settings::values.shader_backend.GetValue())); 261 static_cast<u32>(Settings::values.shader_backend.GetValue()));
247 AddField(field_type, "Renderer_UseAsynchronousShaders", 262 AddField(field_type, "Renderer_UseAsynchronousShaders",
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 2b42a4555..077d72cd0 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -236,13 +236,13 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
236 return DriverResult::Success; 236 return DriverResult::Success;
237} 237}
238 238
239DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc, 239DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCommand sc,
240 std::span<const u8> buffer, 240 std::span<const u8> buffer,
241 MCUCommandResponse& output) { 241 MCUCommandResponse& output) {
242 SubCommandPacket packet{ 242 SubCommandPacket packet{
243 .output_report = OutputReport::MCU_DATA, 243 .output_report = OutputReport::MCU_DATA,
244 .packet_counter = GetCounter(), 244 .packet_counter = GetCounter(),
245 .sub_command = sc, 245 .mcu_sub_command = sc,
246 .command_data = {}, 246 .command_data = {},
247 }; 247 };
248 248
@@ -269,8 +269,7 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod
269 std::size_t tries{}; 269 std::size_t tries{};
270 270
271 do { 271 do {
272 const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)}; 272 const auto result = SendMCUData(report_mode, MCUSubCommand::SetDeviceMode, {}, output);
273 const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output);
274 273
275 if (result != DriverResult::Success) { 274 if (result != DriverResult::Success) {
276 return result; 275 return result;
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
index 62cae739a..411ec018a 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.h
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -156,7 +156,7 @@ public:
156 * @param buffer data to be send 156 * @param buffer data to be send
157 * @returns output buffer containing the response 157 * @returns output buffer containing the response
158 */ 158 */
159 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer, 159 DriverResult SendMCUData(ReportMode report_mode, MCUSubCommand sc, std::span<const u8> buffer,
160 MCUCommandResponse& output); 160 MCUCommandResponse& output);
161 161
162 /** 162 /**
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index dcac0e422..b03143e04 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -575,7 +575,6 @@ struct NFCPollingCommandData {
575static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size"); 575static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
576 576
577struct NFCRequestState { 577struct NFCRequestState {
578 MCUSubCommand sub_command;
579 NFCReadCommand command_argument; 578 NFCReadCommand command_argument;
580 u8 packet_id; 579 u8 packet_id;
581 INSERT_PADDING_BYTES(0x1); 580 INSERT_PADDING_BYTES(0x1);
@@ -587,6 +586,7 @@ struct NFCRequestState {
587 NFCPollingCommandData nfc_polling; 586 NFCPollingCommandData nfc_polling;
588 }; 587 };
589 u8 crc; 588 u8 crc;
589 INSERT_PADDING_BYTES(0x1);
590}; 590};
591static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); 591static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
592 592
@@ -659,7 +659,10 @@ struct SubCommandPacket {
659 OutputReport output_report; 659 OutputReport output_report;
660 u8 packet_counter; 660 u8 packet_counter;
661 INSERT_PADDING_BYTES(0x8); // This contains vibration data 661 INSERT_PADDING_BYTES(0x8); // This contains vibration data
662 SubCommand sub_command; 662 union {
663 SubCommand sub_command;
664 MCUSubCommand mcu_sub_command;
665 };
663 std::array<u8, 0x26> command_data; 666 std::array<u8, 0x26> command_data;
664}; 667};
665static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size"); 668static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index eeba82986..77ea6d5cf 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -278,7 +278,6 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
278 278
279DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) { 279DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
280 NFCRequestState request{ 280 NFCRequestState request{
281 .sub_command = MCUSubCommand::ReadDeviceMode,
282 .command_argument = NFCReadCommand::StartPolling, 281 .command_argument = NFCReadCommand::StartPolling,
283 .packet_id = 0x0, 282 .packet_id = 0x0,
284 .packet_flag = MCUPacketFlag::LastCommandPacket, 283 .packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -296,13 +295,13 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
296 295
297 std::array<u8, sizeof(NFCRequestState)> request_data{}; 296 std::array<u8, sizeof(NFCRequestState)> request_data{};
298 memcpy(request_data.data(), &request, sizeof(NFCRequestState)); 297 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
299 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); 298 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
300 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 299 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
300 output);
301} 301}
302 302
303DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { 303DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
304 NFCRequestState request{ 304 NFCRequestState request{
305 .sub_command = MCUSubCommand::ReadDeviceMode,
306 .command_argument = NFCReadCommand::StopPolling, 305 .command_argument = NFCReadCommand::StopPolling,
307 .packet_id = 0x0, 306 .packet_id = 0x0,
308 .packet_flag = MCUPacketFlag::LastCommandPacket, 307 .packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -313,13 +312,13 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
313 312
314 std::array<u8, sizeof(NFCRequestState)> request_data{}; 313 std::array<u8, sizeof(NFCRequestState)> request_data{};
315 memcpy(request_data.data(), &request, sizeof(NFCRequestState)); 314 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
316 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); 315 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
317 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 316 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
317 output);
318} 318}
319 319
320DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) { 320DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
321 NFCRequestState request{ 321 NFCRequestState request{
322 .sub_command = MCUSubCommand::ReadDeviceMode,
323 .command_argument = NFCReadCommand::StartWaitingRecieve, 322 .command_argument = NFCReadCommand::StartWaitingRecieve,
324 .packet_id = 0x0, 323 .packet_id = 0x0,
325 .packet_flag = MCUPacketFlag::LastCommandPacket, 324 .packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -330,13 +329,13 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& out
330 329
331 std::vector<u8> request_data(sizeof(NFCRequestState)); 330 std::vector<u8> request_data(sizeof(NFCRequestState));
332 memcpy(request_data.data(), &request, sizeof(NFCRequestState)); 331 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
333 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); 332 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
334 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 333 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
334 output);
335} 335}
336 336
337DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { 337DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
338 NFCRequestState request{ 338 NFCRequestState request{
339 .sub_command = MCUSubCommand::ReadDeviceMode,
340 .command_argument = NFCReadCommand::Ntag, 339 .command_argument = NFCReadCommand::Ntag,
341 .packet_id = 0x0, 340 .packet_id = 0x0,
342 .packet_flag = MCUPacketFlag::LastCommandPacket, 341 .packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -355,8 +354,9 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
355 354
356 std::array<u8, sizeof(NFCRequestState)> request_data{}; 355 std::array<u8, sizeof(NFCRequestState)> request_data{};
357 memcpy(request_data.data(), &request, sizeof(NFCRequestState)); 356 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
358 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); 357 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
359 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 358 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
359 output);
360} 360}
361 361
362NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { 362NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index 9361b00c5..8c2ee4eb3 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -82,6 +82,9 @@ void MappingFactory::RegisterButton(const MappingData& data) {
82 new_input.Set("axis", data.index); 82 new_input.Set("axis", data.index);
83 new_input.Set("threshold", 0.5f); 83 new_input.Set("threshold", 0.5f);
84 break; 84 break;
85 case EngineInputType::Motion:
86 new_input.Set("motion", data.index);
87 break;
85 default: 88 default:
86 return; 89 return;
87 } 90 }
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 8c6a6521a..380a01542 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -667,7 +667,7 @@ public:
667 .raw_value = input_engine->GetAxis(identifier, axis_z), 667 .raw_value = input_engine->GetAxis(identifier, axis_z),
668 .properties = properties_z, 668 .properties = properties_z,
669 }; 669 };
670 status.delta_timestamp = 5000; 670 status.delta_timestamp = 1000;
671 status.force_update = true; 671 status.force_update = true;
672 return status; 672 return status;
673 } 673 }
@@ -939,6 +939,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
939 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), 939 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
940 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), 940 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
941 .inverted = params.Get("invert", "+") == "-", 941 .inverted = params.Get("invert", "+") == "-",
942 .inverted_button = params.Get("inverted", false) != 0,
942 .toggle = params.Get("toggle", false) != 0, 943 .toggle = params.Get("toggle", false) != 0,
943 }; 944 };
944 input_engine->PreSetController(identifier); 945 input_engine->PreSetController(identifier);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 0cd87a48f..fee510f7b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -473,7 +473,8 @@ void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
473} 473}
474 474
475void EmitSetSampleMask(EmitContext& ctx, Id value) { 475void EmitSetSampleMask(EmitContext& ctx, Id value) {
476 ctx.OpStore(ctx.sample_mask, value); 476 const Id pointer{ctx.OpAccessChain(ctx.output_u32, ctx.sample_mask, ctx.u32_zero_value)};
477 ctx.OpStore(pointer, value);
477} 478}
478 479
479void EmitSetFragDepth(EmitContext& ctx, Id value) { 480void EmitSetFragDepth(EmitContext& ctx, Id value) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index d48d4860e..47739794f 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1572,7 +1572,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
1572 Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth); 1572 Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth);
1573 } 1573 }
1574 if (info.stores_sample_mask) { 1574 if (info.stores_sample_mask) {
1575 sample_mask = DefineOutput(*this, U32[1], std::nullopt); 1575 const Id array_type{TypeArray(U32[1], Const(1U))};
1576 sample_mask = DefineOutput(*this, array_type, std::nullopt);
1576 Decorate(sample_mask, spv::Decoration::BuiltIn, spv::BuiltIn::SampleMask); 1577 Decorate(sample_mask, spv::Decoration::BuiltIn, spv::BuiltIn::SampleMask);
1577 } 1578 }
1578 break; 1579 break;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 39b774c98..1e158f375 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -15,7 +15,7 @@ add_executable(tests
15 core/core_timing.cpp 15 core/core_timing.cpp
16 core/internal_network/network.cpp 16 core/internal_network/network.cpp
17 precompiled_headers.h 17 precompiled_headers.h
18 video_core/buffer_base.cpp 18 video_core/memory_tracker.cpp
19 input_common/calibration_configuration_job.cpp 19 input_common/calibration_configuration_job.cpp
20) 20)
21 21
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
deleted file mode 100644
index 734dbf4b6..000000000
--- a/src/tests/video_core/buffer_base.cpp
+++ /dev/null
@@ -1,549 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <stdexcept>
5#include <unordered_map>
6
7#include <catch2/catch_test_macros.hpp>
8
9#include "common/alignment.h"
10#include "common/common_types.h"
11#include "video_core/buffer_cache/buffer_base.h"
12
13namespace {
14using VideoCommon::BufferBase;
15using Range = std::pair<u64, u64>;
16
17constexpr u64 PAGE = 4096;
18constexpr u64 WORD = 4096 * 64;
19
20constexpr VAddr c = 0x1328914000;
21
22class RasterizerInterface {
23public:
24 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
25 const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS};
26 const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >>
27 Core::Memory::YUZU_PAGEBITS};
28 for (u64 page = page_start; page < page_end; ++page) {
29 int& value = page_table[page];
30 value += delta;
31 if (value < 0) {
32 throw std::logic_error{"negative page"};
33 }
34 if (value == 0) {
35 page_table.erase(page);
36 }
37 }
38 }
39
40 [[nodiscard]] int Count(VAddr addr) const noexcept {
41 const auto it = page_table.find(addr >> Core::Memory::YUZU_PAGEBITS);
42 return it == page_table.end() ? 0 : it->second;
43 }
44
45 [[nodiscard]] unsigned Count() const noexcept {
46 unsigned count = 0;
47 for (const auto& [index, value] : page_table) {
48 count += value;
49 }
50 return count;
51 }
52
53private:
54 std::unordered_map<u64, int> page_table;
55};
56} // Anonymous namespace
57
58TEST_CASE("BufferBase: Small buffer", "[video_core]") {
59 RasterizerInterface rasterizer;
60 BufferBase buffer(rasterizer, c, WORD);
61 REQUIRE(rasterizer.Count() == 0);
62 buffer.UnmarkRegionAsCpuModified(c, WORD);
63 REQUIRE(rasterizer.Count() == WORD / PAGE);
64 REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{0, 0});
65
66 buffer.MarkRegionAsCpuModified(c + PAGE, 1);
67 REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{PAGE * 1, PAGE * 2});
68}
69
70TEST_CASE("BufferBase: Large buffer", "[video_core]") {
71 RasterizerInterface rasterizer;
72 BufferBase buffer(rasterizer, c, WORD * 32);
73 buffer.UnmarkRegionAsCpuModified(c, WORD * 32);
74 buffer.MarkRegionAsCpuModified(c + 4096, WORD * 4);
75 REQUIRE(buffer.ModifiedCpuRegion(c, WORD + PAGE * 2) == Range{PAGE, WORD + PAGE * 2});
76 REQUIRE(buffer.ModifiedCpuRegion(c + PAGE * 2, PAGE * 6) == Range{PAGE * 2, PAGE * 8});
77 REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 4 + PAGE});
78 REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 4, PAGE) == Range{WORD * 4, WORD * 4 + PAGE});
79 REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 3 + PAGE * 63, PAGE) ==
80 Range{WORD * 3 + PAGE * 63, WORD * 4});
81
82 buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 6, PAGE);
83 buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE);
84 REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) ==
85 Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 9});
86
87 buffer.UnmarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE);
88 REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) ==
89 Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 7});
90
91 buffer.MarkRegionAsCpuModified(c + PAGE, WORD * 31 + PAGE * 63);
92 REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 32});
93
94 buffer.UnmarkRegionAsCpuModified(c + PAGE * 4, PAGE);
95 buffer.UnmarkRegionAsCpuModified(c + PAGE * 6, PAGE);
96
97 buffer.UnmarkRegionAsCpuModified(c, WORD * 32);
98 REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{0, 0});
99}
100
101TEST_CASE("BufferBase: Rasterizer counting", "[video_core]") {
102 RasterizerInterface rasterizer;
103 BufferBase buffer(rasterizer, c, PAGE * 2);
104 REQUIRE(rasterizer.Count() == 0);
105 buffer.UnmarkRegionAsCpuModified(c, PAGE);
106 REQUIRE(rasterizer.Count() == 1);
107 buffer.MarkRegionAsCpuModified(c, PAGE * 2);
108 REQUIRE(rasterizer.Count() == 0);
109 buffer.UnmarkRegionAsCpuModified(c, PAGE);
110 buffer.UnmarkRegionAsCpuModified(c + PAGE, PAGE);
111 REQUIRE(rasterizer.Count() == 2);
112 buffer.MarkRegionAsCpuModified(c, PAGE * 2);
113 REQUIRE(rasterizer.Count() == 0);
114}
115
116TEST_CASE("BufferBase: Basic range", "[video_core]") {
117 RasterizerInterface rasterizer;
118 BufferBase buffer(rasterizer, c, WORD);
119 buffer.UnmarkRegionAsCpuModified(c, WORD);
120 buffer.MarkRegionAsCpuModified(c, PAGE);
121 int num = 0;
122 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
123 REQUIRE(offset == 0U);
124 REQUIRE(size == PAGE);
125 ++num;
126 });
127 REQUIRE(num == 1U);
128}
129
130TEST_CASE("BufferBase: Border upload", "[video_core]") {
131 RasterizerInterface rasterizer;
132 BufferBase buffer(rasterizer, c, WORD * 2);
133 buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
134 buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
135 buffer.ForEachUploadRange(c, WORD * 2, [](u64 offset, u64 size) {
136 REQUIRE(offset == WORD - PAGE);
137 REQUIRE(size == PAGE * 2);
138 });
139}
140
141TEST_CASE("BufferBase: Border upload range", "[video_core]") {
142 RasterizerInterface rasterizer;
143 BufferBase buffer(rasterizer, c, WORD * 2);
144 buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
145 buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
146 buffer.ForEachUploadRange(c + WORD - PAGE, PAGE * 2, [](u64 offset, u64 size) {
147 REQUIRE(offset == WORD - PAGE);
148 REQUIRE(size == PAGE * 2);
149 });
150 buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
151 buffer.ForEachUploadRange(c + WORD - PAGE, PAGE, [](u64 offset, u64 size) {
152 REQUIRE(offset == WORD - PAGE);
153 REQUIRE(size == PAGE);
154 });
155 buffer.ForEachUploadRange(c + WORD, PAGE, [](u64 offset, u64 size) {
156 REQUIRE(offset == WORD);
157 REQUIRE(size == PAGE);
158 });
159}
160
161TEST_CASE("BufferBase: Border upload partial range", "[video_core]") {
162 RasterizerInterface rasterizer;
163 BufferBase buffer(rasterizer, c, WORD * 2);
164 buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
165 buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
166 buffer.ForEachUploadRange(c + WORD - 1, 2, [](u64 offset, u64 size) {
167 REQUIRE(offset == WORD - PAGE);
168 REQUIRE(size == PAGE * 2);
169 });
170 buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
171 buffer.ForEachUploadRange(c + WORD - 1, 1, [](u64 offset, u64 size) {
172 REQUIRE(offset == WORD - PAGE);
173 REQUIRE(size == PAGE);
174 });
175 buffer.ForEachUploadRange(c + WORD + 50, 1, [](u64 offset, u64 size) {
176 REQUIRE(offset == WORD);
177 REQUIRE(size == PAGE);
178 });
179}
180
181TEST_CASE("BufferBase: Partial word uploads", "[video_core]") {
182 RasterizerInterface rasterizer;
183 BufferBase buffer(rasterizer, c, 0x9d000);
184 int num = 0;
185 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
186 REQUIRE(offset == 0U);
187 REQUIRE(size == WORD);
188 ++num;
189 });
190 REQUIRE(num == 1);
191 buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) {
192 REQUIRE(offset == WORD);
193 REQUIRE(size == WORD);
194 ++num;
195 });
196 REQUIRE(num == 2);
197 buffer.ForEachUploadRange(c + 0x79000, 0x24000, [&](u64 offset, u64 size) {
198 REQUIRE(offset == WORD * 2);
199 REQUIRE(size == PAGE * 0x1d);
200 ++num;
201 });
202 REQUIRE(num == 3);
203}
204
205TEST_CASE("BufferBase: Partial page upload", "[video_core]") {
206 RasterizerInterface rasterizer;
207 BufferBase buffer(rasterizer, c, WORD);
208 buffer.UnmarkRegionAsCpuModified(c, WORD);
209 int num = 0;
210 buffer.MarkRegionAsCpuModified(c + PAGE * 2, PAGE);
211 buffer.MarkRegionAsCpuModified(c + PAGE * 9, PAGE);
212 buffer.ForEachUploadRange(c, PAGE * 3, [&](u64 offset, u64 size) {
213 REQUIRE(offset == PAGE * 2);
214 REQUIRE(size == PAGE);
215 ++num;
216 });
217 REQUIRE(num == 1);
218 buffer.ForEachUploadRange(c + PAGE * 7, PAGE * 3, [&](u64 offset, u64 size) {
219 REQUIRE(offset == PAGE * 9);
220 REQUIRE(size == PAGE);
221 ++num;
222 });
223 REQUIRE(num == 2);
224}
225
226TEST_CASE("BufferBase: Partial page upload with multiple words on the right") {
227 RasterizerInterface rasterizer;
228 BufferBase buffer(rasterizer, c, WORD * 8);
229 buffer.UnmarkRegionAsCpuModified(c, WORD * 8);
230 buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7);
231 int num = 0;
232 buffer.ForEachUploadRange(c + PAGE * 10, WORD * 7, [&](u64 offset, u64 size) {
233 REQUIRE(offset == PAGE * 13);
234 REQUIRE(size == WORD * 7 - PAGE * 3);
235 ++num;
236 });
237 REQUIRE(num == 1);
238 buffer.ForEachUploadRange(c + PAGE, WORD * 8, [&](u64 offset, u64 size) {
239 REQUIRE(offset == WORD * 7 + PAGE * 10);
240 REQUIRE(size == PAGE * 3);
241 ++num;
242 });
243 REQUIRE(num == 2);
244}
245
246TEST_CASE("BufferBase: Partial page upload with multiple words on the left", "[video_core]") {
247 RasterizerInterface rasterizer;
248 BufferBase buffer(rasterizer, c, WORD * 8);
249 buffer.UnmarkRegionAsCpuModified(c, WORD * 8);
250 buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7);
251 int num = 0;
252 buffer.ForEachUploadRange(c + PAGE * 16, WORD * 7, [&](u64 offset, u64 size) {
253 REQUIRE(offset == PAGE * 16);
254 REQUIRE(size == WORD * 7 - PAGE * 3);
255 ++num;
256 });
257 REQUIRE(num == 1);
258 buffer.ForEachUploadRange(c + PAGE, WORD, [&](u64 offset, u64 size) {
259 REQUIRE(offset == PAGE * 13);
260 REQUIRE(size == PAGE * 3);
261 ++num;
262 });
263 REQUIRE(num == 2);
264}
265
266TEST_CASE("BufferBase: Partial page upload with multiple words in the middle", "[video_core]") {
267 RasterizerInterface rasterizer;
268 BufferBase buffer(rasterizer, c, WORD * 8);
269 buffer.UnmarkRegionAsCpuModified(c, WORD * 8);
270 buffer.MarkRegionAsCpuModified(c + PAGE * 13, PAGE * 140);
271 int num = 0;
272 buffer.ForEachUploadRange(c + PAGE * 16, WORD, [&](u64 offset, u64 size) {
273 REQUIRE(offset == PAGE * 16);
274 REQUIRE(size == WORD);
275 ++num;
276 });
277 REQUIRE(num == 1);
278 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
279 REQUIRE(offset == PAGE * 13);
280 REQUIRE(size == PAGE * 3);
281 ++num;
282 });
283 REQUIRE(num == 2);
284 buffer.ForEachUploadRange(c, WORD * 8, [&](u64 offset, u64 size) {
285 REQUIRE(offset == WORD + PAGE * 16);
286 REQUIRE(size == PAGE * 73);
287 ++num;
288 });
289 REQUIRE(num == 3);
290}
291
292TEST_CASE("BufferBase: Empty right bits", "[video_core]") {
293 RasterizerInterface rasterizer;
294 BufferBase buffer(rasterizer, c, WORD * 2048);
295 buffer.UnmarkRegionAsCpuModified(c, WORD * 2048);
296 buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
297 buffer.ForEachUploadRange(c, WORD * 2048, [](u64 offset, u64 size) {
298 REQUIRE(offset == WORD - PAGE);
299 REQUIRE(size == PAGE * 2);
300 });
301}
302
303TEST_CASE("BufferBase: Out of bound ranges 1", "[video_core]") {
304 RasterizerInterface rasterizer;
305 BufferBase buffer(rasterizer, c, WORD);
306 buffer.UnmarkRegionAsCpuModified(c, WORD);
307 buffer.MarkRegionAsCpuModified(c, PAGE);
308 int num = 0;
309 buffer.ForEachUploadRange(c - WORD, WORD, [&](u64 offset, u64 size) { ++num; });
310 buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { ++num; });
311 buffer.ForEachUploadRange(c - PAGE, PAGE, [&](u64 offset, u64 size) { ++num; });
312 REQUIRE(num == 0);
313 buffer.ForEachUploadRange(c - PAGE, PAGE * 2, [&](u64 offset, u64 size) { ++num; });
314 REQUIRE(num == 1);
315 buffer.MarkRegionAsCpuModified(c, WORD);
316 REQUIRE(rasterizer.Count() == 0);
317}
318
319TEST_CASE("BufferBase: Out of bound ranges 2", "[video_core]") {
320 RasterizerInterface rasterizer;
321 BufferBase buffer(rasterizer, c, 0x22000);
322 REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x22000, PAGE));
323 REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x28000, PAGE));
324 REQUIRE(rasterizer.Count() == 0);
325 REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x21100, PAGE - 0x100));
326 REQUIRE(rasterizer.Count() == 1);
327 REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c - 0x1000, PAGE * 2));
328 buffer.UnmarkRegionAsCpuModified(c - 0x3000, PAGE * 2);
329 buffer.UnmarkRegionAsCpuModified(c - 0x2000, PAGE * 2);
330 REQUIRE(rasterizer.Count() == 2);
331}
332
333TEST_CASE("BufferBase: Out of bound ranges 3", "[video_core]") {
334 RasterizerInterface rasterizer;
335 BufferBase buffer(rasterizer, c, 0x310720);
336 buffer.UnmarkRegionAsCpuModified(c, 0x310720);
337 REQUIRE(rasterizer.Count(c) == 1);
338 REQUIRE(rasterizer.Count(c + PAGE) == 1);
339 REQUIRE(rasterizer.Count(c + WORD) == 1);
340 REQUIRE(rasterizer.Count(c + WORD + PAGE) == 1);
341}
342
343TEST_CASE("BufferBase: Sparse regions 1", "[video_core]") {
344 RasterizerInterface rasterizer;
345 BufferBase buffer(rasterizer, c, WORD);
346 buffer.UnmarkRegionAsCpuModified(c, WORD);
347 buffer.MarkRegionAsCpuModified(c + PAGE * 1, PAGE);
348 buffer.MarkRegionAsCpuModified(c + PAGE * 3, PAGE * 4);
349 buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable {
350 static constexpr std::array<u64, 2> offsets{PAGE, PAGE * 3};
351 static constexpr std::array<u64, 2> sizes{PAGE, PAGE * 4};
352 REQUIRE(offset == offsets.at(i));
353 REQUIRE(size == sizes.at(i));
354 ++i;
355 });
356}
357
358TEST_CASE("BufferBase: Sparse regions 2", "[video_core]") {
359 RasterizerInterface rasterizer;
360 BufferBase buffer(rasterizer, c, 0x22000);
361 buffer.UnmarkRegionAsCpuModified(c, 0x22000);
362 REQUIRE(rasterizer.Count() == 0x22);
363 buffer.MarkRegionAsCpuModified(c + PAGE * 0x1B, PAGE);
364 buffer.MarkRegionAsCpuModified(c + PAGE * 0x21, PAGE);
365 buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable {
366 static constexpr std::array<u64, 2> offsets{PAGE * 0x1B, PAGE * 0x21};
367 static constexpr std::array<u64, 2> sizes{PAGE, PAGE};
368 REQUIRE(offset == offsets.at(i));
369 REQUIRE(size == sizes.at(i));
370 ++i;
371 });
372}
373
374TEST_CASE("BufferBase: Single page modified range", "[video_core]") {
375 RasterizerInterface rasterizer;
376 BufferBase buffer(rasterizer, c, PAGE);
377 REQUIRE(buffer.IsRegionCpuModified(c, PAGE));
378 buffer.UnmarkRegionAsCpuModified(c, PAGE);
379 REQUIRE(!buffer.IsRegionCpuModified(c, PAGE));
380}
381
382TEST_CASE("BufferBase: Two page modified range", "[video_core]") {
383 RasterizerInterface rasterizer;
384 BufferBase buffer(rasterizer, c, PAGE * 2);
385 REQUIRE(buffer.IsRegionCpuModified(c, PAGE));
386 REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
387 REQUIRE(buffer.IsRegionCpuModified(c, PAGE * 2));
388 buffer.UnmarkRegionAsCpuModified(c, PAGE);
389 REQUIRE(!buffer.IsRegionCpuModified(c, PAGE));
390}
391
392TEST_CASE("BufferBase: Multi word modified ranges", "[video_core]") {
393 for (int offset = 0; offset < 4; ++offset) {
394 const VAddr address = c + WORD * offset;
395 RasterizerInterface rasterizer;
396 BufferBase buffer(rasterizer, address, WORD * 4);
397 REQUIRE(buffer.IsRegionCpuModified(address, PAGE));
398 REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 48, PAGE));
399 REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 56, PAGE));
400
401 buffer.UnmarkRegionAsCpuModified(address + PAGE * 32, PAGE);
402 REQUIRE(buffer.IsRegionCpuModified(address + PAGE, WORD));
403 REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE));
404 REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE));
405 REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 33, PAGE));
406 REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE * 2));
407 REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2));
408
409 buffer.UnmarkRegionAsCpuModified(address + PAGE * 33, PAGE);
410 REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2));
411 }
412}
413
414TEST_CASE("BufferBase: Single page in large buffer", "[video_core]") {
415 RasterizerInterface rasterizer;
416 BufferBase buffer(rasterizer, c, WORD * 16);
417 buffer.UnmarkRegionAsCpuModified(c, WORD * 16);
418 REQUIRE(!buffer.IsRegionCpuModified(c, WORD * 16));
419
420 buffer.MarkRegionAsCpuModified(c + WORD * 12 + PAGE * 8, PAGE);
421 REQUIRE(buffer.IsRegionCpuModified(c, WORD * 16));
422 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 10, WORD * 2));
423 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 11, WORD * 2));
424 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12, WORD * 2));
425 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 4, PAGE * 8));
426 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE * 8));
427 REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE));
428 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 7, PAGE * 2));
429 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 8, PAGE * 2));
430}
431
432TEST_CASE("BufferBase: Out of bounds region query") {
433 RasterizerInterface rasterizer;
434 BufferBase buffer(rasterizer, c, WORD * 16);
435 REQUIRE(!buffer.IsRegionCpuModified(c - PAGE, PAGE));
436 REQUIRE(!buffer.IsRegionCpuModified(c - PAGE * 2, PAGE));
437 REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, PAGE));
438 REQUIRE(buffer.IsRegionCpuModified(c + WORD * 16 - PAGE, WORD * 64));
439 REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, WORD * 64));
440}
441
442TEST_CASE("BufferBase: Wrap word regions") {
443 RasterizerInterface rasterizer;
444 BufferBase buffer(rasterizer, c, WORD * 2);
445 buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
446 buffer.MarkRegionAsCpuModified(c + PAGE * 63, PAGE * 2);
447 REQUIRE(buffer.IsRegionCpuModified(c, WORD * 2));
448 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 62, PAGE));
449 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE));
450 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 64, PAGE));
451 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 2));
452 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 8));
453 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 60, PAGE * 8));
454
455 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16));
456 buffer.MarkRegionAsCpuModified(c + PAGE * 127, PAGE);
457 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16));
458 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, PAGE));
459 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 126, PAGE));
460 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 126, PAGE * 2));
461 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 128, WORD * 16));
462}
463
464TEST_CASE("BufferBase: Unaligned page region query") {
465 RasterizerInterface rasterizer;
466 BufferBase buffer(rasterizer, c, WORD);
467 buffer.UnmarkRegionAsCpuModified(c, WORD);
468 buffer.MarkRegionAsCpuModified(c + 4000, 1000);
469 REQUIRE(buffer.IsRegionCpuModified(c, PAGE));
470 REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
471 REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1000));
472 REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1));
473}
474
475TEST_CASE("BufferBase: Cached write") {
476 RasterizerInterface rasterizer;
477 BufferBase buffer(rasterizer, c, WORD);
478 buffer.UnmarkRegionAsCpuModified(c, WORD);
479 buffer.CachedCpuWrite(c + PAGE, PAGE);
480 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
481 buffer.FlushCachedWrites();
482 REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
483 buffer.MarkRegionAsCpuModified(c, WORD);
484 REQUIRE(rasterizer.Count() == 0);
485}
486
487TEST_CASE("BufferBase: Multiple cached write") {
488 RasterizerInterface rasterizer;
489 BufferBase buffer(rasterizer, c, WORD);
490 buffer.UnmarkRegionAsCpuModified(c, WORD);
491 buffer.CachedCpuWrite(c + PAGE, PAGE);
492 buffer.CachedCpuWrite(c + PAGE * 3, PAGE);
493 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
494 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 3, PAGE));
495 buffer.FlushCachedWrites();
496 REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
497 REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 3, PAGE));
498 buffer.MarkRegionAsCpuModified(c, WORD);
499 REQUIRE(rasterizer.Count() == 0);
500}
501
502TEST_CASE("BufferBase: Cached write unmarked") {
503 RasterizerInterface rasterizer;
504 BufferBase buffer(rasterizer, c, WORD);
505 buffer.UnmarkRegionAsCpuModified(c, WORD);
506 buffer.CachedCpuWrite(c + PAGE, PAGE);
507 buffer.UnmarkRegionAsCpuModified(c + PAGE, PAGE);
508 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
509 buffer.FlushCachedWrites();
510 REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
511 buffer.MarkRegionAsCpuModified(c, WORD);
512 REQUIRE(rasterizer.Count() == 0);
513}
514
515TEST_CASE("BufferBase: Cached write iterated") {
516 RasterizerInterface rasterizer;
517 BufferBase buffer(rasterizer, c, WORD);
518 buffer.UnmarkRegionAsCpuModified(c, WORD);
519 buffer.CachedCpuWrite(c + PAGE, PAGE);
520 int num = 0;
521 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
522 REQUIRE(num == 0);
523 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
524 buffer.FlushCachedWrites();
525 REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
526 buffer.MarkRegionAsCpuModified(c, WORD);
527 REQUIRE(rasterizer.Count() == 0);
528}
529
530TEST_CASE("BufferBase: Cached write downloads") {
531 RasterizerInterface rasterizer;
532 BufferBase buffer(rasterizer, c, WORD);
533 buffer.UnmarkRegionAsCpuModified(c, WORD);
534 REQUIRE(rasterizer.Count() == 64);
535 buffer.CachedCpuWrite(c + PAGE, PAGE);
536 REQUIRE(rasterizer.Count() == 63);
537 buffer.MarkRegionAsGpuModified(c + PAGE, PAGE);
538 int num = 0;
539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
541 REQUIRE(num == 0);
542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
543 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
544 buffer.FlushCachedWrites();
545 REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
546 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
547 buffer.MarkRegionAsCpuModified(c, WORD);
548 REQUIRE(rasterizer.Count() == 0);
549}
diff --git a/src/tests/video_core/memory_tracker.cpp b/src/tests/video_core/memory_tracker.cpp
new file mode 100644
index 000000000..618793668
--- /dev/null
+++ b/src/tests/video_core/memory_tracker.cpp
@@ -0,0 +1,549 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5#include <stdexcept>
6#include <unordered_map>
7
8#include <catch2/catch_test_macros.hpp>
9
10#include "common/alignment.h"
11#include "common/common_types.h"
12#include "video_core/buffer_cache/memory_tracker_base.h"
13
14namespace {
15using Range = std::pair<u64, u64>;
16
17constexpr u64 PAGE = 4096;
18constexpr u64 WORD = 4096 * 64;
19constexpr u64 HIGH_PAGE_BITS = 22;
20constexpr u64 HIGH_PAGE_SIZE = 1ULL << HIGH_PAGE_BITS;
21
22constexpr VAddr c = 16 * HIGH_PAGE_SIZE;
23
24class RasterizerInterface {
25public:
26 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
27 const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS};
28 const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >>
29 Core::Memory::YUZU_PAGEBITS};
30 for (u64 page = page_start; page < page_end; ++page) {
31 int& value = page_table[page];
32 value += delta;
33 if (value < 0) {
34 throw std::logic_error{"negative page"};
35 }
36 if (value == 0) {
37 page_table.erase(page);
38 }
39 }
40 }
41
42 [[nodiscard]] int Count(VAddr addr) const noexcept {
43 const auto it = page_table.find(addr >> Core::Memory::YUZU_PAGEBITS);
44 return it == page_table.end() ? 0 : it->second;
45 }
46
47 [[nodiscard]] unsigned Count() const noexcept {
48 unsigned count = 0;
49 for (const auto& [index, value] : page_table) {
50 count += value;
51 }
52 return count;
53 }
54
55private:
56 std::unordered_map<u64, int> page_table;
57};
58} // Anonymous namespace
59
60using MemoryTracker = VideoCommon::MemoryTrackerBase<RasterizerInterface>;
61
62TEST_CASE("MemoryTracker: Small region", "[video_core]") {
63 RasterizerInterface rasterizer;
64 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
65 REQUIRE(rasterizer.Count() == 0);
66 memory_track->UnmarkRegionAsCpuModified(c, WORD);
67 REQUIRE(rasterizer.Count() == WORD / PAGE);
68 REQUIRE(memory_track->ModifiedCpuRegion(c, WORD) == Range{0, 0});
69
70 memory_track->MarkRegionAsCpuModified(c + PAGE, 1);
71 REQUIRE(memory_track->ModifiedCpuRegion(c, WORD) == Range{c + PAGE * 1, c + PAGE * 2});
72}
73
74TEST_CASE("MemoryTracker: Large region", "[video_core]") {
75 RasterizerInterface rasterizer;
76 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
77 memory_track->UnmarkRegionAsCpuModified(c, WORD * 32);
78 memory_track->MarkRegionAsCpuModified(c + 4096, WORD * 4);
79 REQUIRE(memory_track->ModifiedCpuRegion(c, WORD + PAGE * 2) ==
80 Range{c + PAGE, c + WORD + PAGE * 2});
81 REQUIRE(memory_track->ModifiedCpuRegion(c + PAGE * 2, PAGE * 6) ==
82 Range{c + PAGE * 2, c + PAGE * 8});
83 REQUIRE(memory_track->ModifiedCpuRegion(c, WORD * 32) == Range{c + PAGE, c + WORD * 4 + PAGE});
84 REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 4, PAGE) ==
85 Range{c + WORD * 4, c + WORD * 4 + PAGE});
86 REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 3 + PAGE * 63, PAGE) ==
87 Range{c + WORD * 3 + PAGE * 63, c + WORD * 4});
88
89 memory_track->MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 6, PAGE);
90 memory_track->MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE);
91 REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 5, WORD) ==
92 Range{c + WORD * 5 + PAGE * 6, c + WORD * 5 + PAGE * 9});
93
94 memory_track->UnmarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE);
95 REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 5, WORD) ==
96 Range{c + WORD * 5 + PAGE * 6, c + WORD * 5 + PAGE * 7});
97
98 memory_track->MarkRegionAsCpuModified(c + PAGE, WORD * 31 + PAGE * 63);
99 REQUIRE(memory_track->ModifiedCpuRegion(c, WORD * 32) == Range{c + PAGE, c + WORD * 32});
100
101 memory_track->UnmarkRegionAsCpuModified(c + PAGE * 4, PAGE);
102 memory_track->UnmarkRegionAsCpuModified(c + PAGE * 6, PAGE);
103
104 memory_track->UnmarkRegionAsCpuModified(c, WORD * 32);
105 REQUIRE(memory_track->ModifiedCpuRegion(c, WORD * 32) == Range{0, 0});
106}
107
108TEST_CASE("MemoryTracker: Rasterizer counting", "[video_core]") {
109 RasterizerInterface rasterizer;
110 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
111 REQUIRE(rasterizer.Count() == 0);
112 memory_track->UnmarkRegionAsCpuModified(c, PAGE);
113 REQUIRE(rasterizer.Count() == 1);
114 memory_track->MarkRegionAsCpuModified(c, PAGE * 2);
115 REQUIRE(rasterizer.Count() == 0);
116 memory_track->UnmarkRegionAsCpuModified(c, PAGE);
117 memory_track->UnmarkRegionAsCpuModified(c + PAGE, PAGE);
118 REQUIRE(rasterizer.Count() == 2);
119 memory_track->MarkRegionAsCpuModified(c, PAGE * 2);
120 REQUIRE(rasterizer.Count() == 0);
121}
122
123TEST_CASE("MemoryTracker: Basic range", "[video_core]") {
124 RasterizerInterface rasterizer;
125 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
126 memory_track->UnmarkRegionAsCpuModified(c, WORD);
127 memory_track->MarkRegionAsCpuModified(c, PAGE);
128 int num = 0;
129 memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
130 REQUIRE(offset == c);
131 REQUIRE(size == PAGE);
132 ++num;
133 });
134 REQUIRE(num == 1U);
135}
136
137TEST_CASE("MemoryTracker: Border upload", "[video_core]") {
138 RasterizerInterface rasterizer;
139 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
140 memory_track->UnmarkRegionAsCpuModified(c, WORD * 2);
141 memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
142 memory_track->ForEachUploadRange(c, WORD * 2, [](u64 offset, u64 size) {
143 REQUIRE(offset == c + WORD - PAGE);
144 REQUIRE(size == PAGE * 2);
145 });
146}
147
148TEST_CASE("MemoryTracker: Border upload range", "[video_core]") {
149 RasterizerInterface rasterizer;
150 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
151 memory_track->UnmarkRegionAsCpuModified(c, WORD * 2);
152 memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
153 memory_track->ForEachUploadRange(c + WORD - PAGE, PAGE * 2, [](u64 offset, u64 size) {
154 REQUIRE(offset == c + WORD - PAGE);
155 REQUIRE(size == PAGE * 2);
156 });
157 memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
158 memory_track->ForEachUploadRange(c + WORD - PAGE, PAGE, [](u64 offset, u64 size) {
159 REQUIRE(offset == c + WORD - PAGE);
160 REQUIRE(size == PAGE);
161 });
162 memory_track->ForEachUploadRange(c + WORD, PAGE, [](u64 offset, u64 size) {
163 REQUIRE(offset == c + WORD);
164 REQUIRE(size == PAGE);
165 });
166}
167
168TEST_CASE("MemoryTracker: Border upload partial range", "[video_core]") {
169 RasterizerInterface rasterizer;
170 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
171 memory_track->UnmarkRegionAsCpuModified(c, WORD * 2);
172 memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
173 memory_track->ForEachUploadRange(c + WORD - 1, 2, [](u64 offset, u64 size) {
174 REQUIRE(offset == c + WORD - PAGE);
175 REQUIRE(size == PAGE * 2);
176 });
177 memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
178 memory_track->ForEachUploadRange(c + WORD - 1, 1, [](u64 offset, u64 size) {
179 REQUIRE(offset == c + WORD - PAGE);
180 REQUIRE(size == PAGE);
181 });
182 memory_track->ForEachUploadRange(c + WORD + 50, 1, [](u64 offset, u64 size) {
183 REQUIRE(offset == c + WORD);
184 REQUIRE(size == PAGE);
185 });
186}
187
188TEST_CASE("MemoryTracker: Partial word uploads", "[video_core]") {
189 RasterizerInterface rasterizer;
190 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
191 int num = 0;
192 memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
193 REQUIRE(offset == c);
194 REQUIRE(size == WORD);
195 ++num;
196 });
197 REQUIRE(num == 1);
198 memory_track->ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) {
199 REQUIRE(offset == c + WORD);
200 REQUIRE(size == WORD);
201 ++num;
202 });
203 REQUIRE(num == 2);
204 memory_track->ForEachUploadRange(c + 0x79000, 0x24000, [&](u64 offset, u64 size) {
205 REQUIRE(offset == c + WORD * 2);
206 REQUIRE(size == PAGE * 0x1d);
207 ++num;
208 });
209 REQUIRE(num == 3);
210}
211
212TEST_CASE("MemoryTracker: Partial page upload", "[video_core]") {
213 RasterizerInterface rasterizer;
214 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
215 memory_track->UnmarkRegionAsCpuModified(c, WORD);
216 int num = 0;
217 memory_track->MarkRegionAsCpuModified(c + PAGE * 2, PAGE);
218 memory_track->MarkRegionAsCpuModified(c + PAGE * 9, PAGE);
219 memory_track->ForEachUploadRange(c, PAGE * 3, [&](u64 offset, u64 size) {
220 REQUIRE(offset == c + PAGE * 2);
221 REQUIRE(size == PAGE);
222 ++num;
223 });
224 REQUIRE(num == 1);
225 memory_track->ForEachUploadRange(c + PAGE * 7, PAGE * 3, [&](u64 offset, u64 size) {
226 REQUIRE(offset == c + PAGE * 9);
227 REQUIRE(size == PAGE);
228 ++num;
229 });
230 REQUIRE(num == 2);
231}
232
233TEST_CASE("MemoryTracker: Partial page upload with multiple words on the right") {
234 RasterizerInterface rasterizer;
235 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
236 memory_track->UnmarkRegionAsCpuModified(c, WORD * 9);
237 memory_track->MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7);
238 int num = 0;
239 memory_track->ForEachUploadRange(c + PAGE * 10, WORD * 7, [&](u64 offset, u64 size) {
240 REQUIRE(offset == c + PAGE * 13);
241 REQUIRE(size == WORD * 7 - PAGE * 3);
242 ++num;
243 });
244 REQUIRE(num == 1);
245 memory_track->ForEachUploadRange(c + PAGE, WORD * 8, [&](u64 offset, u64 size) {
246 REQUIRE(offset == c + WORD * 7 + PAGE * 10);
247 REQUIRE(size == PAGE * 3);
248 ++num;
249 });
250 REQUIRE(num == 2);
251}
252
253TEST_CASE("MemoryTracker: Partial page upload with multiple words on the left", "[video_core]") {
254 RasterizerInterface rasterizer;
255 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
256 memory_track->UnmarkRegionAsCpuModified(c, WORD * 8);
257 memory_track->MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7);
258 int num = 0;
259 memory_track->ForEachUploadRange(c + PAGE * 16, WORD * 7, [&](u64 offset, u64 size) {
260 REQUIRE(offset == c + PAGE * 16);
261 REQUIRE(size == WORD * 7 - PAGE * 3);
262 ++num;
263 });
264 REQUIRE(num == 1);
265 memory_track->ForEachUploadRange(c + PAGE, WORD, [&](u64 offset, u64 size) {
266 REQUIRE(offset == c + PAGE * 13);
267 REQUIRE(size == PAGE * 3);
268 ++num;
269 });
270 REQUIRE(num == 2);
271}
272
273TEST_CASE("MemoryTracker: Partial page upload with multiple words in the middle", "[video_core]") {
274 RasterizerInterface rasterizer;
275 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
276 memory_track->UnmarkRegionAsCpuModified(c, WORD * 8);
277 memory_track->MarkRegionAsCpuModified(c + PAGE * 13, PAGE * 140);
278 int num = 0;
279 memory_track->ForEachUploadRange(c + PAGE * 16, WORD, [&](u64 offset, u64 size) {
280 REQUIRE(offset == c + PAGE * 16);
281 REQUIRE(size == WORD);
282 ++num;
283 });
284 REQUIRE(num == 1);
285 memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
286 REQUIRE(offset == c + PAGE * 13);
287 REQUIRE(size == PAGE * 3);
288 ++num;
289 });
290 REQUIRE(num == 2);
291 memory_track->ForEachUploadRange(c, WORD * 8, [&](u64 offset, u64 size) {
292 REQUIRE(offset == c + WORD + PAGE * 16);
293 REQUIRE(size == PAGE * 73);
294 ++num;
295 });
296 REQUIRE(num == 3);
297}
298
299TEST_CASE("MemoryTracker: Empty right bits", "[video_core]") {
300 RasterizerInterface rasterizer;
301 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
302 memory_track->UnmarkRegionAsCpuModified(c, WORD * 2048);
303 memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
304 memory_track->ForEachUploadRange(c, WORD * 2048, [](u64 offset, u64 size) {
305 REQUIRE(offset == c + WORD - PAGE);
306 REQUIRE(size == PAGE * 2);
307 });
308}
309
310TEST_CASE("MemoryTracker: Out of bound ranges 1", "[video_core]") {
311 RasterizerInterface rasterizer;
312 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
313 memory_track->UnmarkRegionAsCpuModified(c - WORD, 3 * WORD);
314 memory_track->MarkRegionAsCpuModified(c, PAGE);
315 REQUIRE(rasterizer.Count() == (3 * WORD - PAGE) / PAGE);
316 int num = 0;
317 memory_track->ForEachUploadRange(c - WORD, WORD, [&](u64 offset, u64 size) { ++num; });
318 memory_track->ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { ++num; });
319 memory_track->ForEachUploadRange(c - PAGE, PAGE, [&](u64 offset, u64 size) { ++num; });
320 REQUIRE(num == 0);
321 memory_track->ForEachUploadRange(c - PAGE, PAGE * 2, [&](u64 offset, u64 size) { ++num; });
322 REQUIRE(num == 1);
323 memory_track->MarkRegionAsCpuModified(c, WORD);
324 REQUIRE(rasterizer.Count() == 2 * WORD / PAGE);
325}
326
327TEST_CASE("MemoryTracker: Out of bound ranges 2", "[video_core]") {
328 RasterizerInterface rasterizer;
329 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
330 REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c + 0x22000, PAGE));
331 REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c + 0x28000, PAGE));
332 REQUIRE(rasterizer.Count() == 2);
333 REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c + 0x21100, PAGE - 0x100));
334 REQUIRE(rasterizer.Count() == 3);
335 REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c - PAGE, PAGE * 2));
336 memory_track->UnmarkRegionAsCpuModified(c - PAGE * 3, PAGE * 2);
337 memory_track->UnmarkRegionAsCpuModified(c - PAGE * 2, PAGE * 2);
338 REQUIRE(rasterizer.Count() == 7);
339}
340
341TEST_CASE("MemoryTracker: Out of bound ranges 3", "[video_core]") {
342 RasterizerInterface rasterizer;
343 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
344 memory_track->UnmarkRegionAsCpuModified(c, 0x310720);
345 REQUIRE(rasterizer.Count(c) == 1);
346 REQUIRE(rasterizer.Count(c + PAGE) == 1);
347 REQUIRE(rasterizer.Count(c + WORD) == 1);
348 REQUIRE(rasterizer.Count(c + WORD + PAGE) == 1);
349}
350
351TEST_CASE("MemoryTracker: Sparse regions 1", "[video_core]") {
352 RasterizerInterface rasterizer;
353 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
354 memory_track->UnmarkRegionAsCpuModified(c, WORD);
355 memory_track->MarkRegionAsCpuModified(c + PAGE * 1, PAGE);
356 memory_track->MarkRegionAsCpuModified(c + PAGE * 3, PAGE * 4);
357 memory_track->ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable {
358 static constexpr std::array<u64, 2> offsets{c + PAGE, c + PAGE * 3};
359 static constexpr std::array<u64, 2> sizes{PAGE, PAGE * 4};
360 REQUIRE(offset == offsets.at(i));
361 REQUIRE(size == sizes.at(i));
362 ++i;
363 });
364}
365
366TEST_CASE("MemoryTracker: Sparse regions 2", "[video_core]") {
367 RasterizerInterface rasterizer;
368 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
369 memory_track->UnmarkRegionAsCpuModified(c, PAGE * 0x23);
370 REQUIRE(rasterizer.Count() == 0x23);
371 memory_track->MarkRegionAsCpuModified(c + PAGE * 0x1B, PAGE);
372 memory_track->MarkRegionAsCpuModified(c + PAGE * 0x21, PAGE);
373 memory_track->ForEachUploadRange(c, PAGE * 0x23, [i = 0](u64 offset, u64 size) mutable {
374 static constexpr std::array<u64, 3> offsets{c + PAGE * 0x1B, c + PAGE * 0x21};
375 static constexpr std::array<u64, 3> sizes{PAGE, PAGE};
376 REQUIRE(offset == offsets.at(i));
377 REQUIRE(size == sizes.at(i));
378 ++i;
379 });
380}
381
382TEST_CASE("MemoryTracker: Single page modified range", "[video_core]") {
383 RasterizerInterface rasterizer;
384 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
385 REQUIRE(memory_track->IsRegionCpuModified(c, PAGE));
386 memory_track->UnmarkRegionAsCpuModified(c, PAGE);
387 REQUIRE(!memory_track->IsRegionCpuModified(c, PAGE));
388}
389
390TEST_CASE("MemoryTracker: Two page modified range", "[video_core]") {
391 RasterizerInterface rasterizer;
392 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
393 REQUIRE(memory_track->IsRegionCpuModified(c, PAGE));
394 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
395 REQUIRE(memory_track->IsRegionCpuModified(c, PAGE * 2));
396 memory_track->UnmarkRegionAsCpuModified(c, PAGE);
397 REQUIRE(!memory_track->IsRegionCpuModified(c, PAGE));
398}
399
400TEST_CASE("MemoryTracker: Multi word modified ranges", "[video_core]") {
401 for (int offset = 0; offset < 4; ++offset) {
402 const VAddr address = c + WORD * offset;
403 RasterizerInterface rasterizer;
404 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
405 REQUIRE(memory_track->IsRegionCpuModified(address, PAGE));
406 REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 48, PAGE));
407 REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 56, PAGE));
408
409 memory_track->UnmarkRegionAsCpuModified(address + PAGE * 32, PAGE);
410 REQUIRE(memory_track->IsRegionCpuModified(address + PAGE, WORD));
411 REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 31, PAGE));
412 REQUIRE(!memory_track->IsRegionCpuModified(address + PAGE * 32, PAGE));
413 REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 33, PAGE));
414 REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 31, PAGE * 2));
415 REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 32, PAGE * 2));
416
417 memory_track->UnmarkRegionAsCpuModified(address + PAGE * 33, PAGE);
418 REQUIRE(!memory_track->IsRegionCpuModified(address + PAGE * 32, PAGE * 2));
419 }
420}
421
422TEST_CASE("MemoryTracker: Single page in large region", "[video_core]") {
423 RasterizerInterface rasterizer;
424 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
425 memory_track->UnmarkRegionAsCpuModified(c, WORD * 16);
426 REQUIRE(!memory_track->IsRegionCpuModified(c, WORD * 16));
427
428 memory_track->MarkRegionAsCpuModified(c + WORD * 12 + PAGE * 8, PAGE);
429 REQUIRE(memory_track->IsRegionCpuModified(c, WORD * 16));
430 REQUIRE(!memory_track->IsRegionCpuModified(c + WORD * 10, WORD * 2));
431 REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 11, WORD * 2));
432 REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12, WORD * 2));
433 REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 4, PAGE * 8));
434 REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE * 8));
435 REQUIRE(!memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE));
436 REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 7, PAGE * 2));
437 REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 8, PAGE * 2));
438}
439
440TEST_CASE("MemoryTracker: Wrap word regions") {
441 RasterizerInterface rasterizer;
442 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
443 memory_track->UnmarkRegionAsCpuModified(c, WORD * 32);
444 memory_track->MarkRegionAsCpuModified(c + PAGE * 63, PAGE * 2);
445 REQUIRE(memory_track->IsRegionCpuModified(c, WORD * 2));
446 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 62, PAGE));
447 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 63, PAGE));
448 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 64, PAGE));
449 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 63, PAGE * 2));
450 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 63, PAGE * 8));
451 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 60, PAGE * 8));
452
453 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 127, WORD * 16));
454 memory_track->MarkRegionAsCpuModified(c + PAGE * 127, PAGE);
455 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 127, WORD * 16));
456 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 127, PAGE));
457 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 126, PAGE));
458 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 126, PAGE * 2));
459 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 128, WORD * 16));
460}
461
462TEST_CASE("MemoryTracker: Unaligned page region query") {
463 RasterizerInterface rasterizer;
464 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
465 memory_track->UnmarkRegionAsCpuModified(c, WORD);
466 memory_track->MarkRegionAsCpuModified(c + 4000, 1000);
467 REQUIRE(memory_track->IsRegionCpuModified(c, PAGE));
468 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
469 REQUIRE(memory_track->IsRegionCpuModified(c + 4000, 1000));
470 REQUIRE(memory_track->IsRegionCpuModified(c + 4000, 1));
471}
472
473TEST_CASE("MemoryTracker: Cached write") {
474 RasterizerInterface rasterizer;
475 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
476 memory_track->UnmarkRegionAsCpuModified(c, WORD);
477 memory_track->CachedCpuWrite(c + PAGE, c + PAGE);
478 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
479 memory_track->FlushCachedWrites();
480 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
481 memory_track->MarkRegionAsCpuModified(c, WORD);
482 REQUIRE(rasterizer.Count() == 0);
483}
484
485TEST_CASE("MemoryTracker: Multiple cached write") {
486 RasterizerInterface rasterizer;
487 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
488 memory_track->UnmarkRegionAsCpuModified(c, WORD);
489 memory_track->CachedCpuWrite(c + PAGE, PAGE);
490 memory_track->CachedCpuWrite(c + PAGE * 3, PAGE);
491 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
492 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 3, PAGE));
493 memory_track->FlushCachedWrites();
494 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
495 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 3, PAGE));
496 memory_track->MarkRegionAsCpuModified(c, WORD);
497 REQUIRE(rasterizer.Count() == 0);
498}
499
500TEST_CASE("MemoryTracker: Cached write unmarked") {
501 RasterizerInterface rasterizer;
502 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
503 memory_track->UnmarkRegionAsCpuModified(c, WORD);
504 memory_track->CachedCpuWrite(c + PAGE, PAGE);
505 memory_track->UnmarkRegionAsCpuModified(c + PAGE, PAGE);
506 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
507 memory_track->FlushCachedWrites();
508 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
509 memory_track->MarkRegionAsCpuModified(c, WORD);
510 REQUIRE(rasterizer.Count() == 0);
511}
512
513TEST_CASE("MemoryTracker: Cached write iterated") {
514 RasterizerInterface rasterizer;
515 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
516 memory_track->UnmarkRegionAsCpuModified(c, WORD);
517 memory_track->CachedCpuWrite(c + PAGE, PAGE);
518 int num = 0;
519 memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
520 REQUIRE(num == 0);
521 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
522 memory_track->FlushCachedWrites();
523 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
524 memory_track->MarkRegionAsCpuModified(c, WORD);
525 REQUIRE(rasterizer.Count() == 0);
526}
527
528TEST_CASE("MemoryTracker: Cached write downloads") {
529 RasterizerInterface rasterizer;
530 std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(rasterizer));
531 memory_track->UnmarkRegionAsCpuModified(c, WORD);
532 REQUIRE(rasterizer.Count() == 64);
533 memory_track->CachedCpuWrite(c + PAGE, PAGE);
534 REQUIRE(rasterizer.Count() == 63);
535 memory_track->MarkRegionAsGpuModified(c + PAGE, PAGE);
536 int num = 0;
537 memory_track->ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
538 REQUIRE(num == 0);
539 num = 0;
540 memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
541 REQUIRE(num == 0);
542 REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
543 REQUIRE(memory_track->IsRegionGpuModified(c + PAGE, PAGE));
544 memory_track->FlushCachedWrites();
545 REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
546 REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));
547 memory_track->MarkRegionAsCpuModified(c, WORD);
548 REQUIRE(rasterizer.Count() == 0);
549} \ No newline at end of file
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e904573d7..a0009a36f 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -11,8 +11,11 @@ endif()
11 11
12add_library(video_core STATIC 12add_library(video_core STATIC
13 buffer_cache/buffer_base.h 13 buffer_cache/buffer_base.h
14 buffer_cache/buffer_cache_base.h
14 buffer_cache/buffer_cache.cpp 15 buffer_cache/buffer_cache.cpp
15 buffer_cache/buffer_cache.h 16 buffer_cache/buffer_cache.h
17 buffer_cache/memory_tracker_base.h
18 buffer_cache/word_manager.h
16 cache_types.h 19 cache_types.h
17 cdma_pusher.cpp 20 cdma_pusher.cpp
18 cdma_pusher.h 21 cdma_pusher.h
@@ -104,6 +107,7 @@ add_library(video_core STATIC
104 renderer_null/renderer_null.h 107 renderer_null/renderer_null.h
105 renderer_opengl/blit_image.cpp 108 renderer_opengl/blit_image.cpp
106 renderer_opengl/blit_image.h 109 renderer_opengl/blit_image.h
110 renderer_opengl/gl_buffer_cache_base.cpp
107 renderer_opengl/gl_buffer_cache.cpp 111 renderer_opengl/gl_buffer_cache.cpp
108 renderer_opengl/gl_buffer_cache.h 112 renderer_opengl/gl_buffer_cache.h
109 renderer_opengl/gl_compute_pipeline.cpp 113 renderer_opengl/gl_compute_pipeline.cpp
@@ -154,6 +158,7 @@ add_library(video_core STATIC
154 renderer_vulkan/renderer_vulkan.cpp 158 renderer_vulkan/renderer_vulkan.cpp
155 renderer_vulkan/vk_blit_screen.cpp 159 renderer_vulkan/vk_blit_screen.cpp
156 renderer_vulkan/vk_blit_screen.h 160 renderer_vulkan/vk_blit_screen.h
161 renderer_vulkan/vk_buffer_cache_base.cpp
157 renderer_vulkan/vk_buffer_cache.cpp 162 renderer_vulkan/vk_buffer_cache.cpp
158 renderer_vulkan/vk_buffer_cache.h 163 renderer_vulkan/vk_buffer_cache.h
159 renderer_vulkan/vk_command_pool.cpp 164 renderer_vulkan/vk_command_pool.cpp
@@ -174,6 +179,8 @@ add_library(video_core STATIC
174 renderer_vulkan/vk_master_semaphore.h 179 renderer_vulkan/vk_master_semaphore.h
175 renderer_vulkan/vk_pipeline_cache.cpp 180 renderer_vulkan/vk_pipeline_cache.cpp
176 renderer_vulkan/vk_pipeline_cache.h 181 renderer_vulkan/vk_pipeline_cache.h
182 renderer_vulkan/vk_present_manager.cpp
183 renderer_vulkan/vk_present_manager.h
177 renderer_vulkan/vk_query_cache.cpp 184 renderer_vulkan/vk_query_cache.cpp
178 renderer_vulkan/vk_query_cache.h 185 renderer_vulkan/vk_query_cache.h
179 renderer_vulkan/vk_rasterizer.cpp 186 renderer_vulkan/vk_rasterizer.cpp
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index 1b4d63616..0bb3bf8ae 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -1,5 +1,5 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
@@ -11,15 +11,14 @@
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/div_ceil.h" 14#include "video_core/buffer_cache/word_manager.h"
15#include "common/settings.h"
16#include "core/memory.h"
17 15
18namespace VideoCommon { 16namespace VideoCommon {
19 17
20enum class BufferFlagBits { 18enum class BufferFlagBits {
21 Picked = 1 << 0, 19 Picked = 1 << 0,
22 CachedWrites = 1 << 1, 20 CachedWrites = 1 << 1,
21 PreemtiveDownload = 1 << 2,
23}; 22};
24DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits) 23DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
25 24
@@ -36,116 +35,12 @@ struct NullBufferParams {};
36 */ 35 */
37template <class RasterizerInterface> 36template <class RasterizerInterface>
38class BufferBase { 37class BufferBase {
39 static constexpr u64 PAGES_PER_WORD = 64;
40 static constexpr u64 BYTES_PER_PAGE = Core::Memory::YUZU_PAGESIZE;
41 static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
42
43 /// Vector tracking modified pages tightly packed with small vector optimization
44 union WordsArray {
45 /// Returns the pointer to the words state
46 [[nodiscard]] const u64* Pointer(bool is_short) const noexcept {
47 return is_short ? &stack : heap;
48 }
49
50 /// Returns the pointer to the words state
51 [[nodiscard]] u64* Pointer(bool is_short) noexcept {
52 return is_short ? &stack : heap;
53 }
54
55 u64 stack = 0; ///< Small buffers storage
56 u64* heap; ///< Not-small buffers pointer to the storage
57 };
58
59 struct Words {
60 explicit Words() = default;
61 explicit Words(u64 size_bytes_) : size_bytes{size_bytes_} {
62 if (IsShort()) {
63 cpu.stack = ~u64{0};
64 gpu.stack = 0;
65 cached_cpu.stack = 0;
66 untracked.stack = ~u64{0};
67 } else {
68 // Share allocation between CPU and GPU pages and set their default values
69 const size_t num_words = NumWords();
70 u64* const alloc = new u64[num_words * 4];
71 cpu.heap = alloc;
72 gpu.heap = alloc + num_words;
73 cached_cpu.heap = alloc + num_words * 2;
74 untracked.heap = alloc + num_words * 3;
75 std::fill_n(cpu.heap, num_words, ~u64{0});
76 std::fill_n(gpu.heap, num_words, 0);
77 std::fill_n(cached_cpu.heap, num_words, 0);
78 std::fill_n(untracked.heap, num_words, ~u64{0});
79 }
80 // Clean up tailing bits
81 const u64 last_word_size = size_bytes % BYTES_PER_WORD;
82 const u64 last_local_page = Common::DivCeil(last_word_size, BYTES_PER_PAGE);
83 const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD;
84 const u64 last_word = (~u64{0} << shift) >> shift;
85 cpu.Pointer(IsShort())[NumWords() - 1] = last_word;
86 untracked.Pointer(IsShort())[NumWords() - 1] = last_word;
87 }
88
89 ~Words() {
90 Release();
91 }
92
93 Words& operator=(Words&& rhs) noexcept {
94 Release();
95 size_bytes = rhs.size_bytes;
96 cpu = rhs.cpu;
97 gpu = rhs.gpu;
98 cached_cpu = rhs.cached_cpu;
99 untracked = rhs.untracked;
100 rhs.cpu.heap = nullptr;
101 return *this;
102 }
103
104 Words(Words&& rhs) noexcept
105 : size_bytes{rhs.size_bytes}, cpu{rhs.cpu}, gpu{rhs.gpu},
106 cached_cpu{rhs.cached_cpu}, untracked{rhs.untracked} {
107 rhs.cpu.heap = nullptr;
108 }
109
110 Words& operator=(const Words&) = delete;
111 Words(const Words&) = delete;
112
113 /// Returns true when the buffer fits in the small vector optimization
114 [[nodiscard]] bool IsShort() const noexcept {
115 return size_bytes <= BYTES_PER_WORD;
116 }
117
118 /// Returns the number of words of the buffer
119 [[nodiscard]] size_t NumWords() const noexcept {
120 return Common::DivCeil(size_bytes, BYTES_PER_WORD);
121 }
122
123 /// Release buffer resources
124 void Release() {
125 if (!IsShort()) {
126 // CPU written words is the base for the heap allocation
127 delete[] cpu.heap;
128 }
129 }
130
131 u64 size_bytes = 0;
132 WordsArray cpu;
133 WordsArray gpu;
134 WordsArray cached_cpu;
135 WordsArray untracked;
136 };
137
138 enum class Type {
139 CPU,
140 GPU,
141 CachedCPU,
142 Untracked,
143 };
144
145public: 38public:
146 explicit BufferBase(RasterizerInterface& rasterizer_, VAddr cpu_addr_, u64 size_bytes) 39 static constexpr u64 BASE_PAGE_BITS = 16;
147 : rasterizer{&rasterizer_}, cpu_addr{Common::AlignDown(cpu_addr_, BYTES_PER_PAGE)}, 40 static constexpr u64 BASE_PAGE_SIZE = 1ULL << BASE_PAGE_BITS;
148 words(Common::AlignUp(size_bytes + (cpu_addr_ - cpu_addr), BYTES_PER_PAGE)) {} 41
42 explicit BufferBase(RasterizerInterface& rasterizer_, VAddr cpu_addr_, u64 size_bytes_)
43 : cpu_addr{cpu_addr_}, size_bytes{size_bytes_} {}
149 44
150 explicit BufferBase(NullBufferParams) {} 45 explicit BufferBase(NullBufferParams) {}
151 46
@@ -155,105 +50,15 @@ public:
155 BufferBase& operator=(BufferBase&&) = default; 50 BufferBase& operator=(BufferBase&&) = default;
156 BufferBase(BufferBase&&) = default; 51 BufferBase(BufferBase&&) = default;
157 52
158 /// Returns the inclusive CPU modified range in a begin end pair
159 [[nodiscard]] std::pair<u64, u64> ModifiedCpuRegion(VAddr query_cpu_addr,
160 u64 query_size) const noexcept {
161 const u64 offset = query_cpu_addr - cpu_addr;
162 return ModifiedRegion<Type::CPU>(offset, query_size);
163 }
164
165 /// Returns the inclusive GPU modified range in a begin end pair
166 [[nodiscard]] std::pair<u64, u64> ModifiedGpuRegion(VAddr query_cpu_addr,
167 u64 query_size) const noexcept {
168 const u64 offset = query_cpu_addr - cpu_addr;
169 return ModifiedRegion<Type::GPU>(offset, query_size);
170 }
171
172 /// Returns true if a region has been modified from the CPU
173 [[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept {
174 const u64 offset = query_cpu_addr - cpu_addr;
175 return IsRegionModified<Type::CPU>(offset, query_size);
176 }
177
178 /// Returns true if a region has been modified from the GPU
179 [[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept {
180 const u64 offset = query_cpu_addr - cpu_addr;
181 return IsRegionModified<Type::GPU>(offset, query_size);
182 }
183
184 /// Mark region as CPU modified, notifying the rasterizer about this change
185 void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) {
186 ChangeRegionState<Type::CPU, true>(dirty_cpu_addr, size);
187 }
188
189 /// Unmark region as CPU modified, notifying the rasterizer about this change
190 void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) {
191 ChangeRegionState<Type::CPU, false>(dirty_cpu_addr, size);
192 }
193
194 /// Mark region as modified from the host GPU
195 void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept {
196 ChangeRegionState<Type::GPU, true>(dirty_cpu_addr, size);
197 }
198
199 /// Unmark region as modified from the host GPU
200 void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept {
201 ChangeRegionState<Type::GPU, false>(dirty_cpu_addr, size);
202 }
203
204 /// Mark region as modified from the CPU
205 /// but don't mark it as modified until FlusHCachedWrites is called.
206 void CachedCpuWrite(VAddr dirty_cpu_addr, u64 size) {
207 flags |= BufferFlagBits::CachedWrites;
208 ChangeRegionState<Type::CachedCPU, true>(dirty_cpu_addr, size);
209 }
210
211 /// Flushes cached CPU writes, and notify the rasterizer about the deltas
212 void FlushCachedWrites() noexcept {
213 flags &= ~BufferFlagBits::CachedWrites;
214 const u64 num_words = NumWords();
215 u64* const cached_words = Array<Type::CachedCPU>();
216 u64* const untracked_words = Array<Type::Untracked>();
217 u64* const cpu_words = Array<Type::CPU>();
218 for (u64 word_index = 0; word_index < num_words; ++word_index) {
219 const u64 cached_bits = cached_words[word_index];
220 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits);
221 untracked_words[word_index] |= cached_bits;
222 cpu_words[word_index] |= cached_bits;
223 if (!Settings::values.use_pessimistic_flushes) {
224 cached_words[word_index] = 0;
225 }
226 }
227 }
228
229 /// Call 'func' for each CPU modified range and unmark those pages as CPU modified
230 template <typename Func>
231 void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) {
232 ForEachModifiedRange<Type::CPU>(query_cpu_range, size, true, func);
233 }
234
235 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
236 template <typename Func>
237 void ForEachDownloadRange(VAddr query_cpu_range, u64 size, bool clear, Func&& func) {
238 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, clear, func);
239 }
240
241 template <typename Func>
242 void ForEachDownloadRangeAndClear(VAddr query_cpu_range, u64 size, Func&& func) {
243 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, true, func);
244 }
245
246 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
247 template <typename Func>
248 void ForEachDownloadRange(Func&& func) {
249 ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func);
250 }
251
252 /// Mark buffer as picked 53 /// Mark buffer as picked
253 void Pick() noexcept { 54 void Pick() noexcept {
254 flags |= BufferFlagBits::Picked; 55 flags |= BufferFlagBits::Picked;
255 } 56 }
256 57
58 void MarkPreemtiveDownload() noexcept {
59 flags |= BufferFlagBits::PreemtiveDownload;
60 }
61
257 /// Unmark buffer as picked 62 /// Unmark buffer as picked
258 void Unpick() noexcept { 63 void Unpick() noexcept {
259 flags &= ~BufferFlagBits::Picked; 64 flags &= ~BufferFlagBits::Picked;
@@ -284,6 +89,10 @@ public:
284 return True(flags & BufferFlagBits::CachedWrites); 89 return True(flags & BufferFlagBits::CachedWrites);
285 } 90 }
286 91
92 bool IsPreemtiveDownload() const noexcept {
93 return True(flags & BufferFlagBits::PreemtiveDownload);
94 }
95
287 /// Returns the base CPU address of the buffer 96 /// Returns the base CPU address of the buffer
288 [[nodiscard]] VAddr CpuAddr() const noexcept { 97 [[nodiscard]] VAddr CpuAddr() const noexcept {
289 return cpu_addr; 98 return cpu_addr;
@@ -295,11 +104,6 @@ public:
295 return static_cast<u32>(other_cpu_addr - cpu_addr); 104 return static_cast<u32>(other_cpu_addr - cpu_addr);
296 } 105 }
297 106
298 /// Returns the size in bytes of the buffer
299 [[nodiscard]] u64 SizeBytes() const noexcept {
300 return words.size_bytes;
301 }
302
303 size_t getLRUID() const noexcept { 107 size_t getLRUID() const noexcept {
304 return lru_id; 108 return lru_id;
305 } 109 }
@@ -308,305 +112,16 @@ public:
308 lru_id = lru_id_; 112 lru_id = lru_id_;
309 } 113 }
310 114
311private: 115 size_t SizeBytes() const {
312 template <Type type> 116 return size_bytes;
313 u64* Array() noexcept {
314 if constexpr (type == Type::CPU) {
315 return words.cpu.Pointer(IsShort());
316 } else if constexpr (type == Type::GPU) {
317 return words.gpu.Pointer(IsShort());
318 } else if constexpr (type == Type::CachedCPU) {
319 return words.cached_cpu.Pointer(IsShort());
320 } else if constexpr (type == Type::Untracked) {
321 return words.untracked.Pointer(IsShort());
322 }
323 }
324
325 template <Type type>
326 const u64* Array() const noexcept {
327 if constexpr (type == Type::CPU) {
328 return words.cpu.Pointer(IsShort());
329 } else if constexpr (type == Type::GPU) {
330 return words.gpu.Pointer(IsShort());
331 } else if constexpr (type == Type::CachedCPU) {
332 return words.cached_cpu.Pointer(IsShort());
333 } else if constexpr (type == Type::Untracked) {
334 return words.untracked.Pointer(IsShort());
335 }
336 }
337
338 /**
339 * Change the state of a range of pages
340 *
341 * @param dirty_addr Base address to mark or unmark as modified
342 * @param size Size in bytes to mark or unmark as modified
343 */
344 template <Type type, bool enable>
345 void ChangeRegionState(u64 dirty_addr, s64 size) noexcept(type == Type::GPU) {
346 const s64 difference = dirty_addr - cpu_addr;
347 const u64 offset = std::max<s64>(difference, 0);
348 size += std::min<s64>(difference, 0);
349 if (offset >= SizeBytes() || size < 0) {
350 return;
351 }
352 u64* const untracked_words = Array<Type::Untracked>();
353 u64* const state_words = Array<type>();
354 const u64 offset_end = std::min(offset + size, SizeBytes());
355 const u64 begin_page_index = offset / BYTES_PER_PAGE;
356 const u64 begin_word_index = begin_page_index / PAGES_PER_WORD;
357 const u64 end_page_index = Common::DivCeil(offset_end, BYTES_PER_PAGE);
358 const u64 end_word_index = Common::DivCeil(end_page_index, PAGES_PER_WORD);
359 u64 page_index = begin_page_index % PAGES_PER_WORD;
360 u64 word_index = begin_word_index;
361 while (word_index < end_word_index) {
362 const u64 next_word_first_page = (word_index + 1) * PAGES_PER_WORD;
363 const u64 left_offset =
364 std::min(next_word_first_page - end_page_index, PAGES_PER_WORD) % PAGES_PER_WORD;
365 const u64 right_offset = page_index;
366 u64 bits = ~u64{0};
367 bits = (bits >> right_offset) << right_offset;
368 bits = (bits << left_offset) >> left_offset;
369 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
370 NotifyRasterizer<!enable>(word_index, untracked_words[word_index], bits);
371 }
372 if constexpr (enable) {
373 state_words[word_index] |= bits;
374 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
375 untracked_words[word_index] |= bits;
376 }
377 } else {
378 state_words[word_index] &= ~bits;
379 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
380 untracked_words[word_index] &= ~bits;
381 }
382 }
383 page_index = 0;
384 ++word_index;
385 }
386 }
387
388 /**
389 * Notify rasterizer about changes in the CPU tracking state of a word in the buffer
390 *
391 * @param word_index Index to the word to notify to the rasterizer
392 * @param current_bits Current state of the word
393 * @param new_bits New state of the word
394 *
395 * @tparam add_to_rasterizer True when the rasterizer should start tracking the new pages
396 */
397 template <bool add_to_rasterizer>
398 void NotifyRasterizer(u64 word_index, u64 current_bits, u64 new_bits) const {
399 u64 changed_bits = (add_to_rasterizer ? current_bits : ~current_bits) & new_bits;
400 VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
401 while (changed_bits != 0) {
402 const int empty_bits = std::countr_zero(changed_bits);
403 addr += empty_bits * BYTES_PER_PAGE;
404 changed_bits >>= empty_bits;
405
406 const u32 continuous_bits = std::countr_one(changed_bits);
407 const u64 size = continuous_bits * BYTES_PER_PAGE;
408 const VAddr begin_addr = addr;
409 addr += size;
410 changed_bits = continuous_bits < PAGES_PER_WORD ? (changed_bits >> continuous_bits) : 0;
411 rasterizer->UpdatePagesCachedCount(begin_addr, size, add_to_rasterizer ? 1 : -1);
412 }
413 }
414
415 /**
416 * Loop over each page in the given range, turn off those bits and notify the rasterizer if
417 * needed. Call the given function on each turned off range.
418 *
419 * @param query_cpu_range Base CPU address to loop over
420 * @param size Size in bytes of the CPU range to loop over
421 * @param func Function to call for each turned off region
422 */
423 template <Type type, typename Func>
424 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) {
425 static_assert(type != Type::Untracked);
426
427 const s64 difference = query_cpu_range - cpu_addr;
428 const u64 query_begin = std::max<s64>(difference, 0);
429 size += std::min<s64>(difference, 0);
430 if (query_begin >= SizeBytes() || size < 0) {
431 return;
432 }
433 u64* const untracked_words = Array<Type::Untracked>();
434 u64* const state_words = Array<type>();
435 const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes());
436 u64* const words_begin = state_words + query_begin / BYTES_PER_WORD;
437 u64* const words_end = state_words + Common::DivCeil(query_end, BYTES_PER_WORD);
438
439 const auto modified = [](u64 word) { return word != 0; };
440 const auto first_modified_word = std::find_if(words_begin, words_end, modified);
441 if (first_modified_word == words_end) {
442 // Exit early when the buffer is not modified
443 return;
444 }
445 const auto last_modified_word = std::find_if_not(first_modified_word, words_end, modified);
446
447 const u64 word_index_begin = std::distance(state_words, first_modified_word);
448 const u64 word_index_end = std::distance(state_words, last_modified_word);
449
450 const unsigned local_page_begin = std::countr_zero(*first_modified_word);
451 const unsigned local_page_end =
452 static_cast<unsigned>(PAGES_PER_WORD) - std::countl_zero(last_modified_word[-1]);
453 const u64 word_page_begin = word_index_begin * PAGES_PER_WORD;
454 const u64 word_page_end = (word_index_end - 1) * PAGES_PER_WORD;
455 const u64 query_page_begin = query_begin / BYTES_PER_PAGE;
456 const u64 query_page_end = Common::DivCeil(query_end, BYTES_PER_PAGE);
457 const u64 page_index_begin = std::max(word_page_begin + local_page_begin, query_page_begin);
458 const u64 page_index_end = std::min(word_page_end + local_page_end, query_page_end);
459 const u64 first_word_page_begin = page_index_begin % PAGES_PER_WORD;
460 const u64 last_word_page_end = (page_index_end - 1) % PAGES_PER_WORD + 1;
461
462 u64 page_begin = first_word_page_begin;
463 u64 current_base = 0;
464 u64 current_size = 0;
465 bool on_going = false;
466 for (u64 word_index = word_index_begin; word_index < word_index_end; ++word_index) {
467 const bool is_last_word = word_index + 1 == word_index_end;
468 const u64 page_end = is_last_word ? last_word_page_end : PAGES_PER_WORD;
469 const u64 right_offset = page_begin;
470 const u64 left_offset = PAGES_PER_WORD - page_end;
471 u64 bits = ~u64{0};
472 bits = (bits >> right_offset) << right_offset;
473 bits = (bits << left_offset) >> left_offset;
474
475 const u64 current_word = state_words[word_index] & bits;
476 if (clear) {
477 state_words[word_index] &= ~bits;
478 }
479
480 if constexpr (type == Type::CPU) {
481 const u64 current_bits = untracked_words[word_index] & bits;
482 untracked_words[word_index] &= ~bits;
483 NotifyRasterizer<true>(word_index, current_bits, ~u64{0});
484 }
485 // Exclude CPU modified pages when visiting GPU pages
486 const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0);
487 u64 page = page_begin;
488 page_begin = 0;
489
490 while (page < page_end) {
491 const int empty_bits = std::countr_zero(word >> page);
492 if (on_going && empty_bits != 0) {
493 InvokeModifiedRange(func, current_size, current_base);
494 current_size = 0;
495 on_going = false;
496 }
497 if (empty_bits == PAGES_PER_WORD) {
498 break;
499 }
500 page += empty_bits;
501
502 const int continuous_bits = std::countr_one(word >> page);
503 if (!on_going && continuous_bits != 0) {
504 current_base = word_index * PAGES_PER_WORD + page;
505 on_going = true;
506 }
507 current_size += continuous_bits;
508 page += continuous_bits;
509 }
510 }
511 if (on_going && current_size > 0) {
512 InvokeModifiedRange(func, current_size, current_base);
513 }
514 }
515
516 template <typename Func>
517 void InvokeModifiedRange(Func&& func, u64 current_size, u64 current_base) {
518 const u64 current_size_bytes = current_size * BYTES_PER_PAGE;
519 const u64 offset_begin = current_base * BYTES_PER_PAGE;
520 const u64 offset_end = std::min(offset_begin + current_size_bytes, SizeBytes());
521 func(offset_begin, offset_end - offset_begin);
522 }
523
524 /**
525 * Returns true when a region has been modified
526 *
527 * @param offset Offset in bytes from the start of the buffer
528 * @param size Size in bytes of the region to query for modifications
529 */
530 template <Type type>
531 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
532 static_assert(type != Type::Untracked);
533
534 const u64* const untracked_words = Array<Type::Untracked>();
535 const u64* const state_words = Array<type>();
536 const u64 num_query_words = size / BYTES_PER_WORD + 1;
537 const u64 word_begin = offset / BYTES_PER_WORD;
538 const u64 word_end = std::min<u64>(word_begin + num_query_words, NumWords());
539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
542 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
543 const u64 word = state_words[word_index] & ~off_word;
544 if (word == 0) {
545 continue;
546 }
547 const u64 page_end = std::min((word_index + 1) * PAGES_PER_WORD, page_limit);
548 const u64 local_page_end = page_end % PAGES_PER_WORD;
549 const u64 page_end_shift = (PAGES_PER_WORD - local_page_end) % PAGES_PER_WORD;
550 if (((word >> page_index) << page_index) << page_end_shift != 0) {
551 return true;
552 }
553 }
554 return false;
555 }
556
557 /**
558 * Returns a begin end pair with the inclusive modified region
559 *
560 * @param offset Offset in bytes from the start of the buffer
561 * @param size Size in bytes of the region to query for modifications
562 */
563 template <Type type>
564 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
565 static_assert(type != Type::Untracked);
566
567 const u64* const untracked_words = Array<Type::Untracked>();
568 const u64* const state_words = Array<type>();
569 const u64 num_query_words = size / BYTES_PER_WORD + 1;
570 const u64 word_begin = offset / BYTES_PER_WORD;
571 const u64 word_end = std::min<u64>(word_begin + num_query_words, NumWords());
572 const u64 page_base = offset / BYTES_PER_PAGE;
573 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
574 u64 begin = std::numeric_limits<u64>::max();
575 u64 end = 0;
576 for (u64 word_index = word_begin; word_index < word_end; ++word_index) {
577 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
578 const u64 word = state_words[word_index] & ~off_word;
579 if (word == 0) {
580 continue;
581 }
582 const u64 local_page_begin = std::countr_zero(word);
583 const u64 local_page_end = PAGES_PER_WORD - std::countl_zero(word);
584 const u64 page_index = word_index * PAGES_PER_WORD;
585 const u64 page_begin = std::max(page_index + local_page_begin, page_base);
586 const u64 page_end = std::min(page_index + local_page_end, page_limit);
587 begin = std::min(begin, page_begin);
588 end = std::max(end, page_end);
589 }
590 static constexpr std::pair<u64, u64> EMPTY{0, 0};
591 return begin < end ? std::make_pair(begin * BYTES_PER_PAGE, end * BYTES_PER_PAGE) : EMPTY;
592 }
593
594 /// Returns the number of words of the buffer
595 [[nodiscard]] size_t NumWords() const noexcept {
596 return words.NumWords();
597 } 117 }
598 118
599 /// Returns true when the buffer fits in the small vector optimization 119private:
600 [[nodiscard]] bool IsShort() const noexcept {
601 return words.IsShort();
602 }
603
604 RasterizerInterface* rasterizer = nullptr;
605 VAddr cpu_addr = 0; 120 VAddr cpu_addr = 0;
606 Words words;
607 BufferFlagBits flags{}; 121 BufferFlagBits flags{};
608 int stream_score = 0; 122 int stream_score = 0;
609 size_t lru_id = SIZE_MAX; 123 size_t lru_id = SIZE_MAX;
124 size_t size_bytes = 0;
610}; 125};
611 126
612} // namespace VideoCommon 127} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp
index a16308b60..40db243d2 100644
--- a/src/video_core/buffer_cache/buffer_cache.cpp
+++ b/src/video_core/buffer_cache/buffer_cache.cpp
@@ -1,5 +1,5 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "common/microprofile.h" 4#include "common/microprofile.h"
5 5
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index abdc593df..6624919a4 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1,485 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <algorithm> 6#include <algorithm>
7#include <array>
8#include <memory> 7#include <memory>
9#include <mutex>
10#include <numeric> 8#include <numeric>
11#include <span>
12#include <vector>
13
14#include <boost/container/small_vector.hpp>
15#include <boost/icl/interval_set.hpp>
16
17#include "common/common_types.h"
18#include "common/div_ceil.h"
19#include "common/literals.h"
20#include "common/lru_cache.h"
21#include "common/microprofile.h"
22#include "common/polyfill_ranges.h"
23#include "common/scratch_buffer.h"
24#include "common/settings.h"
25#include "core/memory.h"
26#include "video_core/buffer_cache/buffer_base.h"
27#include "video_core/control/channel_state_cache.h"
28#include "video_core/delayed_destruction_ring.h"
29#include "video_core/dirty_flags.h"
30#include "video_core/engines/draw_manager.h"
31#include "video_core/engines/kepler_compute.h"
32#include "video_core/engines/maxwell_3d.h"
33#include "video_core/memory_manager.h"
34#include "video_core/rasterizer_interface.h"
35#include "video_core/surface.h"
36#include "video_core/texture_cache/slot_vector.h"
37#include "video_core/texture_cache/types.h"
38 9
39namespace VideoCommon { 10#include "video_core/buffer_cache/buffer_cache_base.h"
40
41MICROPROFILE_DECLARE(GPU_PrepareBuffers);
42MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
43MICROPROFILE_DECLARE(GPU_DownloadMemory);
44
45using BufferId = SlotId;
46
47using VideoCore::Surface::PixelFormat;
48using namespace Common::Literals;
49
50constexpr u32 NUM_VERTEX_BUFFERS = 32;
51constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4;
52constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18;
53constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
54constexpr u32 NUM_STORAGE_BUFFERS = 16;
55constexpr u32 NUM_TEXTURE_BUFFERS = 16;
56constexpr u32 NUM_STAGES = 5;
57
58enum class ObtainBufferSynchronize : u32 {
59 NoSynchronize = 0,
60 FullSynchronize = 1,
61 SynchronizeNoDirty = 2,
62};
63
64enum class ObtainBufferOperation : u32 {
65 DoNothing = 0,
66 MarkAsWritten = 1,
67 DiscardWrite = 2,
68 MarkQuery = 3,
69};
70
71using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
72using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
73
74template <typename P>
75class BufferCache : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
76
77 // Page size for caching purposes.
78 // This is unrelated to the CPU page size and it can be changed as it seems optimal.
79 static constexpr u32 YUZU_PAGEBITS = 16;
80 static constexpr u64 YUZU_PAGESIZE = u64{1} << YUZU_PAGEBITS;
81
82 static constexpr bool IS_OPENGL = P::IS_OPENGL;
83 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS =
84 P::HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS;
85 static constexpr bool HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT =
86 P::HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT;
87 static constexpr bool NEEDS_BIND_UNIFORM_INDEX = P::NEEDS_BIND_UNIFORM_INDEX;
88 static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX;
89 static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
90 static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
91
92 static constexpr BufferId NULL_BUFFER_ID{0};
93
94 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
95 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
96 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
97
98 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
99
100 using Runtime = typename P::Runtime;
101 using Buffer = typename P::Buffer;
102
103 using IntervalSet = boost::icl::interval_set<VAddr>;
104 using IntervalType = typename IntervalSet::interval_type;
105
106 struct Empty {};
107
108 struct OverlapResult {
109 std::vector<BufferId> ids;
110 VAddr begin;
111 VAddr end;
112 bool has_stream_leap = false;
113 };
114
115 struct Binding {
116 VAddr cpu_addr{};
117 u32 size{};
118 BufferId buffer_id;
119 };
120
121 struct TextureBufferBinding : Binding {
122 PixelFormat format;
123 };
124
125 static constexpr Binding NULL_BINDING{
126 .cpu_addr = 0,
127 .size = 0,
128 .buffer_id = NULL_BUFFER_ID,
129 };
130
131public:
132 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB);
133
134 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
135 Core::Memory::Memory& cpu_memory_, Runtime& runtime_);
136
137 void TickFrame();
138
139 void WriteMemory(VAddr cpu_addr, u64 size);
140
141 void CachedWriteMemory(VAddr cpu_addr, u64 size);
142
143 void DownloadMemory(VAddr cpu_addr, u64 size);
144
145 bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
146
147 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
148
149 void DisableGraphicsUniformBuffer(size_t stage, u32 index);
150
151 void UpdateGraphicsBuffers(bool is_indexed);
152
153 void UpdateComputeBuffers();
154
155 void BindHostGeometryBuffers(bool is_indexed);
156
157 void BindHostStageBuffers(size_t stage);
158
159 void BindHostComputeBuffers();
160
161 void SetUniformBuffersState(const std::array<u32, NUM_STAGES>& mask,
162 const UniformBufferSizes* sizes);
163
164 void SetComputeUniformBufferState(u32 mask, const ComputeUniformBufferSizes* sizes);
165
166 void UnbindGraphicsStorageBuffers(size_t stage);
167
168 void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
169 bool is_written);
170
171 void UnbindGraphicsTextureBuffers(size_t stage);
172
173 void BindGraphicsTextureBuffer(size_t stage, size_t tbo_index, GPUVAddr gpu_addr, u32 size,
174 PixelFormat format, bool is_written, bool is_image);
175
176 void UnbindComputeStorageBuffers();
177
178 void BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
179 bool is_written);
180
181 void UnbindComputeTextureBuffers();
182
183 void BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, PixelFormat format,
184 bool is_written, bool is_image);
185
186 void FlushCachedWrites();
187
188 /// Return true when there are uncommitted buffers to be downloaded
189 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
190
191 void AccumulateFlushes();
192
193 /// Return true when the caller should wait for async downloads
194 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
195
196 /// Commit asynchronous downloads
197 void CommitAsyncFlushes();
198 void CommitAsyncFlushesHigh();
199
200 /// Pop asynchronous downloads
201 void PopAsyncFlushes();
202
203 bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount);
204
205 bool DMAClear(GPUVAddr src_address, u64 amount, u32 value);
206
207 [[nodiscard]] std::pair<Buffer*, u32> ObtainBuffer(GPUVAddr gpu_addr, u32 size,
208 ObtainBufferSynchronize sync_info,
209 ObtainBufferOperation post_op);
210
211 /// Return true when a CPU region is modified from the GPU
212 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
213
214 /// Return true when a region is registered on the cache
215 [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size);
216
217 /// Return true when a CPU region is modified from the CPU
218 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
219
220 void SetDrawIndirect(
221 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
222 current_draw_indirect = current_draw_indirect_;
223 }
224
225 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
226
227 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
228
229 std::recursive_mutex mutex;
230 Runtime& runtime;
231
232private:
233 template <typename Func>
234 static void ForEachEnabledBit(u32 enabled_mask, Func&& func) {
235 for (u32 index = 0; enabled_mask != 0; ++index, enabled_mask >>= 1) {
236 const int disabled_bits = std::countr_zero(enabled_mask);
237 index += disabled_bits;
238 enabled_mask >>= disabled_bits;
239 func(index);
240 }
241 }
242
243 template <typename Func>
244 void ForEachBufferInRange(VAddr cpu_addr, u64 size, Func&& func) {
245 const u64 page_end = Common::DivCeil(cpu_addr + size, YUZU_PAGESIZE);
246 for (u64 page = cpu_addr >> YUZU_PAGEBITS; page < page_end;) {
247 const BufferId buffer_id = page_table[page];
248 if (!buffer_id) {
249 ++page;
250 continue;
251 }
252 Buffer& buffer = slot_buffers[buffer_id];
253 func(buffer_id, buffer);
254
255 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
256 page = Common::DivCeil(end_addr, YUZU_PAGESIZE);
257 }
258 }
259
260 template <typename Func>
261 void ForEachWrittenRange(VAddr cpu_addr, u64 size, Func&& func) {
262 const VAddr start_address = cpu_addr;
263 const VAddr end_address = start_address + size;
264 const VAddr search_base =
265 static_cast<VAddr>(std::min<s64>(0LL, static_cast<s64>(start_address - size)));
266 const IntervalType search_interval{search_base, search_base + 1};
267 auto it = common_ranges.lower_bound(search_interval);
268 if (it == common_ranges.end()) {
269 it = common_ranges.begin();
270 }
271 for (; it != common_ranges.end(); it++) {
272 VAddr inter_addr_end = it->upper();
273 VAddr inter_addr = it->lower();
274 if (inter_addr >= end_address) {
275 break;
276 }
277 if (inter_addr_end <= start_address) {
278 continue;
279 }
280 if (inter_addr_end > end_address) {
281 inter_addr_end = end_address;
282 }
283 if (inter_addr < start_address) {
284 inter_addr = start_address;
285 }
286 func(inter_addr, inter_addr_end);
287 }
288 }
289
290 static bool IsRangeGranular(VAddr cpu_addr, size_t size) {
291 return (cpu_addr & ~Core::Memory::YUZU_PAGEMASK) ==
292 ((cpu_addr + size) & ~Core::Memory::YUZU_PAGEMASK);
293 }
294
295 void RunGarbageCollector();
296
297 void BindHostIndexBuffer();
298
299 void BindHostVertexBuffers();
300
301 void BindHostDrawIndirectBuffers();
302
303 void BindHostGraphicsUniformBuffers(size_t stage);
304
305 void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind);
306
307 void BindHostGraphicsStorageBuffers(size_t stage);
308
309 void BindHostGraphicsTextureBuffers(size_t stage);
310
311 void BindHostTransformFeedbackBuffers();
312
313 void BindHostComputeUniformBuffers();
314
315 void BindHostComputeStorageBuffers();
316
317 void BindHostComputeTextureBuffers();
318
319 void DoUpdateGraphicsBuffers(bool is_indexed);
320
321 void DoUpdateComputeBuffers();
322
323 void UpdateIndexBuffer();
324
325 void UpdateVertexBuffers();
326
327 void UpdateVertexBuffer(u32 index);
328
329 void UpdateDrawIndirect();
330
331 void UpdateUniformBuffers(size_t stage);
332
333 void UpdateStorageBuffers(size_t stage);
334
335 void UpdateTextureBuffers(size_t stage);
336
337 void UpdateTransformFeedbackBuffers();
338
339 void UpdateTransformFeedbackBuffer(u32 index);
340
341 void UpdateComputeUniformBuffers();
342
343 void UpdateComputeStorageBuffers();
344
345 void UpdateComputeTextureBuffers();
346
347 void MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size);
348
349 [[nodiscard]] BufferId FindBuffer(VAddr cpu_addr, u32 size);
350
351 [[nodiscard]] OverlapResult ResolveOverlaps(VAddr cpu_addr, u32 wanted_size);
352
353 void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score);
354
355 [[nodiscard]] BufferId CreateBuffer(VAddr cpu_addr, u32 wanted_size);
356
357 void Register(BufferId buffer_id);
358
359 void Unregister(BufferId buffer_id);
360
361 template <bool insert>
362 void ChangeRegister(BufferId buffer_id);
363
364 void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept;
365
366 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
367
368 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
369
370 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
371 std::span<BufferCopy> copies);
372
373 void ImmediateUploadMemory(Buffer& buffer, u64 largest_copy,
374 std::span<const BufferCopy> copies);
375
376 void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
377
378 void DownloadBufferMemory(Buffer& buffer_id);
379
380 void DownloadBufferMemory(Buffer& buffer_id, VAddr cpu_addr, u64 size);
381
382 void DeleteBuffer(BufferId buffer_id);
383
384 void NotifyBufferDeletion();
385
386 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
387 bool is_written = false) const;
388
389 [[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size,
390 PixelFormat format);
391
392 [[nodiscard]] std::span<const u8> ImmediateBufferWithData(VAddr cpu_addr, size_t size);
393
394 [[nodiscard]] std::span<u8> ImmediateBuffer(size_t wanted_capacity);
395
396 [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
397
398 void ClearDownload(IntervalType subtract_interval);
399
400 VideoCore::RasterizerInterface& rasterizer;
401 Core::Memory::Memory& cpu_memory;
402
403 SlotVector<Buffer> slot_buffers;
404 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
405
406 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
407
408 u32 last_index_count = 0;
409
410 Binding index_buffer;
411 std::array<Binding, NUM_VERTEX_BUFFERS> vertex_buffers;
412 std::array<std::array<Binding, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES> uniform_buffers;
413 std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers;
414 std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers;
415 std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers;
416 Binding count_buffer_binding;
417 Binding indirect_buffer_binding;
418
419 std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers;
420 std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers;
421 std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS> compute_texture_buffers;
422
423 std::array<u32, NUM_STAGES> enabled_uniform_buffer_masks{};
424 u32 enabled_compute_uniform_buffer_mask = 0;
425
426 const UniformBufferSizes* uniform_buffer_sizes{};
427 const ComputeUniformBufferSizes* compute_uniform_buffer_sizes{};
428
429 std::array<u32, NUM_STAGES> enabled_storage_buffers{};
430 std::array<u32, NUM_STAGES> written_storage_buffers{};
431 u32 enabled_compute_storage_buffers = 0;
432 u32 written_compute_storage_buffers = 0;
433
434 std::array<u32, NUM_STAGES> enabled_texture_buffers{};
435 std::array<u32, NUM_STAGES> written_texture_buffers{};
436 std::array<u32, NUM_STAGES> image_texture_buffers{};
437 u32 enabled_compute_texture_buffers = 0;
438 u32 written_compute_texture_buffers = 0;
439 u32 image_compute_texture_buffers = 0;
440
441 std::array<u32, 16> uniform_cache_hits{};
442 std::array<u32, 16> uniform_cache_shots{};
443
444 u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
445
446 bool has_deleted_buffers = false;
447
448 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty>
449 dirty_uniform_buffers{};
450 std::conditional_t<IS_OPENGL, std::array<u32, NUM_STAGES>, Empty> fast_bound_uniform_buffers{};
451 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS,
452 std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>, Empty>
453 uniform_buffer_binding_sizes{};
454 11
455 std::vector<BufferId> cached_write_buffer_ids; 12namespace VideoCommon {
456
457 IntervalSet uncommitted_ranges;
458 IntervalSet common_ranges;
459 std::deque<IntervalSet> committed_ranges;
460
461 Common::ScratchBuffer<u8> immediate_buffer_alloc;
462
463 struct LRUItemParams {
464 using ObjectType = BufferId;
465 using TickType = u64;
466 };
467 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
468 u64 frame_tick = 0;
469 u64 total_used_memory = 0;
470 u64 minimum_memory = 0;
471 u64 critical_memory = 0;
472 13
473 std::array<BufferId, ((1ULL << 39) >> YUZU_PAGEBITS)> page_table; 14using Core::Memory::YUZU_PAGESIZE;
474};
475 15
476template <class P> 16template <class P>
477BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_, 17BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
478 Core::Memory::Memory& cpu_memory_, Runtime& runtime_) 18 Core::Memory::Memory& cpu_memory_, Runtime& runtime_)
479 : runtime{runtime_}, rasterizer{rasterizer_}, cpu_memory{cpu_memory_} { 19 : runtime{runtime_}, rasterizer{rasterizer_}, cpu_memory{cpu_memory_}, memory_tracker{
20 rasterizer} {
480 // Ensure the first slot is used for the null buffer 21 // Ensure the first slot is used for the null buffer
481 void(slot_buffers.insert(runtime, NullBufferParams{})); 22 void(slot_buffers.insert(runtime, NullBufferParams{}));
482 common_ranges.clear(); 23 common_ranges.clear();
24 inline_buffer_id = NULL_BUFFER_ID;
483 25
484 if (!runtime.CanReportMemoryUsage()) { 26 if (!runtime.CanReportMemoryUsage()) {
485 minimum_memory = DEFAULT_EXPECTED_MEMORY; 27 minimum_memory = DEFAULT_EXPECTED_MEMORY;
@@ -543,35 +85,78 @@ void BufferCache<P>::TickFrame() {
543 } 85 }
544 ++frame_tick; 86 ++frame_tick;
545 delayed_destruction_ring.Tick(); 87 delayed_destruction_ring.Tick();
88
89 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
90 for (auto& buffer : async_buffers_death_ring) {
91 runtime.FreeDeferredStagingBuffer(buffer);
92 }
93 async_buffers_death_ring.clear();
94 }
546} 95}
547 96
548template <class P> 97template <class P>
549void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) { 98void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) {
550 ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) { 99 memory_tracker.MarkRegionAsCpuModified(cpu_addr, size);
551 buffer.MarkRegionAsCpuModified(cpu_addr, size); 100 if (memory_tracker.IsRegionGpuModified(cpu_addr, size)) {
552 }); 101 const IntervalType subtract_interval{cpu_addr, cpu_addr + size};
102 ClearDownload(subtract_interval);
103 common_ranges.subtract(subtract_interval);
104 }
553} 105}
554 106
555template <class P> 107template <class P>
556void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) { 108void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
557 ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) { 109 memory_tracker.CachedCpuWrite(cpu_addr, size);
558 if (!buffer.HasCachedWrites()) { 110}
559 cached_write_buffer_ids.push_back(buffer_id); 111
560 } 112template <class P>
561 buffer.CachedCpuWrite(cpu_addr, size); 113std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(VAddr cpu_addr,
562 }); 114 u64 size) {
115 std::optional<VideoCore::RasterizerDownloadArea> area{};
116 area.emplace();
117 VAddr cpu_addr_start_aligned = Common::AlignDown(cpu_addr, Core::Memory::YUZU_PAGESIZE);
118 VAddr cpu_addr_end_aligned = Common::AlignUp(cpu_addr + size, Core::Memory::YUZU_PAGESIZE);
119 area->start_address = cpu_addr_start_aligned;
120 area->end_address = cpu_addr_end_aligned;
121 if (memory_tracker.IsRegionPreflushable(cpu_addr, size)) {
122 area->preemtive = true;
123 return area;
124 };
125 memory_tracker.MarkRegionAsPreflushable(cpu_addr_start_aligned,
126 cpu_addr_end_aligned - cpu_addr_start_aligned);
127 area->preemtive = !IsRegionGpuModified(cpu_addr, size);
128 return area;
563} 129}
564 130
565template <class P> 131template <class P>
566void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) { 132void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
133 WaitOnAsyncFlushes(cpu_addr, size);
567 ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) { 134 ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) {
568 DownloadBufferMemory(buffer, cpu_addr, size); 135 DownloadBufferMemory(buffer, cpu_addr, size);
569 }); 136 });
570} 137}
571 138
572template <class P> 139template <class P>
140void BufferCache<P>::WaitOnAsyncFlushes(VAddr cpu_addr, u64 size) {
141 bool must_wait = false;
142 ForEachInOverlapCounter(async_downloads, cpu_addr, size,
143 [&](VAddr, VAddr, int) { must_wait = true; });
144 bool must_release = false;
145 ForEachInRangeSet(pending_ranges, cpu_addr, size, [&](VAddr, VAddr) { must_release = true; });
146 if (must_release) {
147 std::function<void()> tmp([]() {});
148 rasterizer.SignalFence(std::move(tmp));
149 }
150 if (must_wait || must_release) {
151 rasterizer.ReleaseFences();
152 }
153}
154
155template <class P>
573void BufferCache<P>::ClearDownload(IntervalType subtract_interval) { 156void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
157 RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
574 uncommitted_ranges.subtract(subtract_interval); 158 uncommitted_ranges.subtract(subtract_interval);
159 pending_ranges.subtract(subtract_interval);
575 for (auto& interval_set : committed_ranges) { 160 for (auto& interval_set : committed_ranges) {
576 interval_set.subtract(subtract_interval); 161 interval_set.subtract(subtract_interval);
577 } 162 }
@@ -591,6 +176,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
591 } 176 }
592 177
593 const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount}; 178 const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
179 WaitOnAsyncFlushes(*cpu_src_address, static_cast<u32>(amount));
594 ClearDownload(subtract_interval); 180 ClearDownload(subtract_interval);
595 181
596 BufferId buffer_a; 182 BufferId buffer_a;
@@ -616,10 +202,11 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
616 const VAddr diff = base_address - *cpu_src_address; 202 const VAddr diff = base_address - *cpu_src_address;
617 const VAddr new_base_address = *cpu_dest_address + diff; 203 const VAddr new_base_address = *cpu_dest_address + diff;
618 const IntervalType add_interval{new_base_address, new_base_address + size}; 204 const IntervalType add_interval{new_base_address, new_base_address + size};
619 uncommitted_ranges.add(add_interval);
620 tmp_intervals.push_back(add_interval); 205 tmp_intervals.push_back(add_interval);
206 uncommitted_ranges.add(add_interval);
207 pending_ranges.add(add_interval);
621 }; 208 };
622 ForEachWrittenRange(*cpu_src_address, amount, mirror); 209 ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror);
623 // This subtraction in this order is important for overlapping copies. 210 // This subtraction in this order is important for overlapping copies.
624 common_ranges.subtract(subtract_interval); 211 common_ranges.subtract(subtract_interval);
625 const bool has_new_downloads = tmp_intervals.size() != 0; 212 const bool has_new_downloads = tmp_intervals.size() != 0;
@@ -628,9 +215,9 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
628 } 215 }
629 runtime.CopyBuffer(dest_buffer, src_buffer, copies); 216 runtime.CopyBuffer(dest_buffer, src_buffer, copies);
630 if (has_new_downloads) { 217 if (has_new_downloads) {
631 dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount); 218 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
632 } 219 }
633 std::vector<u8> tmp_buffer(amount); 220 tmp_buffer.resize(amount);
634 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); 221 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
635 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); 222 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
636 return true; 223 return true;
@@ -866,10 +453,7 @@ void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_add
866 453
867template <class P> 454template <class P>
868void BufferCache<P>::FlushCachedWrites() { 455void BufferCache<P>::FlushCachedWrites() {
869 for (const BufferId buffer_id : cached_write_buffer_ids) { 456 memory_tracker.FlushCachedWrites();
870 slot_buffers[buffer_id].FlushCachedWrites();
871 }
872 cached_write_buffer_ids.clear();
873} 457}
874 458
875template <class P> 459template <class P>
@@ -879,10 +463,6 @@ bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
879 463
880template <class P> 464template <class P>
881void BufferCache<P>::AccumulateFlushes() { 465void BufferCache<P>::AccumulateFlushes() {
882 if (Settings::values.gpu_accuracy.GetValue() != Settings::GPUAccuracy::High) {
883 uncommitted_ranges.clear();
884 return;
885 }
886 if (uncommitted_ranges.empty()) { 466 if (uncommitted_ranges.empty()) {
887 return; 467 return;
888 } 468 }
@@ -891,7 +471,11 @@ void BufferCache<P>::AccumulateFlushes() {
891 471
892template <class P> 472template <class P>
893bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { 473bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
894 return false; 474 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
475 return (!async_buffers.empty() && async_buffers.front().has_value());
476 } else {
477 return false;
478 }
895} 479}
896 480
897template <class P> 481template <class P>
@@ -899,12 +483,15 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
899 AccumulateFlushes(); 483 AccumulateFlushes();
900 484
901 if (committed_ranges.empty()) { 485 if (committed_ranges.empty()) {
486 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
487
488 async_buffers.emplace_back(std::optional<Async_Buffer>{});
489 }
902 return; 490 return;
903 } 491 }
904 MICROPROFILE_SCOPE(GPU_DownloadMemory); 492 MICROPROFILE_SCOPE(GPU_DownloadMemory);
905 const bool is_accuracy_normal =
906 Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::Normal;
907 493
494 pending_ranges.clear();
908 auto it = committed_ranges.begin(); 495 auto it = committed_ranges.begin();
909 while (it != committed_ranges.end()) { 496 while (it != committed_ranges.end()) {
910 auto& current_intervals = *it; 497 auto& current_intervals = *it;
@@ -926,11 +513,12 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
926 const std::size_t size = interval.upper() - interval.lower(); 513 const std::size_t size = interval.upper() - interval.lower();
927 const VAddr cpu_addr = interval.lower(); 514 const VAddr cpu_addr = interval.lower();
928 ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) { 515 ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
929 buffer.ForEachDownloadRangeAndClear( 516 const VAddr buffer_start = buffer.CpuAddr();
930 cpu_addr, size, [&](u64 range_offset, u64 range_size) { 517 const VAddr buffer_end = buffer_start + buffer.SizeBytes();
931 if (is_accuracy_normal) { 518 const VAddr new_start = std::max(buffer_start, cpu_addr);
932 return; 519 const VAddr new_end = std::min(buffer_end, cpu_addr + size);
933 } 520 memory_tracker.ForEachDownloadRange(
521 new_start, new_end - new_start, false, [&](u64 cpu_addr_out, u64 range_size) {
934 const VAddr buffer_addr = buffer.CpuAddr(); 522 const VAddr buffer_addr = buffer.CpuAddr();
935 const auto add_download = [&](VAddr start, VAddr end) { 523 const auto add_download = [&](VAddr start, VAddr end) {
936 const u64 new_offset = start - buffer_addr; 524 const u64 new_offset = start - buffer_addr;
@@ -944,92 +532,143 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
944 buffer_id, 532 buffer_id,
945 }); 533 });
946 // Align up to avoid cache conflicts 534 // Align up to avoid cache conflicts
947 constexpr u64 align = 8ULL; 535 constexpr u64 align = 64ULL;
948 constexpr u64 mask = ~(align - 1ULL); 536 constexpr u64 mask = ~(align - 1ULL);
949 total_size_bytes += (new_size + align - 1) & mask; 537 total_size_bytes += (new_size + align - 1) & mask;
950 largest_copy = std::max(largest_copy, new_size); 538 largest_copy = std::max(largest_copy, new_size);
951 }; 539 };
952 540
953 const VAddr start_address = buffer_addr + range_offset; 541 ForEachInRangeSet(common_ranges, cpu_addr_out, range_size, add_download);
954 const VAddr end_address = start_address + range_size;
955 ForEachWrittenRange(start_address, range_size, add_download);
956 const IntervalType subtract_interval{start_address, end_address};
957 common_ranges.subtract(subtract_interval);
958 }); 542 });
959 }); 543 });
960 } 544 }
961 } 545 }
962 committed_ranges.clear(); 546 committed_ranges.clear();
963 if (downloads.empty()) { 547 if (downloads.empty()) {
548 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
549
550 async_buffers.emplace_back(std::optional<Async_Buffer>{});
551 }
964 return; 552 return;
965 } 553 }
966 if constexpr (USE_MEMORY_MAPS) { 554 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
967 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes); 555 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
556 boost::container::small_vector<BufferCopy, 4> normalized_copies;
557 IntervalSet new_async_range{};
968 runtime.PreCopyBarrier(); 558 runtime.PreCopyBarrier();
969 for (auto& [copy, buffer_id] : downloads) { 559 for (auto& [copy, buffer_id] : downloads) {
970 // Have in mind the staging buffer offset for the copy
971 copy.dst_offset += download_staging.offset; 560 copy.dst_offset += download_staging.offset;
972 const std::array copies{copy}; 561 const std::array copies{copy};
973 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, false); 562 BufferCopy second_copy{copy};
563 Buffer& buffer = slot_buffers[buffer_id];
564 second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
565 VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
566 const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
567 async_downloads += std::make_pair(base_interval, 1);
568 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
569 normalized_copies.push_back(second_copy);
974 } 570 }
975 runtime.PostCopyBarrier(); 571 runtime.PostCopyBarrier();
976 runtime.Finish(); 572 pending_downloads.emplace_back(std::move(normalized_copies));
977 for (const auto& [copy, buffer_id] : downloads) { 573 async_buffers.emplace_back(download_staging);
978 const Buffer& buffer = slot_buffers[buffer_id];
979 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
980 // Undo the modified offset
981 const u64 dst_offset = copy.dst_offset - download_staging.offset;
982 const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
983 cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
984 }
985 } else { 574 } else {
986 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy); 575 if (!Settings::IsGPULevelHigh()) {
987 for (const auto& [copy, buffer_id] : downloads) { 576 committed_ranges.clear();
988 Buffer& buffer = slot_buffers[buffer_id]; 577 uncommitted_ranges.clear();
989 buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size)); 578 } else {
990 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset; 579 if constexpr (USE_MEMORY_MAPS) {
991 cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size); 580 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
581 runtime.PreCopyBarrier();
582 for (auto& [copy, buffer_id] : downloads) {
583 // Have in mind the staging buffer offset for the copy
584 copy.dst_offset += download_staging.offset;
585 const std::array copies{copy};
586 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies,
587 false);
588 }
589 runtime.PostCopyBarrier();
590 runtime.Finish();
591 for (const auto& [copy, buffer_id] : downloads) {
592 const Buffer& buffer = slot_buffers[buffer_id];
593 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
594 // Undo the modified offset
595 const u64 dst_offset = copy.dst_offset - download_staging.offset;
596 const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
597 cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
598 }
599 } else {
600 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
601 for (const auto& [copy, buffer_id] : downloads) {
602 Buffer& buffer = slot_buffers[buffer_id];
603 buffer.ImmediateDownload(copy.src_offset,
604 immediate_buffer.subspan(0, copy.size));
605 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
606 cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
607 }
608 }
992 } 609 }
993 } 610 }
994} 611}
995 612
996template <class P> 613template <class P>
997void BufferCache<P>::CommitAsyncFlushes() { 614void BufferCache<P>::CommitAsyncFlushes() {
998 if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) { 615 CommitAsyncFlushesHigh();
999 CommitAsyncFlushesHigh();
1000 } else {
1001 uncommitted_ranges.clear();
1002 committed_ranges.clear();
1003 }
1004} 616}
1005 617
1006template <class P> 618template <class P>
1007void BufferCache<P>::PopAsyncFlushes() {} 619void BufferCache<P>::PopAsyncFlushes() {
620 MICROPROFILE_SCOPE(GPU_DownloadMemory);
621 PopAsyncBuffers();
622}
1008 623
1009template <class P> 624template <class P>
1010bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { 625void BufferCache<P>::PopAsyncBuffers() {
1011 const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE); 626 if (async_buffers.empty()) {
1012 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) { 627 return;
1013 const BufferId image_id = page_table[page]; 628 }
1014 if (!image_id) { 629 if (!async_buffers.front().has_value()) {
1015 ++page; 630 async_buffers.pop_front();
1016 continue; 631 return;
1017 } 632 }
1018 Buffer& buffer = slot_buffers[image_id]; 633 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
1019 if (buffer.IsRegionGpuModified(addr, size)) { 634 auto& downloads = pending_downloads.front();
1020 return true; 635 auto& async_buffer = async_buffers.front();
636 u8* base = async_buffer->mapped_span.data();
637 const size_t base_offset = async_buffer->offset;
638 for (const auto& copy : downloads) {
639 const VAddr cpu_addr = static_cast<VAddr>(copy.src_offset);
640 const u64 dst_offset = copy.dst_offset - base_offset;
641 const u8* read_mapped_memory = base + dst_offset;
642 ForEachInOverlapCounter(
643 async_downloads, cpu_addr, copy.size, [&](VAddr start, VAddr end, int count) {
644 cpu_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - cpu_addr],
645 end - start);
646 if (count == 1) {
647 const IntervalType base_interval{start, end};
648 common_ranges.subtract(base_interval);
649 }
650 });
651 const IntervalType subtract_interval{cpu_addr, cpu_addr + copy.size};
652 RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1);
1021 } 653 }
1022 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); 654 async_buffers_death_ring.emplace_back(*async_buffer);
1023 page = Common::DivCeil(end_addr, YUZU_PAGESIZE); 655 async_buffers.pop_front();
656 pending_downloads.pop_front();
1024 } 657 }
1025 return false; 658}
659
660template <class P>
661bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
662 bool is_dirty = false;
663 ForEachInRangeSet(common_ranges, addr, size, [&](VAddr, VAddr) { is_dirty = true; });
664 return is_dirty;
1026} 665}
1027 666
1028template <class P> 667template <class P>
1029bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) { 668bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) {
1030 const VAddr end_addr = addr + size; 669 const VAddr end_addr = addr + size;
1031 const u64 page_end = Common::DivCeil(end_addr, YUZU_PAGESIZE); 670 const u64 page_end = Common::DivCeil(end_addr, CACHING_PAGESIZE);
1032 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) { 671 for (u64 page = addr >> CACHING_PAGEBITS; page < page_end;) {
1033 const BufferId buffer_id = page_table[page]; 672 const BufferId buffer_id = page_table[page];
1034 if (!buffer_id) { 673 if (!buffer_id) {
1035 ++page; 674 ++page;
@@ -1041,28 +680,14 @@ bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) {
1041 if (buf_start_addr < end_addr && addr < buf_end_addr) { 680 if (buf_start_addr < end_addr && addr < buf_end_addr) {
1042 return true; 681 return true;
1043 } 682 }
1044 page = Common::DivCeil(end_addr, YUZU_PAGESIZE); 683 page = Common::DivCeil(end_addr, CACHING_PAGESIZE);
1045 } 684 }
1046 return false; 685 return false;
1047} 686}
1048 687
1049template <class P> 688template <class P>
1050bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { 689bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
1051 const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE); 690 return memory_tracker.IsRegionCpuModified(addr, size);
1052 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) {
1053 const BufferId image_id = page_table[page];
1054 if (!image_id) {
1055 ++page;
1056 continue;
1057 }
1058 Buffer& buffer = slot_buffers[image_id];
1059 if (buffer.IsRegionCpuModified(addr, size)) {
1060 return true;
1061 }
1062 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
1063 page = Common::DivCeil(end_addr, YUZU_PAGESIZE);
1064 }
1065 return false;
1066} 691}
1067 692
1068template <class P> 693template <class P>
@@ -1072,7 +697,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
1072 const u32 offset = buffer.Offset(index_buffer.cpu_addr); 697 const u32 offset = buffer.Offset(index_buffer.cpu_addr);
1073 const u32 size = index_buffer.size; 698 const u32 size = index_buffer.size;
1074 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 699 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
1075 if (!draw_state.inline_index_draw_indexes.empty()) { 700 if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
1076 if constexpr (USE_MEMORY_MAPS) { 701 if constexpr (USE_MEMORY_MAPS) {
1077 auto upload_staging = runtime.UploadStagingBuffer(size); 702 auto upload_staging = runtime.UploadStagingBuffer(size);
1078 std::array<BufferCopy, 1> copies{ 703 std::array<BufferCopy, 1> copies{
@@ -1155,7 +780,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
1155 TouchBuffer(buffer, binding.buffer_id); 780 TouchBuffer(buffer, binding.buffer_id);
1156 const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID && 781 const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
1157 size <= uniform_buffer_skip_cache_size && 782 size <= uniform_buffer_skip_cache_size &&
1158 !buffer.IsRegionGpuModified(cpu_addr, size); 783 !memory_tracker.IsRegionGpuModified(cpu_addr, size);
1159 if (use_fast_buffer) { 784 if (use_fast_buffer) {
1160 if constexpr (IS_OPENGL) { 785 if constexpr (IS_OPENGL) {
1161 if (runtime.HasFastBufferSubData()) { 786 if (runtime.HasFastBufferSubData()) {
@@ -1378,27 +1003,36 @@ void BufferCache<P>::UpdateIndexBuffer() {
1378 // We have to check for the dirty flags and index count 1003 // We have to check for the dirty flags and index count
1379 // The index count is currently changed without updating the dirty flags 1004 // The index count is currently changed without updating the dirty flags
1380 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 1005 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
1381 const auto& index_array = draw_state.index_buffer; 1006 const auto& index_buffer_ref = draw_state.index_buffer;
1382 auto& flags = maxwell3d->dirty.flags; 1007 auto& flags = maxwell3d->dirty.flags;
1383 if (!flags[Dirty::IndexBuffer]) { 1008 if (!flags[Dirty::IndexBuffer]) {
1384 return; 1009 return;
1385 } 1010 }
1386 flags[Dirty::IndexBuffer] = false; 1011 flags[Dirty::IndexBuffer] = false;
1387 last_index_count = index_array.count; 1012 if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
1388 if (!draw_state.inline_index_draw_indexes.empty()) {
1389 auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size()); 1013 auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size());
1014 u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE);
1015 if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] {
1016 inline_buffer_id = CreateBuffer(0, buffer_size);
1017 }
1018 if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] {
1019 slot_buffers.erase(inline_buffer_id);
1020 inline_buffer_id = CreateBuffer(0, buffer_size);
1021 }
1390 index_buffer = Binding{ 1022 index_buffer = Binding{
1391 .cpu_addr = 0, 1023 .cpu_addr = 0,
1392 .size = inline_index_size, 1024 .size = inline_index_size,
1393 .buffer_id = CreateBuffer(0, inline_index_size), 1025 .buffer_id = inline_buffer_id,
1394 }; 1026 };
1395 return; 1027 return;
1396 } 1028 }
1397 const GPUVAddr gpu_addr_begin = index_array.StartAddress(); 1029
1398 const GPUVAddr gpu_addr_end = index_array.EndAddress(); 1030 const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress();
1031 const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress();
1399 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); 1032 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
1400 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 1033 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
1401 const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes(); 1034 const u32 draw_size =
1035 (index_buffer_ref.count + index_buffer_ref.first) * index_buffer_ref.FormatSizeInBytes();
1402 const u32 size = std::min(address_size, draw_size); 1036 const u32 size = std::min(address_size, draw_size);
1403 if (size == 0 || !cpu_addr) { 1037 if (size == 0 || !cpu_addr) {
1404 index_buffer = NULL_BINDING; 1038 index_buffer = NULL_BINDING;
@@ -1434,17 +1068,15 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1434 const GPUVAddr gpu_addr_begin = array.Address(); 1068 const GPUVAddr gpu_addr_begin = array.Address();
1435 const GPUVAddr gpu_addr_end = limit.Address() + 1; 1069 const GPUVAddr gpu_addr_end = limit.Address() + 1;
1436 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); 1070 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
1437 u32 address_size = static_cast<u32>( 1071 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
1438 std::min(gpu_addr_end - gpu_addr_begin, static_cast<u64>(std::numeric_limits<u32>::max()))); 1072 u32 size = address_size; // TODO: Analyze stride and number of vertices
1439 if (array.enable == 0 || address_size == 0 || !cpu_addr) { 1073 if (array.enable == 0 || size == 0 || !cpu_addr) {
1440 vertex_buffers[index] = NULL_BINDING; 1074 vertex_buffers[index] = NULL_BINDING;
1441 return; 1075 return;
1442 } 1076 }
1443 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { 1077 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
1444 address_size = 1078 size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
1445 static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, address_size));
1446 } 1079 }
1447 const u32 size = address_size; // TODO: Analyze stride and number of vertices
1448 vertex_buffers[index] = Binding{ 1080 vertex_buffers[index] = Binding{
1449 .cpu_addr = *cpu_addr, 1081 .cpu_addr = *cpu_addr,
1450 .size = size, 1082 .size = size,
@@ -1591,17 +1223,16 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
1591 1223
1592template <class P> 1224template <class P>
1593void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) { 1225void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) {
1594 Buffer& buffer = slot_buffers[buffer_id]; 1226 memory_tracker.MarkRegionAsGpuModified(cpu_addr, size);
1595 buffer.MarkRegionAsGpuModified(cpu_addr, size); 1227
1228 if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) {
1229 SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size);
1230 }
1596 1231
1597 const IntervalType base_interval{cpu_addr, cpu_addr + size}; 1232 const IntervalType base_interval{cpu_addr, cpu_addr + size};
1598 common_ranges.add(base_interval); 1233 common_ranges.add(base_interval);
1599
1600 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
1601 if (!is_async) {
1602 return;
1603 }
1604 uncommitted_ranges.add(base_interval); 1234 uncommitted_ranges.add(base_interval);
1235 pending_ranges.add(base_interval);
1605} 1236}
1606 1237
1607template <class P> 1238template <class P>
@@ -1609,7 +1240,7 @@ BufferId BufferCache<P>::FindBuffer(VAddr cpu_addr, u32 size) {
1609 if (cpu_addr == 0) { 1240 if (cpu_addr == 0) {
1610 return NULL_BUFFER_ID; 1241 return NULL_BUFFER_ID;
1611 } 1242 }
1612 const u64 page = cpu_addr >> YUZU_PAGEBITS; 1243 const u64 page = cpu_addr >> CACHING_PAGEBITS;
1613 const BufferId buffer_id = page_table[page]; 1244 const BufferId buffer_id = page_table[page];
1614 if (!buffer_id) { 1245 if (!buffer_id) {
1615 return CreateBuffer(cpu_addr, size); 1246 return CreateBuffer(cpu_addr, size);
@@ -1638,9 +1269,9 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
1638 .has_stream_leap = has_stream_leap, 1269 .has_stream_leap = has_stream_leap,
1639 }; 1270 };
1640 } 1271 }
1641 for (; cpu_addr >> YUZU_PAGEBITS < Common::DivCeil(end, YUZU_PAGESIZE); 1272 for (; cpu_addr >> CACHING_PAGEBITS < Common::DivCeil(end, CACHING_PAGESIZE);
1642 cpu_addr += YUZU_PAGESIZE) { 1273 cpu_addr += CACHING_PAGESIZE) {
1643 const BufferId overlap_id = page_table[cpu_addr >> YUZU_PAGEBITS]; 1274 const BufferId overlap_id = page_table[cpu_addr >> CACHING_PAGEBITS];
1644 if (!overlap_id) { 1275 if (!overlap_id) {
1645 continue; 1276 continue;
1646 } 1277 }
@@ -1666,11 +1297,11 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
1666 // as a stream buffer. Increase the size to skip constantly recreating buffers. 1297 // as a stream buffer. Increase the size to skip constantly recreating buffers.
1667 has_stream_leap = true; 1298 has_stream_leap = true;
1668 if (expands_right) { 1299 if (expands_right) {
1669 begin -= YUZU_PAGESIZE * 256; 1300 begin -= CACHING_PAGESIZE * 256;
1670 cpu_addr = begin; 1301 cpu_addr = begin;
1671 } 1302 }
1672 if (expands_left) { 1303 if (expands_left) {
1673 end += YUZU_PAGESIZE * 256; 1304 end += CACHING_PAGESIZE * 256;
1674 } 1305 }
1675 } 1306 }
1676 } 1307 }
@@ -1690,25 +1321,22 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
1690 if (accumulate_stream_score) { 1321 if (accumulate_stream_score) {
1691 new_buffer.IncreaseStreamScore(overlap.StreamScore() + 1); 1322 new_buffer.IncreaseStreamScore(overlap.StreamScore() + 1);
1692 } 1323 }
1693 std::vector<BufferCopy> copies; 1324 boost::container::small_vector<BufferCopy, 1> copies;
1694 const size_t dst_base_offset = overlap.CpuAddr() - new_buffer.CpuAddr(); 1325 const size_t dst_base_offset = overlap.CpuAddr() - new_buffer.CpuAddr();
1695 overlap.ForEachDownloadRange([&](u64 begin, u64 range_size) { 1326 copies.push_back(BufferCopy{
1696 copies.push_back(BufferCopy{ 1327 .src_offset = 0,
1697 .src_offset = begin, 1328 .dst_offset = dst_base_offset,
1698 .dst_offset = dst_base_offset + begin, 1329 .size = overlap.SizeBytes(),
1699 .size = range_size,
1700 });
1701 new_buffer.UnmarkRegionAsCpuModified(begin, range_size);
1702 new_buffer.MarkRegionAsGpuModified(begin, range_size);
1703 }); 1330 });
1704 if (!copies.empty()) { 1331 runtime.CopyBuffer(new_buffer, overlap, copies);
1705 runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies); 1332 DeleteBuffer(overlap_id, true);
1706 }
1707 DeleteBuffer(overlap_id);
1708} 1333}
1709 1334
1710template <class P> 1335template <class P>
1711BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { 1336BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
1337 VAddr cpu_addr_end = Common::AlignUp(cpu_addr + wanted_size, CACHING_PAGESIZE);
1338 cpu_addr = Common::AlignDown(cpu_addr, CACHING_PAGESIZE);
1339 wanted_size = static_cast<u32>(cpu_addr_end - cpu_addr);
1712 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size); 1340 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
1713 const u32 size = static_cast<u32>(overlap.end - overlap.begin); 1341 const u32 size = static_cast<u32>(overlap.end - overlap.begin);
1714 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); 1342 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
@@ -1718,7 +1346,7 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
1718 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); 1346 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
1719 } 1347 }
1720 Register(new_buffer_id); 1348 Register(new_buffer_id);
1721 TouchBuffer(slot_buffers[new_buffer_id], new_buffer_id); 1349 TouchBuffer(new_buffer, new_buffer_id);
1722 return new_buffer_id; 1350 return new_buffer_id;
1723} 1351}
1724 1352
@@ -1746,8 +1374,8 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
1746 } 1374 }
1747 const VAddr cpu_addr_begin = buffer.CpuAddr(); 1375 const VAddr cpu_addr_begin = buffer.CpuAddr();
1748 const VAddr cpu_addr_end = cpu_addr_begin + size; 1376 const VAddr cpu_addr_end = cpu_addr_begin + size;
1749 const u64 page_begin = cpu_addr_begin / YUZU_PAGESIZE; 1377 const u64 page_begin = cpu_addr_begin / CACHING_PAGESIZE;
1750 const u64 page_end = Common::DivCeil(cpu_addr_end, YUZU_PAGESIZE); 1378 const u64 page_end = Common::DivCeil(cpu_addr_end, CACHING_PAGESIZE);
1751 for (u64 page = page_begin; page != page_end; ++page) { 1379 for (u64 page = page_begin; page != page_end; ++page) {
1752 if constexpr (insert) { 1380 if constexpr (insert) {
1753 page_table[page] = buffer_id; 1381 page_table[page] = buffer_id;
@@ -1766,9 +1394,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
1766 1394
1767template <class P> 1395template <class P>
1768bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { 1396bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
1769 if (buffer.CpuAddr() == 0) {
1770 return true;
1771 }
1772 return SynchronizeBufferImpl(buffer, cpu_addr, size); 1397 return SynchronizeBufferImpl(buffer, cpu_addr, size);
1773} 1398}
1774 1399
@@ -1777,10 +1402,11 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
1777 boost::container::small_vector<BufferCopy, 4> copies; 1402 boost::container::small_vector<BufferCopy, 4> copies;
1778 u64 total_size_bytes = 0; 1403 u64 total_size_bytes = 0;
1779 u64 largest_copy = 0; 1404 u64 largest_copy = 0;
1780 buffer.ForEachUploadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) { 1405 VAddr buffer_start = buffer.CpuAddr();
1406 memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
1781 copies.push_back(BufferCopy{ 1407 copies.push_back(BufferCopy{
1782 .src_offset = total_size_bytes, 1408 .src_offset = total_size_bytes,
1783 .dst_offset = range_offset, 1409 .dst_offset = cpu_addr_out - buffer_start,
1784 .size = range_size, 1410 .size = range_size,
1785 }); 1411 });
1786 total_size_bytes += range_size; 1412 total_size_bytes += range_size;
@@ -1795,6 +1421,51 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
1795} 1421}
1796 1422
1797template <class P> 1423template <class P>
1424bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) {
1425 boost::container::small_vector<BufferCopy, 4> copies;
1426 u64 total_size_bytes = 0;
1427 u64 largest_copy = 0;
1428 IntervalSet found_sets{};
1429 auto make_copies = [&] {
1430 for (auto& interval : found_sets) {
1431 const std::size_t sub_size = interval.upper() - interval.lower();
1432 const VAddr cpu_addr_ = interval.lower();
1433 copies.push_back(BufferCopy{
1434 .src_offset = total_size_bytes,
1435 .dst_offset = cpu_addr_ - buffer.CpuAddr(),
1436 .size = sub_size,
1437 });
1438 total_size_bytes += sub_size;
1439 largest_copy = std::max<u64>(largest_copy, sub_size);
1440 }
1441 const std::span<BufferCopy> copies_span(copies.data(), copies.size());
1442 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
1443 };
1444 memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
1445 const VAddr base_adr = cpu_addr_out;
1446 const VAddr end_adr = base_adr + range_size;
1447 const IntervalType add_interval{base_adr, end_adr};
1448 found_sets.add(add_interval);
1449 });
1450 if (found_sets.empty()) {
1451 return true;
1452 }
1453 const IntervalType search_interval{cpu_addr, cpu_addr + size};
1454 auto it = common_ranges.lower_bound(search_interval);
1455 auto it_end = common_ranges.upper_bound(search_interval);
1456 if (it == common_ranges.end()) {
1457 make_copies();
1458 return false;
1459 }
1460 while (it != it_end) {
1461 found_sets.subtract(*it);
1462 it++;
1463 }
1464 make_copies();
1465 return false;
1466}
1467
1468template <class P>
1798void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, 1469void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
1799 std::span<BufferCopy> copies) { 1470 std::span<BufferCopy> copies) {
1800 if constexpr (USE_MEMORY_MAPS) { 1471 if constexpr (USE_MEMORY_MAPS) {
@@ -1805,39 +1476,45 @@ void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 larg
1805} 1476}
1806 1477
1807template <class P> 1478template <class P>
1808void BufferCache<P>::ImmediateUploadMemory(Buffer& buffer, u64 largest_copy, 1479void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
1809 std::span<const BufferCopy> copies) { 1480 [[maybe_unused]] u64 largest_copy,
1810 std::span<u8> immediate_buffer; 1481 [[maybe_unused]] std::span<const BufferCopy> copies) {
1811 for (const BufferCopy& copy : copies) { 1482 if constexpr (!USE_MEMORY_MAPS) {
1812 std::span<const u8> upload_span; 1483 std::span<u8> immediate_buffer;
1813 const VAddr cpu_addr = buffer.CpuAddr() + copy.dst_offset; 1484 for (const BufferCopy& copy : copies) {
1814 if (IsRangeGranular(cpu_addr, copy.size)) { 1485 std::span<const u8> upload_span;
1815 upload_span = std::span(cpu_memory.GetPointer(cpu_addr), copy.size); 1486 const VAddr cpu_addr = buffer.CpuAddr() + copy.dst_offset;
1816 } else { 1487 if (IsRangeGranular(cpu_addr, copy.size)) {
1817 if (immediate_buffer.empty()) { 1488 upload_span = std::span(cpu_memory.GetPointer(cpu_addr), copy.size);
1818 immediate_buffer = ImmediateBuffer(largest_copy); 1489 } else {
1490 if (immediate_buffer.empty()) {
1491 immediate_buffer = ImmediateBuffer(largest_copy);
1492 }
1493 cpu_memory.ReadBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
1494 upload_span = immediate_buffer.subspan(0, copy.size);
1819 } 1495 }
1820 cpu_memory.ReadBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size); 1496 buffer.ImmediateUpload(copy.dst_offset, upload_span);
1821 upload_span = immediate_buffer.subspan(0, copy.size);
1822 } 1497 }
1823 buffer.ImmediateUpload(copy.dst_offset, upload_span);
1824 } 1498 }
1825} 1499}
1826 1500
1827template <class P> 1501template <class P>
1828void BufferCache<P>::MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, 1502void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
1829 std::span<BufferCopy> copies) { 1503 [[maybe_unused]] u64 total_size_bytes,
1830 auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes); 1504 [[maybe_unused]] std::span<BufferCopy> copies) {
1831 const std::span<u8> staging_pointer = upload_staging.mapped_span; 1505 if constexpr (USE_MEMORY_MAPS) {
1832 for (BufferCopy& copy : copies) { 1506 auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
1833 u8* const src_pointer = staging_pointer.data() + copy.src_offset; 1507 const std::span<u8> staging_pointer = upload_staging.mapped_span;
1834 const VAddr cpu_addr = buffer.CpuAddr() + copy.dst_offset; 1508 for (BufferCopy& copy : copies) {
1835 cpu_memory.ReadBlockUnsafe(cpu_addr, src_pointer, copy.size); 1509 u8* const src_pointer = staging_pointer.data() + copy.src_offset;
1510 const VAddr cpu_addr = buffer.CpuAddr() + copy.dst_offset;
1511 cpu_memory.ReadBlockUnsafe(cpu_addr, src_pointer, copy.size);
1836 1512
1837 // Apply the staging offset 1513 // Apply the staging offset
1838 copy.src_offset += upload_staging.offset; 1514 copy.src_offset += upload_staging.offset;
1515 }
1516 runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
1839 } 1517 }
1840 runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
1841} 1518}
1842 1519
1843template <class P> 1520template <class P>
@@ -1847,7 +1524,9 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
1847 if (!is_dirty) { 1524 if (!is_dirty) {
1848 return false; 1525 return false;
1849 } 1526 }
1850 if (!IsRegionGpuModified(dest_address, copy_size)) { 1527 VAddr aligned_start = Common::AlignDown(dest_address, YUZU_PAGESIZE);
1528 VAddr aligned_end = Common::AlignUp(dest_address + copy_size, YUZU_PAGESIZE);
1529 if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) {
1851 return false; 1530 return false;
1852 } 1531 }
1853 1532
@@ -1886,30 +1565,31 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1886 boost::container::small_vector<BufferCopy, 1> copies; 1565 boost::container::small_vector<BufferCopy, 1> copies;
1887 u64 total_size_bytes = 0; 1566 u64 total_size_bytes = 0;
1888 u64 largest_copy = 0; 1567 u64 largest_copy = 0;
1889 buffer.ForEachDownloadRangeAndClear(cpu_addr, size, [&](u64 range_offset, u64 range_size) { 1568 memory_tracker.ForEachDownloadRangeAndClear(
1890 const VAddr buffer_addr = buffer.CpuAddr(); 1569 cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
1891 const auto add_download = [&](VAddr start, VAddr end) { 1570 const VAddr buffer_addr = buffer.CpuAddr();
1892 const u64 new_offset = start - buffer_addr; 1571 const auto add_download = [&](VAddr start, VAddr end) {
1893 const u64 new_size = end - start; 1572 const u64 new_offset = start - buffer_addr;
1894 copies.push_back(BufferCopy{ 1573 const u64 new_size = end - start;
1895 .src_offset = new_offset, 1574 copies.push_back(BufferCopy{
1896 .dst_offset = total_size_bytes, 1575 .src_offset = new_offset,
1897 .size = new_size, 1576 .dst_offset = total_size_bytes,
1898 }); 1577 .size = new_size,
1899 // Align up to avoid cache conflicts 1578 });
1900 constexpr u64 align = 256ULL; 1579 // Align up to avoid cache conflicts
1901 constexpr u64 mask = ~(align - 1ULL); 1580 constexpr u64 align = 64ULL;
1902 total_size_bytes += (new_size + align - 1) & mask; 1581 constexpr u64 mask = ~(align - 1ULL);
1903 largest_copy = std::max(largest_copy, new_size); 1582 total_size_bytes += (new_size + align - 1) & mask;
1904 }; 1583 largest_copy = std::max(largest_copy, new_size);
1905 1584 };
1906 const VAddr start_address = buffer_addr + range_offset; 1585
1907 const VAddr end_address = start_address + range_size; 1586 const VAddr start_address = cpu_addr_out;
1908 ForEachWrittenRange(start_address, range_size, add_download); 1587 const VAddr end_address = start_address + range_size;
1909 const IntervalType subtract_interval{start_address, end_address}; 1588 ForEachInRangeSet(common_ranges, start_address, range_size, add_download);
1910 ClearDownload(subtract_interval); 1589 const IntervalType subtract_interval{start_address, end_address};
1911 common_ranges.subtract(subtract_interval); 1590 ClearDownload(subtract_interval);
1912 }); 1591 common_ranges.subtract(subtract_interval);
1592 });
1913 if (total_size_bytes == 0) { 1593 if (total_size_bytes == 0) {
1914 return; 1594 return;
1915 } 1595 }
@@ -1943,7 +1623,7 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1943} 1623}
1944 1624
1945template <class P> 1625template <class P>
1946void BufferCache<P>::DeleteBuffer(BufferId buffer_id) { 1626void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
1947 const auto scalar_replace = [buffer_id](Binding& binding) { 1627 const auto scalar_replace = [buffer_id](Binding& binding) {
1948 if (binding.buffer_id == buffer_id) { 1628 if (binding.buffer_id == buffer_id) {
1949 binding.buffer_id = BufferId{}; 1629 binding.buffer_id = BufferId{};
@@ -1959,11 +1639,12 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
1959 replace(transform_feedback_buffers); 1639 replace(transform_feedback_buffers);
1960 replace(compute_uniform_buffers); 1640 replace(compute_uniform_buffers);
1961 replace(compute_storage_buffers); 1641 replace(compute_storage_buffers);
1962 std::erase(cached_write_buffer_ids, buffer_id);
1963 1642
1964 // Mark the whole buffer as CPU written to stop tracking CPU writes 1643 // Mark the whole buffer as CPU written to stop tracking CPU writes
1965 Buffer& buffer = slot_buffers[buffer_id]; 1644 if (!do_not_mark) {
1966 buffer.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes()); 1645 Buffer& buffer = slot_buffers[buffer_id];
1646 memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes());
1647 }
1967 1648
1968 Unregister(buffer_id); 1649 Unregister(buffer_id);
1969 delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id])); 1650 delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
@@ -2011,7 +1692,7 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
2011 LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); 1692 LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
2012 return NULL_BINDING; 1693 return NULL_BINDING;
2013 } 1694 }
2014 const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); 1695 const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE);
2015 const Binding binding{ 1696 const Binding binding{
2016 .cpu_addr = *cpu_addr, 1697 .cpu_addr = *cpu_addr,
2017 .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), 1698 .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr),
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
new file mode 100644
index 000000000..0445ec47f
--- /dev/null
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -0,0 +1,579 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <numeric>
12#include <span>
13#include <unordered_map>
14#include <vector>
15
16#include <boost/container/small_vector.hpp>
17#define BOOST_NO_MT
18#include <boost/pool/detail/mutex.hpp>
19#undef BOOST_NO_MT
20#include <boost/icl/interval.hpp>
21#include <boost/icl/interval_base_set.hpp>
22#include <boost/icl/interval_set.hpp>
23#include <boost/icl/split_interval_map.hpp>
24#include <boost/pool/pool.hpp>
25#include <boost/pool/pool_alloc.hpp>
26#include <boost/pool/poolfwd.hpp>
27
28#include "common/common_types.h"
29#include "common/div_ceil.h"
30#include "common/literals.h"
31#include "common/lru_cache.h"
32#include "common/microprofile.h"
33#include "common/scope_exit.h"
34#include "common/settings.h"
35#include "core/memory.h"
36#include "video_core/buffer_cache/buffer_base.h"
37#include "video_core/control/channel_state_cache.h"
38#include "video_core/delayed_destruction_ring.h"
39#include "video_core/dirty_flags.h"
40#include "video_core/engines/draw_manager.h"
41#include "video_core/engines/kepler_compute.h"
42#include "video_core/engines/maxwell_3d.h"
43#include "video_core/memory_manager.h"
44#include "video_core/rasterizer_interface.h"
45#include "video_core/surface.h"
46#include "video_core/texture_cache/slot_vector.h"
47#include "video_core/texture_cache/types.h"
48
49namespace boost {
50template <typename T>
51class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
52}
53
54namespace VideoCommon {
55
56MICROPROFILE_DECLARE(GPU_PrepareBuffers);
57MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
58MICROPROFILE_DECLARE(GPU_DownloadMemory);
59
60using BufferId = SlotId;
61
62using VideoCore::Surface::PixelFormat;
63using namespace Common::Literals;
64
65constexpr u32 NUM_VERTEX_BUFFERS = 32;
66constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4;
67constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18;
68constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
69constexpr u32 NUM_STORAGE_BUFFERS = 16;
70constexpr u32 NUM_TEXTURE_BUFFERS = 16;
71constexpr u32 NUM_STAGES = 5;
72
73using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
74using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
75
76enum class ObtainBufferSynchronize : u32 {
77 NoSynchronize = 0,
78 FullSynchronize = 1,
79 SynchronizeNoDirty = 2,
80};
81
82enum class ObtainBufferOperation : u32 {
83 DoNothing = 0,
84 MarkAsWritten = 1,
85 DiscardWrite = 2,
86 MarkQuery = 3,
87};
88
89template <typename P>
90class BufferCache : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
91 // Page size for caching purposes.
92 // This is unrelated to the CPU page size and it can be changed as it seems optimal.
93 static constexpr u32 CACHING_PAGEBITS = 16;
94 static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS;
95
96 static constexpr bool IS_OPENGL = P::IS_OPENGL;
97 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS =
98 P::HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS;
99 static constexpr bool HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT =
100 P::HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT;
101 static constexpr bool NEEDS_BIND_UNIFORM_INDEX = P::NEEDS_BIND_UNIFORM_INDEX;
102 static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX;
103 static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
104 static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
105 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
106
107 static constexpr BufferId NULL_BUFFER_ID{0};
108
109 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
110 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
111 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
112
113 // Debug Flags.
114
115 static constexpr bool DISABLE_DOWNLOADS = true;
116
117 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
118
119 using Runtime = typename P::Runtime;
120 using Buffer = typename P::Buffer;
121 using Async_Buffer = typename P::Async_Buffer;
122 using MemoryTracker = typename P::MemoryTracker;
123
124 using IntervalCompare = std::less<VAddr>;
125 using IntervalInstance = boost::icl::interval_type_default<VAddr, std::less>;
126 using IntervalAllocator = boost::fast_pool_allocator<VAddr>;
127 using IntervalSet = boost::icl::interval_set<VAddr>;
128 using IntervalType = typename IntervalSet::interval_type;
129
130 template <typename Type>
131 struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
132 // types
133 typedef counter_add_functor<Type> type;
134 typedef boost::icl::identity_based_inplace_combine<Type> base_type;
135
136 // public member functions
137 void operator()(Type& current, const Type& added) const {
138 current += added;
139 if (current < base_type::identity_element()) {
140 current = base_type::identity_element();
141 }
142 }
143
144 // public static functions
145 static void version(Type&){};
146 };
147
148 using OverlapCombine = counter_add_functor<int>;
149 using OverlapSection = boost::icl::inter_section<int>;
150 using OverlapCounter = boost::icl::split_interval_map<VAddr, int>;
151
152 struct Empty {};
153
154 struct OverlapResult {
155 std::vector<BufferId> ids;
156 VAddr begin;
157 VAddr end;
158 bool has_stream_leap = false;
159 };
160
161 struct Binding {
162 VAddr cpu_addr{};
163 u32 size{};
164 BufferId buffer_id;
165 };
166
167 struct TextureBufferBinding : Binding {
168 PixelFormat format;
169 };
170
171 static constexpr Binding NULL_BINDING{
172 .cpu_addr = 0,
173 .size = 0,
174 .buffer_id = NULL_BUFFER_ID,
175 };
176
177public:
178 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB);
179
180 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
181 Core::Memory::Memory& cpu_memory_, Runtime& runtime_);
182
183 void TickFrame();
184
185 void WriteMemory(VAddr cpu_addr, u64 size);
186
187 void CachedWriteMemory(VAddr cpu_addr, u64 size);
188
189 void DownloadMemory(VAddr cpu_addr, u64 size);
190
191 std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size);
192
193 bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
194
195 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
196
197 void DisableGraphicsUniformBuffer(size_t stage, u32 index);
198
199 void UpdateGraphicsBuffers(bool is_indexed);
200
201 void UpdateComputeBuffers();
202
203 void BindHostGeometryBuffers(bool is_indexed);
204
205 void BindHostStageBuffers(size_t stage);
206
207 void BindHostComputeBuffers();
208
209 void SetUniformBuffersState(const std::array<u32, NUM_STAGES>& mask,
210 const UniformBufferSizes* sizes);
211
212 void SetComputeUniformBufferState(u32 mask, const ComputeUniformBufferSizes* sizes);
213
214 void UnbindGraphicsStorageBuffers(size_t stage);
215
216 void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
217 bool is_written);
218
219 void UnbindGraphicsTextureBuffers(size_t stage);
220
221 void BindGraphicsTextureBuffer(size_t stage, size_t tbo_index, GPUVAddr gpu_addr, u32 size,
222 PixelFormat format, bool is_written, bool is_image);
223
224 void UnbindComputeStorageBuffers();
225
226 void BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
227 bool is_written);
228
229 void UnbindComputeTextureBuffers();
230
231 void BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, PixelFormat format,
232 bool is_written, bool is_image);
233
234 [[nodiscard]] std::pair<Buffer*, u32> ObtainBuffer(GPUVAddr gpu_addr, u32 size,
235 ObtainBufferSynchronize sync_info,
236 ObtainBufferOperation post_op);
237 void FlushCachedWrites();
238
239 /// Return true when there are uncommitted buffers to be downloaded
240 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
241
242 void AccumulateFlushes();
243
244 /// Return true when the caller should wait for async downloads
245 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
246
247 /// Commit asynchronous downloads
248 void CommitAsyncFlushes();
249 void CommitAsyncFlushesHigh();
250
251 /// Pop asynchronous downloads
252 void PopAsyncFlushes();
253 void PopAsyncBuffers();
254
255 bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount);
256
257 bool DMAClear(GPUVAddr src_address, u64 amount, u32 value);
258
259 /// Return true when a CPU region is modified from the GPU
260 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
261
262 /// Return true when a region is registered on the cache
263 [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size);
264
265 /// Return true when a CPU region is modified from the CPU
266 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
267
268 void SetDrawIndirect(
269 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
270 current_draw_indirect = current_draw_indirect_;
271 }
272
273 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
274
275 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
276
277 std::recursive_mutex mutex;
278 Runtime& runtime;
279
280private:
281 template <typename Func>
282 static void ForEachEnabledBit(u32 enabled_mask, Func&& func) {
283 for (u32 index = 0; enabled_mask != 0; ++index, enabled_mask >>= 1) {
284 const int disabled_bits = std::countr_zero(enabled_mask);
285 index += disabled_bits;
286 enabled_mask >>= disabled_bits;
287 func(index);
288 }
289 }
290
291 template <typename Func>
292 void ForEachBufferInRange(VAddr cpu_addr, u64 size, Func&& func) {
293 const u64 page_end = Common::DivCeil(cpu_addr + size, CACHING_PAGESIZE);
294 for (u64 page = cpu_addr >> CACHING_PAGEBITS; page < page_end;) {
295 const BufferId buffer_id = page_table[page];
296 if (!buffer_id) {
297 ++page;
298 continue;
299 }
300 Buffer& buffer = slot_buffers[buffer_id];
301 func(buffer_id, buffer);
302
303 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
304 page = Common::DivCeil(end_addr, CACHING_PAGESIZE);
305 }
306 }
307
308 template <typename Func>
309 void ForEachInRangeSet(IntervalSet& current_range, VAddr cpu_addr, u64 size, Func&& func) {
310 const VAddr start_address = cpu_addr;
311 const VAddr end_address = start_address + size;
312 const IntervalType search_interval{start_address, end_address};
313 auto it = current_range.lower_bound(search_interval);
314 if (it == current_range.end()) {
315 return;
316 }
317 auto end_it = current_range.upper_bound(search_interval);
318 for (; it != end_it; it++) {
319 VAddr inter_addr_end = it->upper();
320 VAddr inter_addr = it->lower();
321 if (inter_addr_end > end_address) {
322 inter_addr_end = end_address;
323 }
324 if (inter_addr < start_address) {
325 inter_addr = start_address;
326 }
327 func(inter_addr, inter_addr_end);
328 }
329 }
330
331 template <typename Func>
332 void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
333 Func&& func) {
334 const VAddr start_address = cpu_addr;
335 const VAddr end_address = start_address + size;
336 const IntervalType search_interval{start_address, end_address};
337 auto it = current_range.lower_bound(search_interval);
338 if (it == current_range.end()) {
339 return;
340 }
341 auto end_it = current_range.upper_bound(search_interval);
342 for (; it != end_it; it++) {
343 auto& inter = it->first;
344 VAddr inter_addr_end = inter.upper();
345 VAddr inter_addr = inter.lower();
346 if (inter_addr_end > end_address) {
347 inter_addr_end = end_address;
348 }
349 if (inter_addr < start_address) {
350 inter_addr = start_address;
351 }
352 func(inter_addr, inter_addr_end, it->second);
353 }
354 }
355
356 void RemoveEachInOverlapCounter(OverlapCounter& current_range,
357 const IntervalType search_interval, int subtract_value) {
358 bool any_removals = false;
359 current_range.add(std::make_pair(search_interval, subtract_value));
360 do {
361 any_removals = false;
362 auto it = current_range.lower_bound(search_interval);
363 if (it == current_range.end()) {
364 return;
365 }
366 auto end_it = current_range.upper_bound(search_interval);
367 for (; it != end_it; it++) {
368 if (it->second <= 0) {
369 any_removals = true;
370 current_range.erase(it);
371 break;
372 }
373 }
374 } while (any_removals);
375 }
376
377 static bool IsRangeGranular(VAddr cpu_addr, size_t size) {
378 return (cpu_addr & ~Core::Memory::YUZU_PAGEMASK) ==
379 ((cpu_addr + size) & ~Core::Memory::YUZU_PAGEMASK);
380 }
381
382 void RunGarbageCollector();
383
384 void WaitOnAsyncFlushes(VAddr cpu_addr, u64 size);
385
386 void BindHostIndexBuffer();
387
388 void BindHostVertexBuffers();
389
390 void BindHostDrawIndirectBuffers();
391
392 void BindHostGraphicsUniformBuffers(size_t stage);
393
394 void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind);
395
396 void BindHostGraphicsStorageBuffers(size_t stage);
397
398 void BindHostGraphicsTextureBuffers(size_t stage);
399
400 void BindHostTransformFeedbackBuffers();
401
402 void BindHostComputeUniformBuffers();
403
404 void BindHostComputeStorageBuffers();
405
406 void BindHostComputeTextureBuffers();
407
408 void DoUpdateGraphicsBuffers(bool is_indexed);
409
410 void DoUpdateComputeBuffers();
411
412 void UpdateIndexBuffer();
413
414 void UpdateVertexBuffers();
415
416 void UpdateVertexBuffer(u32 index);
417
418 void UpdateDrawIndirect();
419
420 void UpdateUniformBuffers(size_t stage);
421
422 void UpdateStorageBuffers(size_t stage);
423
424 void UpdateTextureBuffers(size_t stage);
425
426 void UpdateTransformFeedbackBuffers();
427
428 void UpdateTransformFeedbackBuffer(u32 index);
429
430 void UpdateComputeUniformBuffers();
431
432 void UpdateComputeStorageBuffers();
433
434 void UpdateComputeTextureBuffers();
435
436 void MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size);
437
438 [[nodiscard]] BufferId FindBuffer(VAddr cpu_addr, u32 size);
439
440 [[nodiscard]] OverlapResult ResolveOverlaps(VAddr cpu_addr, u32 wanted_size);
441
442 void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score);
443
444 [[nodiscard]] BufferId CreateBuffer(VAddr cpu_addr, u32 wanted_size);
445
446 void Register(BufferId buffer_id);
447
448 void Unregister(BufferId buffer_id);
449
450 template <bool insert>
451 void ChangeRegister(BufferId buffer_id);
452
453 void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept;
454
455 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
456
457 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
458
459 bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size);
460
461 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
462 std::span<BufferCopy> copies);
463
464 void ImmediateUploadMemory(Buffer& buffer, u64 largest_copy,
465 std::span<const BufferCopy> copies);
466
467 void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
468
469 void DownloadBufferMemory(Buffer& buffer_id);
470
471 void DownloadBufferMemory(Buffer& buffer_id, VAddr cpu_addr, u64 size);
472
473 void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false);
474
475 void NotifyBufferDeletion();
476
477 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
478 bool is_written) const;
479
480 [[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size,
481 PixelFormat format);
482
483 [[nodiscard]] std::span<const u8> ImmediateBufferWithData(VAddr cpu_addr, size_t size);
484
485 [[nodiscard]] std::span<u8> ImmediateBuffer(size_t wanted_capacity);
486
487 [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
488
489 void ClearDownload(IntervalType subtract_interval);
490
491 VideoCore::RasterizerInterface& rasterizer;
492 Core::Memory::Memory& cpu_memory;
493
494 SlotVector<Buffer> slot_buffers;
495 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
496
497 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
498
499 u32 last_index_count = 0;
500
501 Binding index_buffer;
502 std::array<Binding, NUM_VERTEX_BUFFERS> vertex_buffers;
503 std::array<std::array<Binding, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES> uniform_buffers;
504 std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers;
505 std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers;
506 std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers;
507 Binding count_buffer_binding;
508 Binding indirect_buffer_binding;
509
510 std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers;
511 std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers;
512 std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS> compute_texture_buffers;
513
514 std::array<u32, NUM_STAGES> enabled_uniform_buffer_masks{};
515 u32 enabled_compute_uniform_buffer_mask = 0;
516
517 const UniformBufferSizes* uniform_buffer_sizes{};
518 const ComputeUniformBufferSizes* compute_uniform_buffer_sizes{};
519
520 std::array<u32, NUM_STAGES> enabled_storage_buffers{};
521 std::array<u32, NUM_STAGES> written_storage_buffers{};
522 u32 enabled_compute_storage_buffers = 0;
523 u32 written_compute_storage_buffers = 0;
524
525 std::array<u32, NUM_STAGES> enabled_texture_buffers{};
526 std::array<u32, NUM_STAGES> written_texture_buffers{};
527 std::array<u32, NUM_STAGES> image_texture_buffers{};
528 u32 enabled_compute_texture_buffers = 0;
529 u32 written_compute_texture_buffers = 0;
530 u32 image_compute_texture_buffers = 0;
531
532 std::array<u32, 16> uniform_cache_hits{};
533 std::array<u32, 16> uniform_cache_shots{};
534
535 u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
536
537 bool has_deleted_buffers = false;
538
539 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty>
540 dirty_uniform_buffers{};
541 std::conditional_t<IS_OPENGL, std::array<u32, NUM_STAGES>, Empty> fast_bound_uniform_buffers{};
542 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS,
543 std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>, Empty>
544 uniform_buffer_binding_sizes{};
545
546 MemoryTracker memory_tracker;
547 IntervalSet uncommitted_ranges;
548 IntervalSet common_ranges;
549 IntervalSet cached_ranges;
550 IntervalSet pending_ranges;
551 std::deque<IntervalSet> committed_ranges;
552
553 // Async Buffers
554 OverlapCounter async_downloads;
555 std::deque<std::optional<Async_Buffer>> async_buffers;
556 std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
557 std::optional<Async_Buffer> current_buffer;
558
559 std::deque<Async_Buffer> async_buffers_death_ring;
560
561 size_t immediate_buffer_capacity = 0;
562 Common::ScratchBuffer<u8> immediate_buffer_alloc;
563
564 struct LRUItemParams {
565 using ObjectType = BufferId;
566 using TickType = u64;
567 };
568 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
569 u64 frame_tick = 0;
570 u64 total_used_memory = 0;
571 u64 minimum_memory = 0;
572 u64 critical_memory = 0;
573 BufferId inline_buffer_id;
574
575 std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table;
576 std::vector<u8> tmp_buffer;
577};
578
579} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h
new file mode 100644
index 000000000..6036b21c9
--- /dev/null
+++ b/src/video_core/buffer_cache/memory_tracker_base.h
@@ -0,0 +1,299 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <bit>
8#include <deque>
9#include <limits>
10#include <type_traits>
11#include <unordered_set>
12#include <utility>
13
14#include "common/alignment.h"
15#include "common/common_types.h"
16#include "video_core/buffer_cache/word_manager.h"
17
18namespace VideoCommon {
19
20template <class RasterizerInterface>
21class MemoryTrackerBase {
22 static constexpr size_t MAX_CPU_PAGE_BITS = 39;
23 static constexpr size_t HIGHER_PAGE_BITS = 22;
24 static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS;
25 static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL;
26 static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - HIGHER_PAGE_BITS);
27 static constexpr size_t MANAGER_POOL_SIZE = 32;
28 static constexpr size_t WORDS_STACK_NEEDED = HIGHER_PAGE_SIZE / BYTES_PER_WORD;
29 using Manager = WordManager<RasterizerInterface, WORDS_STACK_NEEDED>;
30
31public:
32 MemoryTrackerBase(RasterizerInterface& rasterizer_) : rasterizer{&rasterizer_} {}
33 ~MemoryTrackerBase() = default;
34
35 /// Returns the inclusive CPU modified range in a begin end pair
36 [[nodiscard]] std::pair<u64, u64> ModifiedCpuRegion(VAddr query_cpu_addr,
37 u64 query_size) noexcept {
38 return IteratePairs<true>(
39 query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
40 return manager->template ModifiedRegion<Type::CPU>(offset, size);
41 });
42 }
43
44 /// Returns the inclusive GPU modified range in a begin end pair
45 [[nodiscard]] std::pair<u64, u64> ModifiedGpuRegion(VAddr query_cpu_addr,
46 u64 query_size) noexcept {
47 return IteratePairs<false>(
48 query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
49 return manager->template ModifiedRegion<Type::GPU>(offset, size);
50 });
51 }
52
53 /// Returns true if a region has been modified from the CPU
54 [[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept {
55 return IteratePages<true>(
56 query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
57 return manager->template IsRegionModified<Type::CPU>(offset, size);
58 });
59 }
60
61 /// Returns true if a region has been modified from the GPU
62 [[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) noexcept {
63 return IteratePages<false>(
64 query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
65 return manager->template IsRegionModified<Type::GPU>(offset, size);
66 });
67 }
68
69 /// Returns true if a region has been marked as Preflushable
70 [[nodiscard]] bool IsRegionPreflushable(VAddr query_cpu_addr, u64 query_size) noexcept {
71 return IteratePages<false>(
72 query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
73 return manager->template IsRegionModified<Type::Preflushable>(offset, size);
74 });
75 }
76
77 /// Mark region as CPU modified, notifying the rasterizer about this change
78 void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
79 IteratePages<true>(dirty_cpu_addr, query_size,
80 [](Manager* manager, u64 offset, size_t size) {
81 manager->template ChangeRegionState<Type::CPU, true>(
82 manager->GetCpuAddr() + offset, size);
83 });
84 }
85
86 /// Unmark region as CPU modified, notifying the rasterizer about this change
87 void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
88 IteratePages<true>(dirty_cpu_addr, query_size,
89 [](Manager* manager, u64 offset, size_t size) {
90 manager->template ChangeRegionState<Type::CPU, false>(
91 manager->GetCpuAddr() + offset, size);
92 });
93 }
94
95 /// Mark region as modified from the host GPU
96 void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept {
97 IteratePages<true>(dirty_cpu_addr, query_size,
98 [](Manager* manager, u64 offset, size_t size) {
99 manager->template ChangeRegionState<Type::GPU, true>(
100 manager->GetCpuAddr() + offset, size);
101 });
102 }
103
104 /// Mark region as modified from the host GPU
105 void MarkRegionAsPreflushable(VAddr dirty_cpu_addr, u64 query_size) noexcept {
106 IteratePages<true>(dirty_cpu_addr, query_size,
107 [](Manager* manager, u64 offset, size_t size) {
108 manager->template ChangeRegionState<Type::Preflushable, true>(
109 manager->GetCpuAddr() + offset, size);
110 });
111 }
112
113 /// Unmark region as modified from the host GPU
114 void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept {
115 IteratePages<true>(dirty_cpu_addr, query_size,
116 [](Manager* manager, u64 offset, size_t size) {
117 manager->template ChangeRegionState<Type::GPU, false>(
118 manager->GetCpuAddr() + offset, size);
119 });
120 }
121
122 /// Unmark region as modified from the host GPU
123 void UnmarkRegionAsPreflushable(VAddr dirty_cpu_addr, u64 query_size) noexcept {
124 IteratePages<true>(dirty_cpu_addr, query_size,
125 [](Manager* manager, u64 offset, size_t size) {
126 manager->template ChangeRegionState<Type::Preflushable, false>(
127 manager->GetCpuAddr() + offset, size);
128 });
129 }
130
131 /// Mark region as modified from the CPU
132 /// but don't mark it as modified until FlusHCachedWrites is called.
133 void CachedCpuWrite(VAddr dirty_cpu_addr, u64 query_size) {
134 IteratePages<true>(
135 dirty_cpu_addr, query_size, [this](Manager* manager, u64 offset, size_t size) {
136 const VAddr cpu_address = manager->GetCpuAddr() + offset;
137 manager->template ChangeRegionState<Type::CachedCPU, true>(cpu_address, size);
138 cached_pages.insert(static_cast<u32>(cpu_address >> HIGHER_PAGE_BITS));
139 });
140 }
141
142 /// Flushes cached CPU writes, and notify the rasterizer about the deltas
143 void FlushCachedWrites(VAddr query_cpu_addr, u64 query_size) noexcept {
144 IteratePages<false>(query_cpu_addr, query_size,
145 [](Manager* manager, [[maybe_unused]] u64 offset,
146 [[maybe_unused]] size_t size) { manager->FlushCachedWrites(); });
147 }
148
149 void FlushCachedWrites() noexcept {
150 for (auto id : cached_pages) {
151 top_tier[id]->FlushCachedWrites();
152 }
153 cached_pages.clear();
154 }
155
156 /// Call 'func' for each CPU modified range and unmark those pages as CPU modified
157 template <typename Func>
158 void ForEachUploadRange(VAddr query_cpu_range, u64 query_size, Func&& func) {
159 IteratePages<true>(query_cpu_range, query_size,
160 [&func](Manager* manager, u64 offset, size_t size) {
161 manager->template ForEachModifiedRange<Type::CPU, true>(
162 manager->GetCpuAddr() + offset, size, func);
163 });
164 }
165
166 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
167 template <typename Func>
168 void ForEachDownloadRange(VAddr query_cpu_range, u64 query_size, bool clear, Func&& func) {
169 IteratePages<false>(query_cpu_range, query_size,
170 [&func, clear](Manager* manager, u64 offset, size_t size) {
171 if (clear) {
172 manager->template ForEachModifiedRange<Type::GPU, true>(
173 manager->GetCpuAddr() + offset, size, func);
174 } else {
175 manager->template ForEachModifiedRange<Type::GPU, false>(
176 manager->GetCpuAddr() + offset, size, func);
177 }
178 });
179 }
180
181 template <typename Func>
182 void ForEachDownloadRangeAndClear(VAddr query_cpu_range, u64 query_size, Func&& func) {
183 IteratePages<false>(query_cpu_range, query_size,
184 [&func](Manager* manager, u64 offset, size_t size) {
185 manager->template ForEachModifiedRange<Type::GPU, true>(
186 manager->GetCpuAddr() + offset, size, func);
187 });
188 }
189
190private:
191 template <bool create_region_on_fail, typename Func>
192 bool IteratePages(VAddr cpu_address, size_t size, Func&& func) {
193 using FuncReturn = typename std::invoke_result<Func, Manager*, u64, size_t>::type;
194 static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
195 std::size_t remaining_size{size};
196 std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
197 u64 page_offset{cpu_address & HIGHER_PAGE_MASK};
198 while (remaining_size > 0) {
199 const std::size_t copy_amount{
200 std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
201 auto* manager{top_tier[page_index]};
202 if (manager) {
203 if constexpr (BOOL_BREAK) {
204 if (func(manager, page_offset, copy_amount)) {
205 return true;
206 }
207 } else {
208 func(manager, page_offset, copy_amount);
209 }
210 } else if constexpr (create_region_on_fail) {
211 CreateRegion(page_index);
212 manager = top_tier[page_index];
213 if constexpr (BOOL_BREAK) {
214 if (func(manager, page_offset, copy_amount)) {
215 return true;
216 }
217 } else {
218 func(manager, page_offset, copy_amount);
219 }
220 }
221 page_index++;
222 page_offset = 0;
223 remaining_size -= copy_amount;
224 }
225 return false;
226 }
227
228 template <bool create_region_on_fail, typename Func>
229 std::pair<u64, u64> IteratePairs(VAddr cpu_address, size_t size, Func&& func) {
230 std::size_t remaining_size{size};
231 std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
232 u64 page_offset{cpu_address & HIGHER_PAGE_MASK};
233 u64 begin = std::numeric_limits<u64>::max();
234 u64 end = 0;
235 while (remaining_size > 0) {
236 const std::size_t copy_amount{
237 std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
238 auto* manager{top_tier[page_index]};
239 const auto execute = [&] {
240 auto [new_begin, new_end] = func(manager, page_offset, copy_amount);
241 if (new_begin != 0 || new_end != 0) {
242 const u64 base_address = page_index << HIGHER_PAGE_BITS;
243 begin = std::min(new_begin + base_address, begin);
244 end = std::max(new_end + base_address, end);
245 }
246 };
247 if (manager) {
248 execute();
249 } else if constexpr (create_region_on_fail) {
250 CreateRegion(page_index);
251 manager = top_tier[page_index];
252 execute();
253 }
254 page_index++;
255 page_offset = 0;
256 remaining_size -= copy_amount;
257 }
258 if (begin < end) {
259 return std::make_pair(begin, end);
260 } else {
261 return std::make_pair(0ULL, 0ULL);
262 }
263 }
264
265 void CreateRegion(std::size_t page_index) {
266 const VAddr base_cpu_addr = page_index << HIGHER_PAGE_BITS;
267 top_tier[page_index] = GetNewManager(base_cpu_addr);
268 }
269
270 Manager* GetNewManager(VAddr base_cpu_addess) {
271 const auto on_return = [&] {
272 auto* new_manager = free_managers.front();
273 new_manager->SetCpuAddress(base_cpu_addess);
274 free_managers.pop_front();
275 return new_manager;
276 };
277 if (!free_managers.empty()) {
278 return on_return();
279 }
280 manager_pool.emplace_back();
281 auto& last_pool = manager_pool.back();
282 for (size_t i = 0; i < MANAGER_POOL_SIZE; i++) {
283 new (&last_pool[i]) Manager(0, *rasterizer, HIGHER_PAGE_SIZE);
284 free_managers.push_back(&last_pool[i]);
285 }
286 return on_return();
287 }
288
289 std::deque<std::array<Manager, MANAGER_POOL_SIZE>> manager_pool;
290 std::deque<Manager*> free_managers;
291
292 std::array<Manager*, NUM_HIGH_PAGES> top_tier{};
293
294 std::unordered_set<u32> cached_pages;
295
296 RasterizerInterface* rasterizer = nullptr;
297};
298
299} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h
new file mode 100644
index 000000000..a336bde41
--- /dev/null
+++ b/src/video_core/buffer_cache/word_manager.h
@@ -0,0 +1,485 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <bit>
8#include <limits>
9#include <span>
10#include <utility>
11
12#include "common/alignment.h"
13#include "common/common_funcs.h"
14#include "common/common_types.h"
15#include "common/div_ceil.h"
16#include "core/memory.h"
17
18namespace VideoCommon {
19
20constexpr u64 PAGES_PER_WORD = 64;
21constexpr u64 BYTES_PER_PAGE = Core::Memory::YUZU_PAGESIZE;
22constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
23
24enum class Type {
25 CPU,
26 GPU,
27 CachedCPU,
28 Untracked,
29 Preflushable,
30};
31
32/// Vector tracking modified pages tightly packed with small vector optimization
33template <size_t stack_words = 1>
34struct WordsArray {
35 /// Returns the pointer to the words state
36 [[nodiscard]] const u64* Pointer(bool is_short) const noexcept {
37 return is_short ? stack.data() : heap;
38 }
39
40 /// Returns the pointer to the words state
41 [[nodiscard]] u64* Pointer(bool is_short) noexcept {
42 return is_short ? stack.data() : heap;
43 }
44
45 std::array<u64, stack_words> stack{}; ///< Small buffers storage
46 u64* heap; ///< Not-small buffers pointer to the storage
47};
48
49template <size_t stack_words = 1>
50struct Words {
51 explicit Words() = default;
52 explicit Words(u64 size_bytes_) : size_bytes{size_bytes_} {
53 num_words = Common::DivCeil(size_bytes, BYTES_PER_WORD);
54 if (IsShort()) {
55 cpu.stack.fill(~u64{0});
56 gpu.stack.fill(0);
57 cached_cpu.stack.fill(0);
58 untracked.stack.fill(~u64{0});
59 preflushable.stack.fill(0);
60 } else {
61 // Share allocation between CPU and GPU pages and set their default values
62 u64* const alloc = new u64[num_words * 5];
63 cpu.heap = alloc;
64 gpu.heap = alloc + num_words;
65 cached_cpu.heap = alloc + num_words * 2;
66 untracked.heap = alloc + num_words * 3;
67 preflushable.heap = alloc + num_words * 4;
68 std::fill_n(cpu.heap, num_words, ~u64{0});
69 std::fill_n(gpu.heap, num_words, 0);
70 std::fill_n(cached_cpu.heap, num_words, 0);
71 std::fill_n(untracked.heap, num_words, ~u64{0});
72 std::fill_n(preflushable.heap, num_words, 0);
73 }
74 // Clean up tailing bits
75 const u64 last_word_size = size_bytes % BYTES_PER_WORD;
76 const u64 last_local_page = Common::DivCeil(last_word_size, BYTES_PER_PAGE);
77 const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD;
78 const u64 last_word = (~u64{0} << shift) >> shift;
79 cpu.Pointer(IsShort())[NumWords() - 1] = last_word;
80 untracked.Pointer(IsShort())[NumWords() - 1] = last_word;
81 }
82
83 ~Words() {
84 Release();
85 }
86
87 Words& operator=(Words&& rhs) noexcept {
88 Release();
89 size_bytes = rhs.size_bytes;
90 num_words = rhs.num_words;
91 cpu = rhs.cpu;
92 gpu = rhs.gpu;
93 cached_cpu = rhs.cached_cpu;
94 untracked = rhs.untracked;
95 preflushable = rhs.preflushable;
96 rhs.cpu.heap = nullptr;
97 return *this;
98 }
99
100 Words(Words&& rhs) noexcept
101 : size_bytes{rhs.size_bytes}, num_words{rhs.num_words}, cpu{rhs.cpu}, gpu{rhs.gpu},
102 cached_cpu{rhs.cached_cpu}, untracked{rhs.untracked}, preflushable{rhs.preflushable} {
103 rhs.cpu.heap = nullptr;
104 }
105
106 Words& operator=(const Words&) = delete;
107 Words(const Words&) = delete;
108
109 /// Returns true when the buffer fits in the small vector optimization
110 [[nodiscard]] bool IsShort() const noexcept {
111 return num_words <= stack_words;
112 }
113
114 /// Returns the number of words of the buffer
115 [[nodiscard]] size_t NumWords() const noexcept {
116 return num_words;
117 }
118
119 /// Release buffer resources
120 void Release() {
121 if (!IsShort()) {
122 // CPU written words is the base for the heap allocation
123 delete[] cpu.heap;
124 }
125 }
126
127 template <Type type>
128 std::span<u64> Span() noexcept {
129 if constexpr (type == Type::CPU) {
130 return std::span<u64>(cpu.Pointer(IsShort()), num_words);
131 } else if constexpr (type == Type::GPU) {
132 return std::span<u64>(gpu.Pointer(IsShort()), num_words);
133 } else if constexpr (type == Type::CachedCPU) {
134 return std::span<u64>(cached_cpu.Pointer(IsShort()), num_words);
135 } else if constexpr (type == Type::Untracked) {
136 return std::span<u64>(untracked.Pointer(IsShort()), num_words);
137 } else if constexpr (type == Type::Preflushable) {
138 return std::span<u64>(preflushable.Pointer(IsShort()), num_words);
139 }
140 }
141
142 template <Type type>
143 std::span<const u64> Span() const noexcept {
144 if constexpr (type == Type::CPU) {
145 return std::span<const u64>(cpu.Pointer(IsShort()), num_words);
146 } else if constexpr (type == Type::GPU) {
147 return std::span<const u64>(gpu.Pointer(IsShort()), num_words);
148 } else if constexpr (type == Type::CachedCPU) {
149 return std::span<const u64>(cached_cpu.Pointer(IsShort()), num_words);
150 } else if constexpr (type == Type::Untracked) {
151 return std::span<const u64>(untracked.Pointer(IsShort()), num_words);
152 } else if constexpr (type == Type::Preflushable) {
153 return std::span<const u64>(preflushable.Pointer(IsShort()), num_words);
154 }
155 }
156
157 u64 size_bytes = 0;
158 size_t num_words = 0;
159 WordsArray<stack_words> cpu;
160 WordsArray<stack_words> gpu;
161 WordsArray<stack_words> cached_cpu;
162 WordsArray<stack_words> untracked;
163 WordsArray<stack_words> preflushable;
164};
165
166template <class RasterizerInterface, size_t stack_words = 1>
167class WordManager {
168public:
169 explicit WordManager(VAddr cpu_addr_, RasterizerInterface& rasterizer_, u64 size_bytes)
170 : cpu_addr{cpu_addr_}, rasterizer{&rasterizer_}, words{size_bytes} {}
171
172 explicit WordManager() = default;
173
174 void SetCpuAddress(VAddr new_cpu_addr) {
175 cpu_addr = new_cpu_addr;
176 }
177
178 VAddr GetCpuAddr() const {
179 return cpu_addr;
180 }
181
182 static u64 ExtractBits(u64 word, size_t page_start, size_t page_end) {
183 constexpr size_t number_bits = sizeof(u64) * 8;
184 const size_t limit_page_end = number_bits - std::min(page_end, number_bits);
185 u64 bits = (word >> page_start) << page_start;
186 bits = (bits << limit_page_end) >> limit_page_end;
187 return bits;
188 }
189
190 static std::pair<size_t, size_t> GetWordPage(VAddr address) {
191 const size_t converted_address = static_cast<size_t>(address);
192 const size_t word_number = converted_address / BYTES_PER_WORD;
193 const size_t amount_pages = converted_address % BYTES_PER_WORD;
194 return std::make_pair(word_number, amount_pages / BYTES_PER_PAGE);
195 }
196
197 template <typename Func>
198 void IterateWords(size_t offset, size_t size, Func&& func) const {
199 using FuncReturn = std::invoke_result_t<Func, std::size_t, u64>;
200 static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
201 const size_t start = static_cast<size_t>(std::max<s64>(static_cast<s64>(offset), 0LL));
202 const size_t end = static_cast<size_t>(std::max<s64>(static_cast<s64>(offset + size), 0LL));
203 if (start >= SizeBytes() || end <= start) {
204 return;
205 }
206 auto [start_word, start_page] = GetWordPage(start);
207 auto [end_word, end_page] = GetWordPage(end + BYTES_PER_PAGE - 1ULL);
208 const size_t num_words = NumWords();
209 start_word = std::min(start_word, num_words);
210 end_word = std::min(end_word, num_words);
211 const size_t diff = end_word - start_word;
212 end_word += (end_page + PAGES_PER_WORD - 1ULL) / PAGES_PER_WORD;
213 end_word = std::min(end_word, num_words);
214 end_page += diff * PAGES_PER_WORD;
215 constexpr u64 base_mask{~0ULL};
216 for (size_t word_index = start_word; word_index < end_word; word_index++) {
217 const u64 mask = ExtractBits(base_mask, start_page, end_page);
218 start_page = 0;
219 end_page -= PAGES_PER_WORD;
220 if constexpr (BOOL_BREAK) {
221 if (func(word_index, mask)) {
222 return;
223 }
224 } else {
225 func(word_index, mask);
226 }
227 }
228 }
229
230 template <typename Func>
231 void IteratePages(u64 mask, Func&& func) const {
232 size_t offset = 0;
233 while (mask != 0) {
234 const size_t empty_bits = std::countr_zero(mask);
235 offset += empty_bits;
236 mask = mask >> empty_bits;
237
238 const size_t continuous_bits = std::countr_one(mask);
239 func(offset, continuous_bits);
240 mask = continuous_bits < PAGES_PER_WORD ? (mask >> continuous_bits) : 0;
241 offset += continuous_bits;
242 }
243 }
244
245 /**
246 * Change the state of a range of pages
247 *
248 * @param dirty_addr Base address to mark or unmark as modified
249 * @param size Size in bytes to mark or unmark as modified
250 */
251 template <Type type, bool enable>
252 void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) {
253 std::span<u64> state_words = words.template Span<type>();
254 [[maybe_unused]] std::span<u64> untracked_words = words.template Span<Type::Untracked>();
255 [[maybe_unused]] std::span<u64> cached_words = words.template Span<Type::CachedCPU>();
256 IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) {
257 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
258 NotifyRasterizer<!enable>(index, untracked_words[index], mask);
259 }
260 if constexpr (enable) {
261 state_words[index] |= mask;
262 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
263 untracked_words[index] |= mask;
264 }
265 if constexpr (type == Type::CPU) {
266 cached_words[index] &= ~mask;
267 }
268 } else {
269 if constexpr (type == Type::CPU) {
270 const u64 word = state_words[index] & mask;
271 cached_words[index] &= ~word;
272 }
273 state_words[index] &= ~mask;
274 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
275 untracked_words[index] &= ~mask;
276 }
277 }
278 });
279 }
280
281 /**
282 * Loop over each page in the given range, turn off those bits and notify the rasterizer if
283 * needed. Call the given function on each turned off range.
284 *
285 * @param query_cpu_range Base CPU address to loop over
286 * @param size Size in bytes of the CPU range to loop over
287 * @param func Function to call for each turned off region
288 */
289 template <Type type, bool clear, typename Func>
290 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) {
291 static_assert(type != Type::Untracked);
292
293 std::span<u64> state_words = words.template Span<type>();
294 [[maybe_unused]] std::span<u64> untracked_words = words.template Span<Type::Untracked>();
295 [[maybe_unused]] std::span<u64> cached_words = words.template Span<Type::CachedCPU>();
296 const size_t offset = query_cpu_range - cpu_addr;
297 bool pending = false;
298 size_t pending_offset{};
299 size_t pending_pointer{};
300 const auto release = [&]() {
301 func(cpu_addr + pending_offset * BYTES_PER_PAGE,
302 (pending_pointer - pending_offset) * BYTES_PER_PAGE);
303 };
304 IterateWords(offset, size, [&](size_t index, u64 mask) {
305 if constexpr (type == Type::GPU) {
306 mask &= ~untracked_words[index];
307 }
308 const u64 word = state_words[index] & mask;
309 if constexpr (clear) {
310 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
311 NotifyRasterizer<true>(index, untracked_words[index], mask);
312 }
313 state_words[index] &= ~mask;
314 if constexpr (type == Type::CPU || type == Type::CachedCPU) {
315 untracked_words[index] &= ~mask;
316 }
317 if constexpr (type == Type::CPU) {
318 cached_words[index] &= ~word;
319 }
320 }
321 const size_t base_offset = index * PAGES_PER_WORD;
322 IteratePages(word, [&](size_t pages_offset, size_t pages_size) {
323 const auto reset = [&]() {
324 pending_offset = base_offset + pages_offset;
325 pending_pointer = base_offset + pages_offset + pages_size;
326 };
327 if (!pending) {
328 reset();
329 pending = true;
330 return;
331 }
332 if (pending_pointer == base_offset + pages_offset) {
333 pending_pointer += pages_size;
334 return;
335 }
336 release();
337 reset();
338 });
339 });
340 if (pending) {
341 release();
342 }
343 }
344
345 /**
346 * Returns true when a region has been modified
347 *
348 * @param offset Offset in bytes from the start of the buffer
349 * @param size Size in bytes of the region to query for modifications
350 */
351 template <Type type>
352 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
353 static_assert(type != Type::Untracked);
354
355 const std::span<const u64> state_words = words.template Span<type>();
356 [[maybe_unused]] const std::span<const u64> untracked_words =
357 words.template Span<Type::Untracked>();
358 bool result = false;
359 IterateWords(offset, size, [&](size_t index, u64 mask) {
360 if constexpr (type == Type::GPU) {
361 mask &= ~untracked_words[index];
362 }
363 const u64 word = state_words[index] & mask;
364 if (word != 0) {
365 result = true;
366 return true;
367 }
368 return false;
369 });
370 return result;
371 }
372
373 /**
374 * Returns a begin end pair with the inclusive modified region
375 *
376 * @param offset Offset in bytes from the start of the buffer
377 * @param size Size in bytes of the region to query for modifications
378 */
379 template <Type type>
380 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
381 static_assert(type != Type::Untracked);
382 const std::span<const u64> state_words = words.template Span<type>();
383 [[maybe_unused]] const std::span<const u64> untracked_words =
384 words.template Span<Type::Untracked>();
385 u64 begin = std::numeric_limits<u64>::max();
386 u64 end = 0;
387 IterateWords(offset, size, [&](size_t index, u64 mask) {
388 if constexpr (type == Type::GPU) {
389 mask &= ~untracked_words[index];
390 }
391 const u64 word = state_words[index] & mask;
392 if (word == 0) {
393 return;
394 }
395 const u64 local_page_begin = std::countr_zero(word);
396 const u64 local_page_end = PAGES_PER_WORD - std::countl_zero(word);
397 const u64 page_index = index * PAGES_PER_WORD;
398 begin = std::min(begin, page_index + local_page_begin);
399 end = page_index + local_page_end;
400 });
401 static constexpr std::pair<u64, u64> EMPTY{0, 0};
402 return begin < end ? std::make_pair(begin * BYTES_PER_PAGE, end * BYTES_PER_PAGE) : EMPTY;
403 }
404
405 /// Returns the number of words of the manager
406 [[nodiscard]] size_t NumWords() const noexcept {
407 return words.NumWords();
408 }
409
410 /// Returns the size in bytes of the manager
411 [[nodiscard]] u64 SizeBytes() const noexcept {
412 return words.size_bytes;
413 }
414
415 /// Returns true when the buffer fits in the small vector optimization
416 [[nodiscard]] bool IsShort() const noexcept {
417 return words.IsShort();
418 }
419
420 void FlushCachedWrites() noexcept {
421 const u64 num_words = NumWords();
422 u64* const cached_words = Array<Type::CachedCPU>();
423 u64* const untracked_words = Array<Type::Untracked>();
424 u64* const cpu_words = Array<Type::CPU>();
425 for (u64 word_index = 0; word_index < num_words; ++word_index) {
426 const u64 cached_bits = cached_words[word_index];
427 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits);
428 untracked_words[word_index] |= cached_bits;
429 cpu_words[word_index] |= cached_bits;
430 cached_words[word_index] = 0;
431 }
432 }
433
434private:
435 template <Type type>
436 u64* Array() noexcept {
437 if constexpr (type == Type::CPU) {
438 return words.cpu.Pointer(IsShort());
439 } else if constexpr (type == Type::GPU) {
440 return words.gpu.Pointer(IsShort());
441 } else if constexpr (type == Type::CachedCPU) {
442 return words.cached_cpu.Pointer(IsShort());
443 } else if constexpr (type == Type::Untracked) {
444 return words.untracked.Pointer(IsShort());
445 }
446 }
447
448 template <Type type>
449 const u64* Array() const noexcept {
450 if constexpr (type == Type::CPU) {
451 return words.cpu.Pointer(IsShort());
452 } else if constexpr (type == Type::GPU) {
453 return words.gpu.Pointer(IsShort());
454 } else if constexpr (type == Type::CachedCPU) {
455 return words.cached_cpu.Pointer(IsShort());
456 } else if constexpr (type == Type::Untracked) {
457 return words.untracked.Pointer(IsShort());
458 }
459 }
460
461 /**
462 * Notify rasterizer about changes in the CPU tracking state of a word in the buffer
463 *
464 * @param word_index Index to the word to notify to the rasterizer
465 * @param current_bits Current state of the word
466 * @param new_bits New state of the word
467 *
468 * @tparam add_to_rasterizer True when the rasterizer should start tracking the new pages
469 */
470 template <bool add_to_rasterizer>
471 void NotifyRasterizer(u64 word_index, u64 current_bits, u64 new_bits) const {
472 u64 changed_bits = (add_to_rasterizer ? current_bits : ~current_bits) & new_bits;
473 VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
474 IteratePages(changed_bits, [&](size_t offset, size_t size) {
475 rasterizer->UpdatePagesCachedCount(addr + offset * BYTES_PER_PAGE,
476 size * BYTES_PER_PAGE, add_to_rasterizer ? 1 : -1);
477 });
478 }
479
480 VAddr cpu_addr = 0;
481 RasterizerInterface* rasterizer = nullptr;
482 Words<stack_words> words;
483};
484
485} // namespace VideoCommon
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp
index 4e75f33ca..ab4f4d407 100644
--- a/src/video_core/compatible_formats.cpp
+++ b/src/video_core/compatible_formats.cpp
@@ -126,15 +126,14 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{
126 PixelFormat::ASTC_2D_8X8_SRGB, 126 PixelFormat::ASTC_2D_8X8_SRGB,
127}; 127};
128 128
129// Missing formats: 129constexpr std::array VIEW_CLASS_ASTC_10x5_RGBA{
130// PixelFormat::ASTC_2D_10X5_UNORM 130 PixelFormat::ASTC_2D_10X5_UNORM,
131// PixelFormat::ASTC_2D_10X5_SRGB 131 PixelFormat::ASTC_2D_10X5_SRGB,
132 132};
133// Missing formats:
134// PixelFormat::ASTC_2D_10X6_SRGB
135 133
136constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{ 134constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{
137 PixelFormat::ASTC_2D_10X6_UNORM, 135 PixelFormat::ASTC_2D_10X6_UNORM,
136 PixelFormat::ASTC_2D_10X6_SRGB,
138}; 137};
139 138
140constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ 139constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{
@@ -147,9 +146,10 @@ constexpr std::array VIEW_CLASS_ASTC_10x10_RGBA{
147 PixelFormat::ASTC_2D_10X10_SRGB, 146 PixelFormat::ASTC_2D_10X10_SRGB,
148}; 147};
149 148
150// Missing formats 149constexpr std::array VIEW_CLASS_ASTC_12x10_RGBA{
151// ASTC_2D_12X10_UNORM, 150 PixelFormat::ASTC_2D_12X10_UNORM,
152// ASTC_2D_12X10_SRGB, 151 PixelFormat::ASTC_2D_12X10_SRGB,
152};
153 153
154constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{ 154constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{
155 PixelFormat::ASTC_2D_12X12_UNORM, 155 PixelFormat::ASTC_2D_12X12_UNORM,
@@ -229,9 +229,11 @@ constexpr Table MakeViewTable() {
229 EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA); 229 EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA);
230 EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA); 230 EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA);
231 EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA); 231 EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA);
232 EnableRange(view, VIEW_CLASS_ASTC_10x5_RGBA);
232 EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA); 233 EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA);
233 EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA); 234 EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA);
234 EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA); 235 EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA);
236 EnableRange(view, VIEW_CLASS_ASTC_12x10_RGBA);
235 EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA); 237 EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA);
236 return view; 238 return view;
237} 239}
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index e68850dc5..ebe5536de 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -223,7 +223,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
223 write_buffer.resize_destructive(dst_size); 223 write_buffer.resize_destructive(dst_size);
224 224
225 memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size); 225 memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size);
226 memory_manager.ReadBlockUnsafe(dst_operand.address, write_buffer.data(), dst_size); 226 memory_manager.ReadBlock(dst_operand.address, write_buffer.data(), dst_size);
227 227
228 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, 228 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
229 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 229 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
@@ -288,11 +288,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
288 write_buffer.resize_destructive(dst_size); 288 write_buffer.resize_destructive(dst_size);
289 289
290 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 290 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
291 if (Settings::IsGPULevelExtreme()) { 291 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
292 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
293 } else {
294 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
295 }
296 292
297 // If the input is linear and the output is tiled, swizzle the input and copy it over. 293 // If the input is linear and the output is tiled, swizzle the input and copy it over.
298 SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, 294 SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index c390ac91b..35d699bbf 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -4,13 +4,20 @@
4#pragma once 4#pragma once
5 5
6#include <algorithm> 6#include <algorithm>
7#include <condition_variable>
7#include <cstring> 8#include <cstring>
8#include <deque> 9#include <deque>
9#include <functional> 10#include <functional>
10#include <memory> 11#include <memory>
12#include <mutex>
13#include <thread>
11#include <queue> 14#include <queue>
12 15
13#include "common/common_types.h" 16#include "common/common_types.h"
17#include "common/microprofile.h"
18#include "common/scope_exit.h"
19#include "common/settings.h"
20#include "common/thread.h"
14#include "video_core/delayed_destruction_ring.h" 21#include "video_core/delayed_destruction_ring.h"
15#include "video_core/gpu.h" 22#include "video_core/gpu.h"
16#include "video_core/host1x/host1x.h" 23#include "video_core/host1x/host1x.h"
@@ -23,15 +30,26 @@ class FenceBase {
23public: 30public:
24 explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {} 31 explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {}
25 32
33 bool IsStubbed() const {
34 return is_stubbed;
35 }
36
26protected: 37protected:
27 bool is_stubbed; 38 bool is_stubbed;
28}; 39};
29 40
30template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache> 41template <typename Traits>
31class FenceManager { 42class FenceManager {
43 using TFence = typename Traits::FenceType;
44 using TTextureCache = typename Traits::TextureCacheType;
45 using TBufferCache = typename Traits::BufferCacheType;
46 using TQueryCache = typename Traits::QueryCacheType;
47 static constexpr bool can_async_check = Traits::HAS_ASYNC_CHECK;
48
32public: 49public:
33 /// Notify the fence manager about a new frame 50 /// Notify the fence manager about a new frame
34 void TickFrame() { 51 void TickFrame() {
52 std::unique_lock lock(ring_guard);
35 delayed_destruction_ring.Tick(); 53 delayed_destruction_ring.Tick();
36 } 54 }
37 55
@@ -41,22 +59,43 @@ public:
41 buffer_cache.AccumulateFlushes(); 59 buffer_cache.AccumulateFlushes();
42 } 60 }
43 61
62 void SignalReference() {
63 std::function<void()> do_nothing([] {});
64 SignalFence(std::move(do_nothing));
65 }
66
44 void SyncOperation(std::function<void()>&& func) { 67 void SyncOperation(std::function<void()>&& func) {
45 uncommitted_operations.emplace_back(std::move(func)); 68 uncommitted_operations.emplace_back(std::move(func));
46 } 69 }
47 70
48 void SignalFence(std::function<void()>&& func) { 71 void SignalFence(std::function<void()>&& func) {
49 TryReleasePendingFences(); 72 rasterizer.InvalidateGPUCache();
73 bool delay_fence = Settings::IsGPULevelHigh();
74 if constexpr (!can_async_check) {
75 TryReleasePendingFences<false>();
76 }
50 const bool should_flush = ShouldFlush(); 77 const bool should_flush = ShouldFlush();
51 CommitAsyncFlushes(); 78 CommitAsyncFlushes();
52 uncommitted_operations.emplace_back(std::move(func));
53 CommitOperations();
54 TFence new_fence = CreateFence(!should_flush); 79 TFence new_fence = CreateFence(!should_flush);
55 fences.push(new_fence); 80 if constexpr (can_async_check) {
81 guard.lock();
82 }
83 if (delay_fence) {
84 uncommitted_operations.emplace_back(std::move(func));
85 }
86 pending_operations.emplace_back(std::move(uncommitted_operations));
56 QueueFence(new_fence); 87 QueueFence(new_fence);
88 if (!delay_fence) {
89 func();
90 }
91 fences.push(std::move(new_fence));
57 if (should_flush) { 92 if (should_flush) {
58 rasterizer.FlushCommands(); 93 rasterizer.FlushCommands();
59 } 94 }
95 if constexpr (can_async_check) {
96 guard.unlock();
97 cv.notify_all();
98 }
60 } 99 }
61 100
62 void SignalSyncPoint(u32 value) { 101 void SignalSyncPoint(u32 value) {
@@ -66,29 +105,30 @@ public:
66 } 105 }
67 106
68 void WaitPendingFences() { 107 void WaitPendingFences() {
69 while (!fences.empty()) { 108 if constexpr (!can_async_check) {
70 TFence& current_fence = fences.front(); 109 TryReleasePendingFences<true>();
71 if (ShouldWait()) {
72 WaitFence(current_fence);
73 }
74 PopAsyncFlushes();
75 auto operations = std::move(pending_operations.front());
76 pending_operations.pop_front();
77 for (auto& operation : operations) {
78 operation();
79 }
80 PopFence();
81 } 110 }
82 } 111 }
83 112
84protected: 113protected:
85 explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, 114 explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
86 TTextureCache& texture_cache_, TTBufferCache& buffer_cache_, 115 TTextureCache& texture_cache_, TBufferCache& buffer_cache_,
87 TQueryCache& query_cache_) 116 TQueryCache& query_cache_)
88 : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()}, 117 : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()},
89 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {} 118 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {
119 if constexpr (can_async_check) {
120 fence_thread =
121 std::jthread([this](std::stop_token token) { ReleaseThreadFunc(token); });
122 }
123 }
90 124
91 virtual ~FenceManager() = default; 125 virtual ~FenceManager() {
126 if constexpr (can_async_check) {
127 fence_thread.request_stop();
128 cv.notify_all();
129 fence_thread.join();
130 }
131 }
92 132
93 /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is 133 /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is
94 /// true 134 /// true
@@ -104,15 +144,20 @@ protected:
104 Tegra::GPU& gpu; 144 Tegra::GPU& gpu;
105 Tegra::Host1x::SyncpointManager& syncpoint_manager; 145 Tegra::Host1x::SyncpointManager& syncpoint_manager;
106 TTextureCache& texture_cache; 146 TTextureCache& texture_cache;
107 TTBufferCache& buffer_cache; 147 TBufferCache& buffer_cache;
108 TQueryCache& query_cache; 148 TQueryCache& query_cache;
109 149
110private: 150private:
151 template <bool force_wait>
111 void TryReleasePendingFences() { 152 void TryReleasePendingFences() {
112 while (!fences.empty()) { 153 while (!fences.empty()) {
113 TFence& current_fence = fences.front(); 154 TFence& current_fence = fences.front();
114 if (ShouldWait() && !IsFenceSignaled(current_fence)) { 155 if (ShouldWait() && !IsFenceSignaled(current_fence)) {
115 return; 156 if constexpr (force_wait) {
157 WaitFence(current_fence);
158 } else {
159 return;
160 }
116 } 161 }
117 PopAsyncFlushes(); 162 PopAsyncFlushes();
118 auto operations = std::move(pending_operations.front()); 163 auto operations = std::move(pending_operations.front());
@@ -120,7 +165,49 @@ private:
120 for (auto& operation : operations) { 165 for (auto& operation : operations) {
121 operation(); 166 operation();
122 } 167 }
123 PopFence(); 168 {
169 std::unique_lock lock(ring_guard);
170 delayed_destruction_ring.Push(std::move(current_fence));
171 }
172 fences.pop();
173 }
174 }
175
176 void ReleaseThreadFunc(std::stop_token stop_token) {
177 std::string name = "GPUFencingThread";
178 MicroProfileOnThreadCreate(name.c_str());
179
180 // Cleanup
181 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
182
183 Common::SetCurrentThreadName(name.c_str());
184 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
185
186 TFence current_fence;
187 std::deque<std::function<void()>> current_operations;
188 while (!stop_token.stop_requested()) {
189 {
190 std::unique_lock lock(guard);
191 cv.wait(lock, [&] { return stop_token.stop_requested() || !fences.empty(); });
192 if (stop_token.stop_requested()) [[unlikely]] {
193 return;
194 }
195 current_fence = std::move(fences.front());
196 current_operations = std::move(pending_operations.front());
197 fences.pop();
198 pending_operations.pop_front();
199 }
200 if (!current_fence->IsStubbed()) {
201 WaitFence(current_fence);
202 }
203 PopAsyncFlushes();
204 for (auto& operation : current_operations) {
205 operation();
206 }
207 {
208 std::unique_lock lock(ring_guard);
209 delayed_destruction_ring.Push(std::move(current_fence));
210 }
124 } 211 }
125 } 212 }
126 213
@@ -154,19 +241,16 @@ private:
154 query_cache.CommitAsyncFlushes(); 241 query_cache.CommitAsyncFlushes();
155 } 242 }
156 243
157 void PopFence() {
158 delayed_destruction_ring.Push(std::move(fences.front()));
159 fences.pop();
160 }
161
162 void CommitOperations() {
163 pending_operations.emplace_back(std::move(uncommitted_operations));
164 }
165
166 std::queue<TFence> fences; 244 std::queue<TFence> fences;
167 std::deque<std::function<void()>> uncommitted_operations; 245 std::deque<std::function<void()>> uncommitted_operations;
168 std::deque<std::deque<std::function<void()>>> pending_operations; 246 std::deque<std::deque<std::function<void()>>> pending_operations;
169 247
248 std::mutex guard;
249 std::mutex ring_guard;
250 std::condition_variable cv;
251
252 std::jthread fence_thread;
253
170 DelayedDestructionRing<TFence, 6> delayed_destruction_ring; 254 DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
171}; 255};
172 256
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 2e7f9c5ed..295a416a8 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -283,6 +283,21 @@ struct GPU::Impl {
283 gpu_thread.FlushRegion(addr, size); 283 gpu_thread.FlushRegion(addr, size);
284 } 284 }
285 285
286 VideoCore::RasterizerDownloadArea OnCPURead(VAddr addr, u64 size) {
287 auto raster_area = rasterizer->GetFlushArea(addr, size);
288 if (raster_area.preemtive) {
289 return raster_area;
290 }
291 raster_area.preemtive = true;
292 const u64 fence = RequestSyncOperation([this, &raster_area]() {
293 rasterizer->FlushRegion(raster_area.start_address,
294 raster_area.end_address - raster_area.start_address);
295 });
296 gpu_thread.TickGPU();
297 WaitForSyncOperation(fence);
298 return raster_area;
299 }
300
286 /// Notify rasterizer that any caches of the specified region should be invalidated 301 /// Notify rasterizer that any caches of the specified region should be invalidated
287 void InvalidateRegion(VAddr addr, u64 size) { 302 void InvalidateRegion(VAddr addr, u64 size) {
288 gpu_thread.InvalidateRegion(addr, size); 303 gpu_thread.InvalidateRegion(addr, size);
@@ -538,6 +553,10 @@ void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
538 impl->SwapBuffers(framebuffer); 553 impl->SwapBuffers(framebuffer);
539} 554}
540 555
556VideoCore::RasterizerDownloadArea GPU::OnCPURead(VAddr addr, u64 size) {
557 return impl->OnCPURead(addr, size);
558}
559
541void GPU::FlushRegion(VAddr addr, u64 size) { 560void GPU::FlushRegion(VAddr addr, u64 size) {
542 impl->FlushRegion(addr, size); 561 impl->FlushRegion(addr, size);
543} 562}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 8a871593a..e49c40cf2 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -10,6 +10,7 @@
10#include "core/hle/service/nvdrv/nvdata.h" 10#include "core/hle/service/nvdrv/nvdata.h"
11#include "video_core/cdma_pusher.h" 11#include "video_core/cdma_pusher.h"
12#include "video_core/framebuffer_config.h" 12#include "video_core/framebuffer_config.h"
13#include "video_core/rasterizer_download_area.h"
13 14
14namespace Core { 15namespace Core {
15class System; 16class System;
@@ -241,6 +242,9 @@ public:
241 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); 242 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
242 243
243 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 244 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
245 [[nodiscard]] VideoCore::RasterizerDownloadArea OnCPURead(VAddr addr, u64 size);
246
247 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
244 void FlushRegion(VAddr addr, u64 size); 248 void FlushRegion(VAddr addr, u64 size);
245 249
246 /// Notify rasterizer that any caches of the specified region should be invalidated 250 /// Notify rasterizer that any caches of the specified region should be invalidated
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 01fb5b546..7b2cde7a7 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -82,6 +82,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
82} 82}
83 83
84PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { 84PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
85 std::unique_lock<std::mutex> lock(guard);
85 return kind_map.GetValueAt(gpu_addr); 86 return kind_map.GetValueAt(gpu_addr);
86} 87}
87 88
@@ -160,7 +161,10 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
160 } 161 }
161 remaining_size -= big_page_size; 162 remaining_size -= big_page_size;
162 } 163 }
163 kind_map.Map(gpu_addr, gpu_addr + size, kind); 164 {
165 std::unique_lock<std::mutex> lock(guard);
166 kind_map.Map(gpu_addr, gpu_addr + size, kind);
167 }
164 return gpu_addr; 168 return gpu_addr;
165} 169}
166 170
@@ -553,6 +557,7 @@ size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const {
553} 557}
554 558
555size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const { 559size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
560 std::unique_lock<std::mutex> lock(guard);
556 return kind_map.GetContinuousSizeFrom(gpu_addr); 561 return kind_map.GetContinuousSizeFrom(gpu_addr);
557} 562}
558 563
@@ -745,10 +750,10 @@ void MemoryManager::FlushCaching() {
745 return; 750 return;
746 } 751 }
747 accumulator->Callback([this](GPUVAddr addr, size_t size) { 752 accumulator->Callback([this](GPUVAddr addr, size_t size) {
748 GetSubmappedRangeImpl<false>(addr, size, page_stash); 753 GetSubmappedRangeImpl<false>(addr, size, page_stash2);
749 }); 754 });
750 rasterizer->InnerInvalidation(page_stash); 755 rasterizer->InnerInvalidation(page_stash2);
751 page_stash.clear(); 756 page_stash2.clear();
752 accumulator->Clear(); 757 accumulator->Clear();
753} 758}
754 759
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index fbbe856c4..794535122 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -5,6 +5,7 @@
5 5
6#include <atomic> 6#include <atomic>
7#include <map> 7#include <map>
8#include <mutex>
8#include <optional> 9#include <optional>
9#include <vector> 10#include <vector>
10 11
@@ -215,6 +216,9 @@ private:
215 216
216 std::vector<u64> big_page_continuous; 217 std::vector<u64> big_page_continuous;
217 std::vector<std::pair<VAddr, std::size_t>> page_stash{}; 218 std::vector<std::pair<VAddr, std::size_t>> page_stash{};
219 std::vector<std::pair<VAddr, std::size_t>> page_stash2{};
220
221 mutable std::mutex guard;
218 222
219 static constexpr size_t continuous_bits = 64; 223 static constexpr size_t continuous_bits = 64;
220 224
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 8906ba6d8..1528cc1dd 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -6,6 +6,7 @@
6#include <algorithm> 6#include <algorithm>
7#include <array> 7#include <array>
8#include <cstring> 8#include <cstring>
9#include <functional>
9#include <iterator> 10#include <iterator>
10#include <list> 11#include <list>
11#include <memory> 12#include <memory>
@@ -17,13 +18,19 @@
17 18
18#include "common/assert.h" 19#include "common/assert.h"
19#include "common/settings.h" 20#include "common/settings.h"
21#include "core/memory.h"
20#include "video_core/control/channel_state_cache.h" 22#include "video_core/control/channel_state_cache.h"
21#include "video_core/engines/maxwell_3d.h" 23#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h" 24#include "video_core/memory_manager.h"
23#include "video_core/rasterizer_interface.h" 25#include "video_core/rasterizer_interface.h"
26#include "video_core/texture_cache/slot_vector.h"
24 27
25namespace VideoCommon { 28namespace VideoCommon {
26 29
30using AsyncJobId = SlotId;
31
32static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
33
27template <class QueryCache, class HostCounter> 34template <class QueryCache, class HostCounter>
28class CounterStreamBase { 35class CounterStreamBase {
29public: 36public:
@@ -93,9 +100,13 @@ private:
93template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> 100template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
94class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> { 101class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
95public: 102public:
96 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_) 103 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
97 : rasterizer{rasterizer_}, streams{{CounterStream{static_cast<QueryCache&>(*this), 104 Core::Memory::Memory& cpu_memory_)
98 VideoCore::QueryType::SamplesPassed}}} {} 105 : rasterizer{rasterizer_},
106 cpu_memory{cpu_memory_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
107 VideoCore::QueryType::SamplesPassed}}} {
108 (void)slot_async_jobs.insert(); // Null value
109 }
99 110
100 void InvalidateRegion(VAddr addr, std::size_t size) { 111 void InvalidateRegion(VAddr addr, std::size_t size) {
101 std::unique_lock lock{mutex}; 112 std::unique_lock lock{mutex};
@@ -126,10 +137,15 @@ public:
126 query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); 137 query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
127 } 138 }
128 139
129 query->BindCounter(Stream(type).Current(), timestamp); 140 auto result = query->BindCounter(Stream(type).Current(), timestamp);
130 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 141 if (result) {
131 AsyncFlushQuery(*cpu_addr); 142 auto async_job_id = query->GetAsyncJob();
143 auto& async_job = slot_async_jobs[async_job_id];
144 async_job.collected = true;
145 async_job.value = *result;
146 query->SetAsyncJob(NULL_ASYNC_JOB_ID);
132 } 147 }
148 AsyncFlushQuery(query, timestamp, lock);
133 } 149 }
134 150
135 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. 151 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
@@ -173,15 +189,18 @@ public:
173 } 189 }
174 190
175 void CommitAsyncFlushes() { 191 void CommitAsyncFlushes() {
192 std::unique_lock lock{mutex};
176 committed_flushes.push_back(uncommitted_flushes); 193 committed_flushes.push_back(uncommitted_flushes);
177 uncommitted_flushes.reset(); 194 uncommitted_flushes.reset();
178 } 195 }
179 196
180 bool HasUncommittedFlushes() const { 197 bool HasUncommittedFlushes() const {
198 std::unique_lock lock{mutex};
181 return uncommitted_flushes != nullptr; 199 return uncommitted_flushes != nullptr;
182 } 200 }
183 201
184 bool ShouldWaitAsyncFlushes() const { 202 bool ShouldWaitAsyncFlushes() const {
203 std::unique_lock lock{mutex};
185 if (committed_flushes.empty()) { 204 if (committed_flushes.empty()) {
186 return false; 205 return false;
187 } 206 }
@@ -189,6 +208,7 @@ public:
189 } 208 }
190 209
191 void PopAsyncFlushes() { 210 void PopAsyncFlushes() {
211 std::unique_lock lock{mutex};
192 if (committed_flushes.empty()) { 212 if (committed_flushes.empty()) {
193 return; 213 return;
194 } 214 }
@@ -197,15 +217,25 @@ public:
197 committed_flushes.pop_front(); 217 committed_flushes.pop_front();
198 return; 218 return;
199 } 219 }
200 for (VAddr query_address : *flush_list) { 220 for (AsyncJobId async_job_id : *flush_list) {
201 FlushAndRemoveRegion(query_address, 4); 221 AsyncJob& async_job = slot_async_jobs[async_job_id];
222 if (!async_job.collected) {
223 FlushAndRemoveRegion(async_job.query_location, 2, true);
224 }
202 } 225 }
203 committed_flushes.pop_front(); 226 committed_flushes.pop_front();
204 } 227 }
205 228
206private: 229private:
230 struct AsyncJob {
231 bool collected = false;
232 u64 value = 0;
233 VAddr query_location = 0;
234 std::optional<u64> timestamp{};
235 };
236
207 /// Flushes a memory range to guest memory and removes it from the cache. 237 /// Flushes a memory range to guest memory and removes it from the cache.
208 void FlushAndRemoveRegion(VAddr addr, std::size_t size) { 238 void FlushAndRemoveRegion(VAddr addr, std::size_t size, bool async = false) {
209 const u64 addr_begin = addr; 239 const u64 addr_begin = addr;
210 const u64 addr_end = addr_begin + size; 240 const u64 addr_end = addr_begin + size;
211 const auto in_range = [addr_begin, addr_end](const CachedQuery& query) { 241 const auto in_range = [addr_begin, addr_end](const CachedQuery& query) {
@@ -225,8 +255,16 @@ private:
225 if (!in_range(query)) { 255 if (!in_range(query)) {
226 continue; 256 continue;
227 } 257 }
228 rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1); 258 AsyncJobId async_job_id = query.GetAsyncJob();
229 query.Flush(); 259 auto flush_result = query.Flush(async);
260 if (async_job_id == NULL_ASYNC_JOB_ID) {
261 ASSERT_MSG(false, "This should not be reachable at all");
262 continue;
263 }
264 AsyncJob& async_job = slot_async_jobs[async_job_id];
265 async_job.collected = true;
266 async_job.value = flush_result;
267 query.SetAsyncJob(NULL_ASYNC_JOB_ID);
230 } 268 }
231 std::erase_if(contents, in_range); 269 std::erase_if(contents, in_range);
232 } 270 }
@@ -234,7 +272,6 @@ private:
234 272
235 /// Registers the passed parameters as cached and returns a pointer to the stored cached query. 273 /// Registers the passed parameters as cached and returns a pointer to the stored cached query.
236 CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { 274 CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) {
237 rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1);
238 const u64 page = static_cast<u64>(cpu_addr) >> YUZU_PAGEBITS; 275 const u64 page = static_cast<u64>(cpu_addr) >> YUZU_PAGEBITS;
239 return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, 276 return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr,
240 host_ptr); 277 host_ptr);
@@ -253,26 +290,60 @@ private:
253 return found != std::end(contents) ? &*found : nullptr; 290 return found != std::end(contents) ? &*found : nullptr;
254 } 291 }
255 292
256 void AsyncFlushQuery(VAddr addr) { 293 void AsyncFlushQuery(CachedQuery* query, std::optional<u64> timestamp,
257 if (!uncommitted_flushes) { 294 std::unique_lock<std::recursive_mutex>& lock) {
258 uncommitted_flushes = std::make_shared<std::vector<VAddr>>(); 295 const AsyncJobId new_async_job_id = slot_async_jobs.insert();
296 {
297 AsyncJob& async_job = slot_async_jobs[new_async_job_id];
298 query->SetAsyncJob(new_async_job_id);
299 async_job.query_location = query->GetCpuAddr();
300 async_job.collected = false;
301
302 if (!uncommitted_flushes) {
303 uncommitted_flushes = std::make_shared<std::vector<AsyncJobId>>();
304 }
305 uncommitted_flushes->push_back(new_async_job_id);
259 } 306 }
260 uncommitted_flushes->push_back(addr); 307 lock.unlock();
308 std::function<void()> operation([this, new_async_job_id, timestamp] {
309 std::unique_lock local_lock{mutex};
310 AsyncJob& async_job = slot_async_jobs[new_async_job_id];
311 u64 value = async_job.value;
312 VAddr address = async_job.query_location;
313 slot_async_jobs.erase(new_async_job_id);
314 local_lock.unlock();
315 if (timestamp) {
316 u64 timestamp_value = *timestamp;
317 cpu_memory.WriteBlockUnsafe(address + sizeof(u64), &timestamp_value, sizeof(u64));
318 cpu_memory.WriteBlockUnsafe(address, &value, sizeof(u64));
319 rasterizer.InvalidateRegion(address, sizeof(u64) * 2,
320 VideoCommon::CacheType::NoQueryCache);
321 } else {
322 u32 small_value = static_cast<u32>(value);
323 cpu_memory.WriteBlockUnsafe(address, &small_value, sizeof(u32));
324 rasterizer.InvalidateRegion(address, sizeof(u32),
325 VideoCommon::CacheType::NoQueryCache);
326 }
327 });
328 rasterizer.SyncOperation(std::move(operation));
261 } 329 }
262 330
263 static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; 331 static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
264 static constexpr unsigned YUZU_PAGEBITS = 12; 332 static constexpr unsigned YUZU_PAGEBITS = 12;
265 333
334 SlotVector<AsyncJob> slot_async_jobs;
335
266 VideoCore::RasterizerInterface& rasterizer; 336 VideoCore::RasterizerInterface& rasterizer;
337 Core::Memory::Memory& cpu_memory;
267 338
268 std::recursive_mutex mutex; 339 mutable std::recursive_mutex mutex;
269 340
270 std::unordered_map<u64, std::vector<CachedQuery>> cached_queries; 341 std::unordered_map<u64, std::vector<CachedQuery>> cached_queries;
271 342
272 std::array<CounterStream, VideoCore::NumQueryTypes> streams; 343 std::array<CounterStream, VideoCore::NumQueryTypes> streams;
273 344
274 std::shared_ptr<std::vector<VAddr>> uncommitted_flushes{}; 345 std::shared_ptr<std::vector<AsyncJobId>> uncommitted_flushes{};
275 std::list<std::shared_ptr<std::vector<VAddr>>> committed_flushes; 346 std::list<std::shared_ptr<std::vector<AsyncJobId>>> committed_flushes;
276}; 347};
277 348
278template <class QueryCache, class HostCounter> 349template <class QueryCache, class HostCounter>
@@ -291,12 +362,12 @@ public:
291 virtual ~HostCounterBase() = default; 362 virtual ~HostCounterBase() = default;
292 363
293 /// Returns the current value of the query. 364 /// Returns the current value of the query.
294 u64 Query() { 365 u64 Query(bool async = false) {
295 if (result) { 366 if (result) {
296 return *result; 367 return *result;
297 } 368 }
298 369
299 u64 value = BlockingQuery() + base_result; 370 u64 value = BlockingQuery(async) + base_result;
300 if (dependency) { 371 if (dependency) {
301 value += dependency->Query(); 372 value += dependency->Query();
302 dependency = nullptr; 373 dependency = nullptr;
@@ -317,7 +388,7 @@ public:
317 388
318protected: 389protected:
319 /// Returns the value of query from the backend API blocking as needed. 390 /// Returns the value of query from the backend API blocking as needed.
320 virtual u64 BlockingQuery() const = 0; 391 virtual u64 BlockingQuery(bool async = false) const = 0;
321 392
322private: 393private:
323 std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value. 394 std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value.
@@ -340,26 +411,33 @@ public:
340 CachedQueryBase& operator=(const CachedQueryBase&) = delete; 411 CachedQueryBase& operator=(const CachedQueryBase&) = delete;
341 412
342 /// Flushes the query to guest memory. 413 /// Flushes the query to guest memory.
343 virtual void Flush() { 414 virtual u64 Flush(bool async = false) {
344 // When counter is nullptr it means that it's just been reset. We are supposed to write a 415 // When counter is nullptr it means that it's just been reset. We are supposed to write a
345 // zero in these cases. 416 // zero in these cases.
346 const u64 value = counter ? counter->Query() : 0; 417 const u64 value = counter ? counter->Query(async) : 0;
418 if (async) {
419 return value;
420 }
347 std::memcpy(host_ptr, &value, sizeof(u64)); 421 std::memcpy(host_ptr, &value, sizeof(u64));
348 422
349 if (timestamp) { 423 if (timestamp) {
350 std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64)); 424 std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64));
351 } 425 }
426 return value;
352 } 427 }
353 428
354 /// Binds a counter to this query. 429 /// Binds a counter to this query.
355 void BindCounter(std::shared_ptr<HostCounter> counter_, std::optional<u64> timestamp_) { 430 std::optional<u64> BindCounter(std::shared_ptr<HostCounter> counter_,
431 std::optional<u64> timestamp_) {
432 std::optional<u64> result{};
356 if (counter) { 433 if (counter) {
357 // If there's an old counter set it means the query is being rewritten by the game. 434 // If there's an old counter set it means the query is being rewritten by the game.
358 // To avoid losing the data forever, flush here. 435 // To avoid losing the data forever, flush here.
359 Flush(); 436 result = std::make_optional(Flush());
360 } 437 }
361 counter = std::move(counter_); 438 counter = std::move(counter_);
362 timestamp = timestamp_; 439 timestamp = timestamp_;
440 return result;
363 } 441 }
364 442
365 VAddr GetCpuAddr() const noexcept { 443 VAddr GetCpuAddr() const noexcept {
@@ -374,6 +452,14 @@ public:
374 return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE; 452 return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE;
375 } 453 }
376 454
455 void SetAsyncJob(AsyncJobId assigned_async_job_) {
456 assigned_async_job = assigned_async_job_;
457 }
458
459 AsyncJobId GetAsyncJob() const {
460 return assigned_async_job;
461 }
462
377protected: 463protected:
378 /// Returns true when querying the counter may potentially block. 464 /// Returns true when querying the counter may potentially block.
379 bool WaitPending() const noexcept { 465 bool WaitPending() const noexcept {
@@ -389,6 +475,7 @@ private:
389 u8* host_ptr; ///< Writable host pointer. 475 u8* host_ptr; ///< Writable host pointer.
390 std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree. 476 std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree.
391 std::optional<u64> timestamp; ///< Timestamp to flush to guest memory. 477 std::optional<u64> timestamp; ///< Timestamp to flush to guest memory.
478 AsyncJobId assigned_async_job;
392}; 479};
393 480
394} // namespace VideoCommon 481} // namespace VideoCommon
diff --git a/src/video_core/rasterizer_download_area.h b/src/video_core/rasterizer_download_area.h
new file mode 100644
index 000000000..2d7425c79
--- /dev/null
+++ b/src/video_core/rasterizer_download_area.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace VideoCore {
9
10struct RasterizerDownloadArea {
11 VAddr start_address;
12 VAddr end_address;
13 bool preemtive;
14};
15
16} // namespace VideoCore \ No newline at end of file
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 33e2610bc..7566a8c4e 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -12,6 +12,7 @@
12#include "video_core/cache_types.h" 12#include "video_core/cache_types.h"
13#include "video_core/engines/fermi_2d.h" 13#include "video_core/engines/fermi_2d.h"
14#include "video_core/gpu.h" 14#include "video_core/gpu.h"
15#include "video_core/rasterizer_download_area.h"
15 16
16namespace Tegra { 17namespace Tegra {
17class MemoryManager; 18class MemoryManager;
@@ -95,6 +96,8 @@ public:
95 virtual bool MustFlushRegion(VAddr addr, u64 size, 96 virtual bool MustFlushRegion(VAddr addr, u64 size,
96 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0; 97 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
97 98
99 virtual RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) = 0;
100
98 /// Notify rasterizer that any caches of the specified region should be invalidated 101 /// Notify rasterizer that any caches of the specified region should be invalidated
99 virtual void InvalidateRegion(VAddr addr, u64 size, 102 virtual void InvalidateRegion(VAddr addr, u64 size,
100 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0; 103 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
index 2b5c7defa..bf2ce4c49 100644
--- a/src/video_core/renderer_null/null_rasterizer.cpp
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "common/alignment.h"
5#include "core/memory.h"
4#include "video_core/host1x/host1x.h" 6#include "video_core/host1x/host1x.h"
5#include "video_core/memory_manager.h" 7#include "video_core/memory_manager.h"
6#include "video_core/renderer_null/null_rasterizer.h" 8#include "video_core/renderer_null/null_rasterizer.h"
@@ -46,6 +48,14 @@ bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheTyp
46} 48}
47void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} 49void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
48void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} 50void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {}
51VideoCore::RasterizerDownloadArea RasterizerNull::GetFlushArea(VAddr addr, u64 size) {
52 VideoCore::RasterizerDownloadArea new_area{
53 .start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE),
54 .end_address = Common::AlignUp(addr + size, Core::Memory::YUZU_PAGESIZE),
55 .preemtive = true,
56 };
57 return new_area;
58}
49void RasterizerNull::InvalidateGPUCache() {} 59void RasterizerNull::InvalidateGPUCache() {}
50void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} 60void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {}
51void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {} 61void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {}
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
index 0c59e6a1f..a8d35d2c1 100644
--- a/src/video_core/renderer_null/null_rasterizer.h
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -54,6 +54,7 @@ public:
54 void InvalidateRegion(VAddr addr, u64 size, 54 void InvalidateRegion(VAddr addr, u64 size,
55 VideoCommon::CacheType which = VideoCommon::CacheType::All) override; 55 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
56 void OnCPUWrite(VAddr addr, u64 size) override; 56 void OnCPUWrite(VAddr addr, u64 size) override;
57 VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override;
57 void InvalidateGPUCache() override; 58 void InvalidateGPUCache() override;
58 void UnmapMemory(VAddr addr, u64 size) override; 59 void UnmapMemory(VAddr addr, u64 size) override;
59 void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; 60 void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index a8c3f8b67..18d3c3ac0 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -8,6 +8,7 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/buffer_cache/buffer_cache.h" 10#include "video_core/buffer_cache/buffer_cache.h"
11#include "video_core/buffer_cache/memory_tracker_base.h"
11#include "video_core/rasterizer_interface.h" 12#include "video_core/rasterizer_interface.h"
12#include "video_core/renderer_opengl/gl_device.h" 13#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_resource_manager.h" 14#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -200,6 +201,8 @@ private:
200struct BufferCacheParams { 201struct BufferCacheParams {
201 using Runtime = OpenGL::BufferCacheRuntime; 202 using Runtime = OpenGL::BufferCacheRuntime;
202 using Buffer = OpenGL::Buffer; 203 using Buffer = OpenGL::Buffer;
204 using Async_Buffer = u32;
205 using MemoryTracker = VideoCommon::MemoryTrackerBase<VideoCore::RasterizerInterface>;
203 206
204 static constexpr bool IS_OPENGL = true; 207 static constexpr bool IS_OPENGL = true;
205 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = true; 208 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = true;
@@ -208,6 +211,7 @@ struct BufferCacheParams {
208 static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; 211 static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
209 static constexpr bool USE_MEMORY_MAPS = false; 212 static constexpr bool USE_MEMORY_MAPS = false;
210 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; 213 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true;
214 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false;
211}; 215};
212 216
213using BufferCache = VideoCommon::BufferCache<BufferCacheParams>; 217using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache_base.cpp b/src/video_core/renderer_opengl/gl_buffer_cache_base.cpp
new file mode 100644
index 000000000..f15ae8e25
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_buffer_cache_base.cpp
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "video_core/buffer_cache/buffer_cache.h"
5#include "video_core/renderer_opengl/gl_buffer_cache.h"
6
7namespace VideoCommon {
8template class VideoCommon::BufferCache<OpenGL::BufferCacheParams>;
9}
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 22ed16ebf..400c21981 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -108,7 +108,8 @@ bool IsASTCSupported() {
108 108
109[[nodiscard]] bool IsDebugToolAttached(std::span<const std::string_view> extensions) { 109[[nodiscard]] bool IsDebugToolAttached(std::span<const std::string_view> extensions) {
110 const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED"); 110 const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
111 return nsight || HasExtension(extensions, "GL_EXT_debug_tool"); 111 return nsight || HasExtension(extensions, "GL_EXT_debug_tool") ||
112 Settings::values.renderer_debug.GetValue();
112} 113}
113} // Anonymous namespace 114} // Anonymous namespace
114 115
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index f1446e732..e21b19dcc 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -30,7 +30,17 @@ private:
30}; 30};
31 31
32using Fence = std::shared_ptr<GLInnerFence>; 32using Fence = std::shared_ptr<GLInnerFence>;
33using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; 33
34struct FenceManagerParams {
35 using FenceType = Fence;
36 using BufferCacheType = BufferCache;
37 using TextureCacheType = TextureCache;
38 using QueryCacheType = QueryCache;
39
40 static constexpr bool HAS_ASYNC_CHECK = false;
41};
42
43using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>;
34 44
35class FenceManagerOpenGL final : public GenericFenceManager { 45class FenceManagerOpenGL final : public GenericFenceManager {
36public: 46public:
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index 5070db441..99d7347f5 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -26,8 +26,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
26 26
27} // Anonymous namespace 27} // Anonymous namespace
28 28
29QueryCache::QueryCache(RasterizerOpenGL& rasterizer_) 29QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_)
30 : QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {} 30 : QueryCacheBase(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {}
31 31
32QueryCache::~QueryCache() = default; 32QueryCache::~QueryCache() = default;
33 33
@@ -74,7 +74,7 @@ void HostCounter::EndQuery() {
74 glEndQuery(GetTarget(type)); 74 glEndQuery(GetTarget(type));
75} 75}
76 76
77u64 HostCounter::BlockingQuery() const { 77u64 HostCounter::BlockingQuery([[maybe_unused]] bool async) const {
78 GLint64 value; 78 GLint64 value;
79 glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value); 79 glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value);
80 return static_cast<u64>(value); 80 return static_cast<u64>(value);
@@ -96,7 +96,7 @@ CachedQuery& CachedQuery::operator=(CachedQuery&& rhs) noexcept {
96 return *this; 96 return *this;
97} 97}
98 98
99void CachedQuery::Flush() { 99u64 CachedQuery::Flush([[maybe_unused]] bool async) {
100 // Waiting for a query while another query of the same target is enabled locks Nvidia's driver. 100 // Waiting for a query while another query of the same target is enabled locks Nvidia's driver.
101 // To avoid this disable and re-enable keeping the dependency stream. 101 // To avoid this disable and re-enable keeping the dependency stream.
102 // But we only have to do this if we have pending waits to be done. 102 // But we only have to do this if we have pending waits to be done.
@@ -106,11 +106,13 @@ void CachedQuery::Flush() {
106 stream.Update(false); 106 stream.Update(false);
107 } 107 }
108 108
109 VideoCommon::CachedQueryBase<HostCounter>::Flush(); 109 auto result = VideoCommon::CachedQueryBase<HostCounter>::Flush();
110 110
111 if (slice_counter) { 111 if (slice_counter) {
112 stream.Update(true); 112 stream.Update(true);
113 } 113 }
114
115 return result;
114} 116}
115 117
116} // namespace OpenGL 118} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h
index 14ce59990..872513f22 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.h
+++ b/src/video_core/renderer_opengl/gl_query_cache.h
@@ -28,7 +28,7 @@ using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
28class QueryCache final 28class QueryCache final
29 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { 29 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
30public: 30public:
31 explicit QueryCache(RasterizerOpenGL& rasterizer_); 31 explicit QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_);
32 ~QueryCache(); 32 ~QueryCache();
33 33
34 OGLQuery AllocateQuery(VideoCore::QueryType type); 34 OGLQuery AllocateQuery(VideoCore::QueryType type);
@@ -51,7 +51,7 @@ public:
51 void EndQuery(); 51 void EndQuery();
52 52
53private: 53private:
54 u64 BlockingQuery() const override; 54 u64 BlockingQuery(bool async = false) const override;
55 55
56 QueryCache& cache; 56 QueryCache& cache;
57 const VideoCore::QueryType type; 57 const VideoCore::QueryType type;
@@ -70,7 +70,7 @@ public:
70 CachedQuery(const CachedQuery&) = delete; 70 CachedQuery(const CachedQuery&) = delete;
71 CachedQuery& operator=(const CachedQuery&) = delete; 71 CachedQuery& operator=(const CachedQuery&) = delete;
72 72
73 void Flush() override; 73 u64 Flush(bool async = false) override;
74 74
75private: 75private:
76 QueryCache* cache; 76 QueryCache* cache;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4993d4709..f5baa0f3c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -63,7 +63,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
63 buffer_cache(*this, cpu_memory_, buffer_cache_runtime), 63 buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, 64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager,
65 state_tracker, gpu.ShaderNotify()), 65 state_tracker, gpu.ShaderNotify()),
66 query_cache(*this), accelerate_dma(buffer_cache, texture_cache), 66 query_cache(*this, cpu_memory_), accelerate_dma(buffer_cache, texture_cache),
67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), 67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
68 blit_image(program_manager_) {} 68 blit_image(program_manager_) {}
69 69
@@ -433,6 +433,29 @@ bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheT
433 return false; 433 return false;
434} 434}
435 435
436VideoCore::RasterizerDownloadArea RasterizerOpenGL::GetFlushArea(VAddr addr, u64 size) {
437 {
438 std::scoped_lock lock{texture_cache.mutex};
439 auto area = texture_cache.GetFlushArea(addr, size);
440 if (area) {
441 return *area;
442 }
443 }
444 {
445 std::scoped_lock lock{buffer_cache.mutex};
446 auto area = buffer_cache.GetFlushArea(addr, size);
447 if (area) {
448 return *area;
449 }
450 }
451 VideoCore::RasterizerDownloadArea new_area{
452 .start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE),
453 .end_address = Common::AlignUp(addr + size, Core::Memory::YUZU_PAGESIZE),
454 .preemtive = true,
455 };
456 return new_area;
457}
458
436void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { 459void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
437 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 460 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
438 if (addr == 0 || size == 0) { 461 if (addr == 0 || size == 0) {
@@ -1281,7 +1304,7 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
1281 const Tegra::DMA::BufferOperand& buffer_operand, 1304 const Tegra::DMA::BufferOperand& buffer_operand,
1282 const Tegra::DMA::ImageOperand& image_operand) { 1305 const Tegra::DMA::ImageOperand& image_operand) {
1283 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 1306 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1284 const auto image_id = texture_cache.DmaImageId(image_operand); 1307 const auto image_id = texture_cache.DmaImageId(image_operand, IS_IMAGE_UPLOAD);
1285 if (image_id == VideoCommon::NULL_IMAGE_ID) { 1308 if (image_id == VideoCommon::NULL_IMAGE_ID) {
1286 return false; 1309 return false;
1287 } 1310 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ad6978bd0..410d8ffc5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -95,6 +95,7 @@ public:
95 VideoCommon::CacheType which = VideoCommon::CacheType::All) override; 95 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
96 bool MustFlushRegion(VAddr addr, u64 size, 96 bool MustFlushRegion(VAddr addr, u64 size,
97 VideoCommon::CacheType which = VideoCommon::CacheType::All) override; 97 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
98 VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override;
98 void InvalidateRegion(VAddr addr, u64 size, 99 void InvalidateRegion(VAddr addr, u64 size,
99 VideoCommon::CacheType which = VideoCommon::CacheType::All) override; 100 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
100 void OnCPUWrite(VAddr addr, u64 size) override; 101 void OnCPUWrite(VAddr addr, u64 size) override;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 032a8ebc5..31118886f 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -231,7 +231,7 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
231 231
232[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime, 232[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime,
233 const VideoCommon::ImageInfo& info) { 233 const VideoCommon::ImageInfo& info) {
234 if (IsPixelFormatASTC(info.format) && !runtime.HasNativeASTC()) { 234 if (IsPixelFormatASTC(info.format) && info.size.depth == 1 && !runtime.HasNativeASTC()) {
235 return Settings::values.accelerate_astc.GetValue() && 235 return Settings::values.accelerate_astc.GetValue() &&
236 !Settings::values.async_astc.GetValue(); 236 !Settings::values.async_astc.GetValue();
237 } 237 }
@@ -861,9 +861,12 @@ GLuint Image::StorageHandle() noexcept {
861 case PixelFormat::ASTC_2D_8X5_SRGB: 861 case PixelFormat::ASTC_2D_8X5_SRGB:
862 case PixelFormat::ASTC_2D_5X4_SRGB: 862 case PixelFormat::ASTC_2D_5X4_SRGB:
863 case PixelFormat::ASTC_2D_5X5_SRGB: 863 case PixelFormat::ASTC_2D_5X5_SRGB:
864 case PixelFormat::ASTC_2D_10X5_SRGB:
865 case PixelFormat::ASTC_2D_10X6_SRGB:
864 case PixelFormat::ASTC_2D_10X8_SRGB: 866 case PixelFormat::ASTC_2D_10X8_SRGB:
865 case PixelFormat::ASTC_2D_6X6_SRGB: 867 case PixelFormat::ASTC_2D_6X6_SRGB:
866 case PixelFormat::ASTC_2D_10X10_SRGB: 868 case PixelFormat::ASTC_2D_10X10_SRGB:
869 case PixelFormat::ASTC_2D_12X10_SRGB:
867 case PixelFormat::ASTC_2D_12X12_SRGB: 870 case PixelFormat::ASTC_2D_12X12_SRGB:
868 case PixelFormat::ASTC_2D_8X6_SRGB: 871 case PixelFormat::ASTC_2D_8X6_SRGB:
869 case PixelFormat::ASTC_2D_6X5_SRGB: 872 case PixelFormat::ASTC_2D_6X5_SRGB:
@@ -1123,7 +1126,8 @@ bool Image::ScaleDown(bool ignore) {
1123 1126
1124ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info, 1127ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
1125 ImageId image_id_, Image& image, const SlotVector<Image>&) 1128 ImageId image_id_, Image& image, const SlotVector<Image>&)
1126 : VideoCommon::ImageViewBase{info, image.info, image_id_}, views{runtime.null_image_views} { 1129 : VideoCommon::ImageViewBase{info, image.info, image_id_, image.gpu_addr},
1130 views{runtime.null_image_views} {
1127 const Device& device = runtime.device; 1131 const Device& device = runtime.device;
1128 if (True(image.flags & ImageFlagBits::Converted)) { 1132 if (True(image.flags & ImageFlagBits::Converted)) {
1129 internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; 1133 internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
@@ -1214,12 +1218,12 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1214 1218
1215ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, 1219ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
1216 const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_) 1220 const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
1217 : VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_}, 1221 : VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
1218 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} 1222 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
1219 1223
1220ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, 1224ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
1221 const VideoCommon::ImageViewInfo& view_info) 1225 const VideoCommon::ImageViewInfo& view_info)
1222 : VideoCommon::ImageViewBase{info, view_info} {} 1226 : VideoCommon::ImageViewBase{info, view_info, 0} {}
1223 1227
1224ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params) 1228ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
1225 : VideoCommon::ImageViewBase{params}, views{runtime.null_image_views} {} 1229 : VideoCommon::ImageViewBase{params}, views{runtime.null_image_views} {}
@@ -1279,7 +1283,7 @@ GLuint ImageView::MakeView(Shader::TextureType view_type, GLenum view_format) {
1279 ApplySwizzle(view.handle, format, casted_swizzle); 1283 ApplySwizzle(view.handle, format, casted_swizzle);
1280 } 1284 }
1281 if (set_object_label) { 1285 if (set_object_label) {
1282 const std::string name = VideoCommon::Name(*this); 1286 const std::string name = VideoCommon::Name(*this, gpu_addr);
1283 glObjectLabel(GL_TEXTURE, view.handle, static_cast<GLsizei>(name.size()), name.data()); 1287 glObjectLabel(GL_TEXTURE, view.handle, static_cast<GLsizei>(name.size()), name.data());
1284 } 1288 }
1285 return view.handle; 1289 return view.handle;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 0dd039ed2..1190999a8 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -314,7 +314,6 @@ private:
314 std::unique_ptr<StorageViews> storage_views; 314 std::unique_ptr<StorageViews> storage_views;
315 GLenum internal_format = GL_NONE; 315 GLenum internal_format = GL_NONE;
316 GLuint default_handle = 0; 316 GLuint default_handle = 0;
317 GPUVAddr gpu_addr = 0;
318 u32 buffer_size = 0; 317 u32 buffer_size = 0;
319 GLuint original_texture = 0; 318 GLuint original_texture = 0;
320 int num_samples = 0; 319 int num_samples = 0;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index ef1190e1f..c7dc7e0a1 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -100,10 +100,13 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
100 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM 100 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM
101 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB 101 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
102 {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM 102 {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM
103 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR}, // ASTC_2D_10X6_SRGB
103 {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM 104 {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM
104 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB 105 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB
105 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM 106 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM
106 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB 107 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
108 {GL_COMPRESSED_RGBA_ASTC_12x10_KHR}, // ASTC_2D_12X10_UNORM
109 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR}, // ASTC_2D_12X10_SRGB
107 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM 110 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM
108 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB 111 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB
109 {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM 112 {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 5dce51be8..8853cf0f7 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -197,10 +197,13 @@ struct FormatTuple {
197 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM 197 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM
198 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB 198 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB
199 {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM 199 {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM
200 {VK_FORMAT_ASTC_10x6_SRGB_BLOCK}, // ASTC_2D_10X6_SRGB
200 {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM 201 {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM
201 {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB 202 {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB
202 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM 203 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM
203 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB 204 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB
205 {VK_FORMAT_ASTC_12x10_UNORM_BLOCK}, // ASTC_2D_12X10_UNORM
206 {VK_FORMAT_ASTC_12x10_SRGB_BLOCK}, // ASTC_2D_12X10_SRGB
204 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM 207 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM
205 {VK_FORMAT_ASTC_12x12_SRGB_BLOCK}, // ASTC_2D_12X12_SRGB 208 {VK_FORMAT_ASTC_12x12_SRGB_BLOCK}, // ASTC_2D_12X12_SRGB
206 {VK_FORMAT_ASTC_8x6_UNORM_BLOCK}, // ASTC_2D_8X6_UNORM 209 {VK_FORMAT_ASTC_8x6_UNORM_BLOCK}, // ASTC_2D_8X6_UNORM
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 2a8d9e377..8e31eba34 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -88,13 +88,14 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
88 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 88 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
89 Settings::values.renderer_debug.GetValue())), 89 Settings::values.renderer_debug.GetValue())),
90 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 90 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
91 surface(CreateSurface(instance, render_window)), 91 surface(CreateSurface(instance, render_window.GetWindowInfo())),
92 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), 92 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
93 state_tracker(), scheduler(device, state_tracker), 93 state_tracker(), scheduler(device, state_tracker),
94 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, 94 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
95 render_window.GetFramebufferLayout().height, false), 95 render_window.GetFramebufferLayout().height, false),
96 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, 96 present_manager(render_window, device, memory_allocator, scheduler, swapchain),
97 screen_info), 97 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager,
98 scheduler, screen_info),
98 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, 99 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,
99 state_tracker, scheduler) { 100 state_tracker, scheduler) {
100 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { 101 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
@@ -121,46 +122,19 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
121 return; 122 return;
122 } 123 }
123 // Update screen info if the framebuffer size has changed. 124 // Update screen info if the framebuffer size has changed.
124 if (screen_info.width != framebuffer->width || screen_info.height != framebuffer->height) { 125 screen_info.width = framebuffer->width;
125 screen_info.width = framebuffer->width; 126 screen_info.height = framebuffer->height;
126 screen_info.height = framebuffer->height; 127
127 }
128 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 128 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
129 const bool use_accelerated = 129 const bool use_accelerated =
130 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 130 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
131 const bool is_srgb = use_accelerated && screen_info.is_srgb; 131 const bool is_srgb = use_accelerated && screen_info.is_srgb;
132 RenderScreenshot(*framebuffer, use_accelerated); 132 RenderScreenshot(*framebuffer, use_accelerated);
133 133
134 bool has_been_recreated = false; 134 Frame* frame = present_manager.GetRenderFrame();
135 const auto recreate_swapchain = [&](u32 width, u32 height) { 135 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
136 if (!has_been_recreated) { 136 scheduler.Flush(*frame->render_ready);
137 has_been_recreated = true; 137 present_manager.Present(frame);
138 scheduler.Finish();
139 }
140 swapchain.Create(width, height, is_srgb);
141 };
142
143 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
144 if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width ||
145 swapchain.GetHeight() != layout.height) {
146 recreate_swapchain(layout.width, layout.height);
147 }
148 bool is_outdated;
149 do {
150 swapchain.AcquireNextImage();
151 is_outdated = swapchain.IsOutDated();
152 if (is_outdated) {
153 recreate_swapchain(layout.width, layout.height);
154 }
155 } while (is_outdated);
156 if (has_been_recreated) {
157 blit_screen.Recreate();
158 }
159 const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
160 const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
161 scheduler.Flush(render_semaphore, present_semaphore);
162 scheduler.WaitWorker();
163 swapchain.Present(render_semaphore);
164 138
165 gpu.RendererFrameEndNotify(); 139 gpu.RendererFrameEndNotify();
166 rasterizer.TickFrame(); 140 rasterizer.TickFrame();
@@ -246,8 +220,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
246 }); 220 });
247 const VkExtent2D render_area{.width = layout.width, .height = layout.height}; 221 const VkExtent2D render_area{.width = layout.width, .height = layout.height};
248 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area); 222 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
249 // Since we're not rendering to the screen, ignore the render semaphore. 223 blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated);
250 void(blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated));
251 224
252 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); 225 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
253 const VkBufferCreateInfo dst_buffer_info{ 226 const VkBufferCreateInfo dst_buffer_info{
@@ -270,7 +243,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
270 .pNext = nullptr, 243 .pNext = nullptr,
271 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, 244 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
272 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, 245 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
273 .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 246 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
274 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 247 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
275 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 248 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
276 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 249 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 009e75e0d..f44367cb2 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -9,6 +9,7 @@
9#include "common/dynamic_library.h" 9#include "common/dynamic_library.h"
10#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
11#include "video_core/renderer_vulkan/vk_blit_screen.h" 11#include "video_core/renderer_vulkan/vk_blit_screen.h"
12#include "video_core/renderer_vulkan/vk_present_manager.h"
12#include "video_core/renderer_vulkan/vk_rasterizer.h" 13#include "video_core/renderer_vulkan/vk_rasterizer.h"
13#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/vk_state_tracker.h" 15#include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -76,6 +77,7 @@ private:
76 StateTracker state_tracker; 77 StateTracker state_tracker;
77 Scheduler scheduler; 78 Scheduler scheduler;
78 Swapchain swapchain; 79 Swapchain swapchain;
80 PresentManager present_manager;
79 BlitScreen blit_screen; 81 BlitScreen blit_screen;
80 RasterizerVulkan rasterizer; 82 RasterizerVulkan rasterizer;
81 std::optional<TurboMode> turbo_mode; 83 std::optional<TurboMode> turbo_mode;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 2f0cc27e8..1e0fdd3d9 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -122,10 +122,12 @@ struct BlitScreen::BufferData {
122 122
123BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_, 123BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_,
124 const Device& device_, MemoryAllocator& memory_allocator_, 124 const Device& device_, MemoryAllocator& memory_allocator_,
125 Swapchain& swapchain_, Scheduler& scheduler_, const ScreenInfo& screen_info_) 125 Swapchain& swapchain_, PresentManager& present_manager_,
126 Scheduler& scheduler_, const ScreenInfo& screen_info_)
126 : cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_}, 127 : cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_},
127 memory_allocator{memory_allocator_}, swapchain{swapchain_}, scheduler{scheduler_}, 128 memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_},
128 image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { 129 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_},
130 current_srgb{swapchain.IsSrgb()}, image_view_format{swapchain.GetImageViewFormat()} {
129 resource_ticks.resize(image_count); 131 resource_ticks.resize(image_count);
130 132
131 CreateStaticResources(); 133 CreateStaticResources();
@@ -135,25 +137,20 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin
135BlitScreen::~BlitScreen() = default; 137BlitScreen::~BlitScreen() = default;
136 138
137void BlitScreen::Recreate() { 139void BlitScreen::Recreate() {
140 present_manager.WaitPresent();
141 scheduler.Finish();
142 device.GetLogical().WaitIdle();
138 CreateDynamicResources(); 143 CreateDynamicResources();
139} 144}
140 145
141VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, 146void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
142 const VkFramebuffer& host_framebuffer, 147 const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout,
143 const Layout::FramebufferLayout layout, VkExtent2D render_area, 148 VkExtent2D render_area, bool use_accelerated) {
144 bool use_accelerated) {
145 RefreshResources(framebuffer); 149 RefreshResources(framebuffer);
146 150
147 // Finish any pending renderpass 151 // Finish any pending renderpass
148 scheduler.RequestOutsideRenderPassOperationContext(); 152 scheduler.RequestOutsideRenderPassOperationContext();
149 153
150 if (const auto swapchain_images = swapchain.GetImageCount(); swapchain_images != image_count) {
151 image_count = swapchain_images;
152 Recreate();
153 }
154
155 const std::size_t image_index = swapchain.GetImageIndex();
156
157 scheduler.Wait(resource_ticks[image_index]); 154 scheduler.Wait(resource_ticks[image_index]);
158 resource_ticks[image_index] = scheduler.CurrentTick(); 155 resource_ticks[image_index] = scheduler.CurrentTick();
159 156
@@ -169,7 +166,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
169 std::memcpy(mapped_span.data(), &data, sizeof(data)); 166 std::memcpy(mapped_span.data(), &data, sizeof(data));
170 167
171 if (!use_accelerated) { 168 if (!use_accelerated) {
172 const u64 image_offset = GetRawImageOffset(framebuffer, image_index); 169 const u64 image_offset = GetRawImageOffset(framebuffer);
173 170
174 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset; 171 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
175 const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr); 172 const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr);
@@ -204,8 +201,8 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
204 .depth = 1, 201 .depth = 1,
205 }, 202 },
206 }; 203 };
207 scheduler.Record([this, copy, image_index](vk::CommandBuffer cmdbuf) { 204 scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
208 const VkImage image = *raw_images[image_index]; 205 const VkImage image = *raw_images[index];
209 const VkImageMemoryBarrier base_barrier{ 206 const VkImageMemoryBarrier base_barrier{
210 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 207 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
211 .pNext = nullptr, 208 .pNext = nullptr,
@@ -245,14 +242,15 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
245 242
246 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); 243 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue();
247 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { 244 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) {
248 UpdateAADescriptorSet(image_index, source_image_view, false); 245 UpdateAADescriptorSet(source_image_view, false);
249 const u32 up_scale = Settings::values.resolution_info.up_scale; 246 const u32 up_scale = Settings::values.resolution_info.up_scale;
250 const u32 down_shift = Settings::values.resolution_info.down_shift; 247 const u32 down_shift = Settings::values.resolution_info.down_shift;
251 VkExtent2D size{ 248 VkExtent2D size{
252 .width = (up_scale * framebuffer.width) >> down_shift, 249 .width = (up_scale * framebuffer.width) >> down_shift,
253 .height = (up_scale * framebuffer.height) >> down_shift, 250 .height = (up_scale * framebuffer.height) >> down_shift,
254 }; 251 };
255 scheduler.Record([this, image_index, size, anti_alias_pass](vk::CommandBuffer cmdbuf) { 252 scheduler.Record([this, index = image_index, size,
253 anti_alias_pass](vk::CommandBuffer cmdbuf) {
256 const VkImageMemoryBarrier base_barrier{ 254 const VkImageMemoryBarrier base_barrier{
257 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 255 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
258 .pNext = nullptr, 256 .pNext = nullptr,
@@ -326,7 +324,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
326 324
327 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 325 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
328 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, 326 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0,
329 aa_descriptor_sets[image_index], {}); 327 aa_descriptor_sets[index], {});
330 cmdbuf.Draw(4, 1, 0, 0); 328 cmdbuf.Draw(4, 1, 0, 0);
331 cmdbuf.EndRenderPass(); 329 cmdbuf.EndRenderPass();
332 330
@@ -369,81 +367,99 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
369 }; 367 };
370 VkImageView fsr_image_view = 368 VkImageView fsr_image_view =
371 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); 369 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
372 UpdateDescriptorSet(image_index, fsr_image_view, true); 370 UpdateDescriptorSet(fsr_image_view, true);
373 } else { 371 } else {
374 const bool is_nn = 372 const bool is_nn =
375 Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; 373 Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor;
376 UpdateDescriptorSet(image_index, source_image_view, is_nn); 374 UpdateDescriptorSet(source_image_view, is_nn);
377 } 375 }
378 376
379 scheduler.Record( 377 scheduler.Record([this, host_framebuffer, index = image_index,
380 [this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) { 378 size = render_area](vk::CommandBuffer cmdbuf) {
381 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 379 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
382 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 380 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
383 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; 381 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
384 const VkClearValue clear_color{ 382 const VkClearValue clear_color{
385 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, 383 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
386 }; 384 };
387 const VkRenderPassBeginInfo renderpass_bi{ 385 const VkRenderPassBeginInfo renderpass_bi{
388 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 386 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
389 .pNext = nullptr, 387 .pNext = nullptr,
390 .renderPass = *renderpass, 388 .renderPass = *renderpass,
391 .framebuffer = host_framebuffer, 389 .framebuffer = host_framebuffer,
392 .renderArea = 390 .renderArea =
393 { 391 {
394 .offset = {0, 0}, 392 .offset = {0, 0},
395 .extent = size, 393 .extent = size,
396 }, 394 },
397 .clearValueCount = 1, 395 .clearValueCount = 1,
398 .pClearValues = &clear_color, 396 .pClearValues = &clear_color,
399 }; 397 };
400 const VkViewport viewport{ 398 const VkViewport viewport{
401 .x = 0.0f, 399 .x = 0.0f,
402 .y = 0.0f, 400 .y = 0.0f,
403 .width = static_cast<float>(size.width), 401 .width = static_cast<float>(size.width),
404 .height = static_cast<float>(size.height), 402 .height = static_cast<float>(size.height),
405 .minDepth = 0.0f, 403 .minDepth = 0.0f,
406 .maxDepth = 1.0f, 404 .maxDepth = 1.0f,
407 }; 405 };
408 const VkRect2D scissor{ 406 const VkRect2D scissor{
409 .offset = {0, 0}, 407 .offset = {0, 0},
410 .extent = size, 408 .extent = size,
411 }; 409 };
412 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); 410 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
413 auto graphics_pipeline = [this]() { 411 auto graphics_pipeline = [this]() {
414 switch (Settings::values.scaling_filter.GetValue()) { 412 switch (Settings::values.scaling_filter.GetValue()) {
415 case Settings::ScalingFilter::NearestNeighbor: 413 case Settings::ScalingFilter::NearestNeighbor:
416 case Settings::ScalingFilter::Bilinear: 414 case Settings::ScalingFilter::Bilinear:
417 return *bilinear_pipeline; 415 return *bilinear_pipeline;
418 case Settings::ScalingFilter::Bicubic: 416 case Settings::ScalingFilter::Bicubic:
419 return *bicubic_pipeline; 417 return *bicubic_pipeline;
420 case Settings::ScalingFilter::Gaussian: 418 case Settings::ScalingFilter::Gaussian:
421 return *gaussian_pipeline; 419 return *gaussian_pipeline;
422 case Settings::ScalingFilter::ScaleForce: 420 case Settings::ScalingFilter::ScaleForce:
423 return *scaleforce_pipeline; 421 return *scaleforce_pipeline;
424 default: 422 default:
425 return *bilinear_pipeline; 423 return *bilinear_pipeline;
426 } 424 }
427 }(); 425 }();
428 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); 426 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
429 cmdbuf.SetViewport(0, viewport); 427 cmdbuf.SetViewport(0, viewport);
430 cmdbuf.SetScissor(0, scissor); 428 cmdbuf.SetScissor(0, scissor);
431 429
432 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 430 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
433 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, 431 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
434 descriptor_sets[image_index], {}); 432 descriptor_sets[index], {});
435 cmdbuf.Draw(4, 1, 0, 0); 433 cmdbuf.Draw(4, 1, 0, 0);
436 cmdbuf.EndRenderPass(); 434 cmdbuf.EndRenderPass();
437 }); 435 });
438 return *semaphores[image_index];
439} 436}
440 437
441VkSemaphore BlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer, 438void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
442 bool use_accelerated) { 439 bool use_accelerated, bool is_srgb) {
443 const std::size_t image_index = swapchain.GetImageIndex(); 440 // Recreate dynamic resources if the the image count or colorspace changed
444 const VkExtent2D render_area = swapchain.GetSize(); 441 if (const std::size_t swapchain_images = swapchain.GetImageCount();
442 swapchain_images != image_count || current_srgb != is_srgb) {
443 current_srgb = is_srgb;
444 image_view_format = current_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
445 image_count = swapchain_images;
446 Recreate();
447 }
448
449 // Recreate the presentation frame if the dimensions of the window changed
445 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 450 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
446 return Draw(framebuffer, *framebuffers[image_index], layout, render_area, use_accelerated); 451 if (layout.width != frame->width || layout.height != frame->height ||
452 is_srgb != frame->is_srgb) {
453 Recreate();
454 present_manager.RecreateFrame(frame, layout.width, layout.height, is_srgb,
455 image_view_format, *renderpass);
456 }
457
458 const VkExtent2D render_area{frame->width, frame->height};
459 Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated);
460 if (++image_index >= image_count) {
461 image_index = 0;
462 }
447} 463}
448 464
449vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { 465vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) {
@@ -471,13 +487,11 @@ void BlitScreen::CreateStaticResources() {
471} 487}
472 488
473void BlitScreen::CreateDynamicResources() { 489void BlitScreen::CreateDynamicResources() {
474 CreateSemaphores();
475 CreateDescriptorPool(); 490 CreateDescriptorPool();
476 CreateDescriptorSetLayout(); 491 CreateDescriptorSetLayout();
477 CreateDescriptorSets(); 492 CreateDescriptorSets();
478 CreatePipelineLayout(); 493 CreatePipelineLayout();
479 CreateRenderPass(); 494 CreateRenderPass();
480 CreateFramebuffers();
481 CreateGraphicsPipeline(); 495 CreateGraphicsPipeline();
482 fsr.reset(); 496 fsr.reset();
483 smaa.reset(); 497 smaa.reset();
@@ -525,11 +539,6 @@ void BlitScreen::CreateShaders() {
525 } 539 }
526} 540}
527 541
528void BlitScreen::CreateSemaphores() {
529 semaphores.resize(image_count);
530 std::ranges::generate(semaphores, [this] { return device.GetLogical().CreateSemaphore(); });
531}
532
533void BlitScreen::CreateDescriptorPool() { 542void BlitScreen::CreateDescriptorPool() {
534 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ 543 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
535 { 544 {
@@ -571,10 +580,10 @@ void BlitScreen::CreateDescriptorPool() {
571} 580}
572 581
573void BlitScreen::CreateRenderPass() { 582void BlitScreen::CreateRenderPass() {
574 renderpass = CreateRenderPassImpl(swapchain.GetImageViewFormat()); 583 renderpass = CreateRenderPassImpl(image_view_format);
575} 584}
576 585
577vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present) { 586vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
578 const VkAttachmentDescription color_attachment{ 587 const VkAttachmentDescription color_attachment{
579 .flags = 0, 588 .flags = 0,
580 .format = format, 589 .format = format,
@@ -584,7 +593,7 @@ vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present
584 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, 593 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
585 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, 594 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
586 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 595 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
587 .finalLayout = is_present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_GENERAL, 596 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
588 }; 597 };
589 598
590 const VkAttachmentReference color_attachment_ref{ 599 const VkAttachmentReference color_attachment_ref{
@@ -1052,16 +1061,6 @@ void BlitScreen::CreateSampler() {
1052 nn_sampler = device.GetLogical().CreateSampler(ci_nn); 1061 nn_sampler = device.GetLogical().CreateSampler(ci_nn);
1053} 1062}
1054 1063
1055void BlitScreen::CreateFramebuffers() {
1056 const VkExtent2D size{swapchain.GetSize()};
1057 framebuffers.resize(image_count);
1058
1059 for (std::size_t i = 0; i < image_count; ++i) {
1060 const VkImageView image_view{swapchain.GetImageViewIndex(i)};
1061 framebuffers[i] = CreateFramebuffer(image_view, size, renderpass);
1062 }
1063}
1064
1065void BlitScreen::ReleaseRawImages() { 1064void BlitScreen::ReleaseRawImages() {
1066 for (const u64 tick : resource_ticks) { 1065 for (const u64 tick : resource_ticks) {
1067 scheduler.Wait(tick); 1066 scheduler.Wait(tick);
@@ -1175,7 +1174,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1175 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); 1174 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1176 return; 1175 return;
1177 } 1176 }
1178 aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer), false); 1177 aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer));
1179 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); 1178 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1180 1179
1181 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ 1180 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{
@@ -1319,8 +1318,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1319 aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci); 1318 aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci);
1320} 1319}
1321 1320
1322void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, 1321void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const {
1323 bool nn) const {
1324 const VkDescriptorImageInfo image_info{ 1322 const VkDescriptorImageInfo image_info{
1325 .sampler = nn ? *nn_sampler : *sampler, 1323 .sampler = nn ? *nn_sampler : *sampler,
1326 .imageView = image_view, 1324 .imageView = image_view,
@@ -1356,8 +1354,7 @@ void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView imag
1356 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {}); 1354 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {});
1357} 1355}
1358 1356
1359void BlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, 1357void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const {
1360 bool nn) const {
1361 const VkDescriptorBufferInfo buffer_info{ 1358 const VkDescriptorBufferInfo buffer_info{
1362 .buffer = *buffer, 1359 .buffer = *buffer,
1363 .offset = offsetof(BufferData, uniform), 1360 .offset = offsetof(BufferData, uniform),
@@ -1480,8 +1477,7 @@ u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer)
1480 return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count; 1477 return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count;
1481} 1478}
1482 1479
1483u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, 1480u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const {
1484 std::size_t image_index) const {
1485 constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData)); 1481 constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData));
1486 return first_image_offset + GetSizeInBytes(framebuffer) * image_index; 1482 return first_image_offset + GetSizeInBytes(framebuffer) * image_index;
1487} 1483}
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index ebe10b08b..68ec20253 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -5,6 +5,7 @@
5 5
6#include <memory> 6#include <memory>
7 7
8#include "core/frontend/framebuffer_layout.h"
8#include "video_core/vulkan_common/vulkan_memory_allocator.h" 9#include "video_core/vulkan_common/vulkan_memory_allocator.h"
9#include "video_core/vulkan_common/vulkan_wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
10 11
@@ -42,6 +43,9 @@ class RasterizerVulkan;
42class Scheduler; 43class Scheduler;
43class SMAA; 44class SMAA;
44class Swapchain; 45class Swapchain;
46class PresentManager;
47
48struct Frame;
45 49
46struct ScreenInfo { 50struct ScreenInfo {
47 VkImage image{}; 51 VkImage image{};
@@ -55,18 +59,17 @@ class BlitScreen {
55public: 59public:
56 explicit BlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window, 60 explicit BlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window,
57 const Device& device, MemoryAllocator& memory_manager, Swapchain& swapchain, 61 const Device& device, MemoryAllocator& memory_manager, Swapchain& swapchain,
58 Scheduler& scheduler, const ScreenInfo& screen_info); 62 PresentManager& present_manager, Scheduler& scheduler,
63 const ScreenInfo& screen_info);
59 ~BlitScreen(); 64 ~BlitScreen();
60 65
61 void Recreate(); 66 void Recreate();
62 67
63 [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer, 68 void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer,
64 const VkFramebuffer& host_framebuffer, 69 const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated);
65 const Layout::FramebufferLayout layout, VkExtent2D render_area,
66 bool use_accelerated);
67 70
68 [[nodiscard]] VkSemaphore DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer, 71 void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
69 bool use_accelerated); 72 bool use_accelerated, bool is_srgb);
70 73
71 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, 74 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
72 VkExtent2D extent); 75 VkExtent2D extent);
@@ -79,10 +82,9 @@ private:
79 82
80 void CreateStaticResources(); 83 void CreateStaticResources();
81 void CreateShaders(); 84 void CreateShaders();
82 void CreateSemaphores();
83 void CreateDescriptorPool(); 85 void CreateDescriptorPool();
84 void CreateRenderPass(); 86 void CreateRenderPass();
85 vk::RenderPass CreateRenderPassImpl(VkFormat, bool is_present = true); 87 vk::RenderPass CreateRenderPassImpl(VkFormat format);
86 void CreateDescriptorSetLayout(); 88 void CreateDescriptorSetLayout();
87 void CreateDescriptorSets(); 89 void CreateDescriptorSets();
88 void CreatePipelineLayout(); 90 void CreatePipelineLayout();
@@ -90,15 +92,14 @@ private:
90 void CreateSampler(); 92 void CreateSampler();
91 93
92 void CreateDynamicResources(); 94 void CreateDynamicResources();
93 void CreateFramebuffers();
94 95
95 void RefreshResources(const Tegra::FramebufferConfig& framebuffer); 96 void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
96 void ReleaseRawImages(); 97 void ReleaseRawImages();
97 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); 98 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
98 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); 99 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
99 100
100 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; 101 void UpdateDescriptorSet(VkImageView image_view, bool nn) const;
101 void UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; 102 void UpdateAADescriptorSet(VkImageView image_view, bool nn) const;
102 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const; 103 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
103 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, 104 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
104 const Layout::FramebufferLayout layout) const; 105 const Layout::FramebufferLayout layout) const;
@@ -107,16 +108,17 @@ private:
107 void CreateFSR(); 108 void CreateFSR();
108 109
109 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; 110 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
110 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, 111 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const;
111 std::size_t image_index) const;
112 112
113 Core::Memory::Memory& cpu_memory; 113 Core::Memory::Memory& cpu_memory;
114 Core::Frontend::EmuWindow& render_window; 114 Core::Frontend::EmuWindow& render_window;
115 const Device& device; 115 const Device& device;
116 MemoryAllocator& memory_allocator; 116 MemoryAllocator& memory_allocator;
117 Swapchain& swapchain; 117 Swapchain& swapchain;
118 PresentManager& present_manager;
118 Scheduler& scheduler; 119 Scheduler& scheduler;
119 std::size_t image_count; 120 std::size_t image_count;
121 std::size_t image_index{};
120 const ScreenInfo& screen_info; 122 const ScreenInfo& screen_info;
121 123
122 vk::ShaderModule vertex_shader; 124 vk::ShaderModule vertex_shader;
@@ -135,7 +137,6 @@ private:
135 vk::Pipeline gaussian_pipeline; 137 vk::Pipeline gaussian_pipeline;
136 vk::Pipeline scaleforce_pipeline; 138 vk::Pipeline scaleforce_pipeline;
137 vk::RenderPass renderpass; 139 vk::RenderPass renderpass;
138 std::vector<vk::Framebuffer> framebuffers;
139 vk::DescriptorSets descriptor_sets; 140 vk::DescriptorSets descriptor_sets;
140 vk::Sampler nn_sampler; 141 vk::Sampler nn_sampler;
141 vk::Sampler sampler; 142 vk::Sampler sampler;
@@ -145,7 +146,6 @@ private:
145 146
146 std::vector<u64> resource_ticks; 147 std::vector<u64> resource_ticks;
147 148
148 std::vector<vk::Semaphore> semaphores;
149 std::vector<vk::Image> raw_images; 149 std::vector<vk::Image> raw_images;
150 std::vector<vk::ImageView> raw_image_views; 150 std::vector<vk::ImageView> raw_image_views;
151 std::vector<MemoryCommit> raw_buffer_commits; 151 std::vector<MemoryCommit> raw_buffer_commits;
@@ -164,6 +164,8 @@ private:
164 u32 raw_width = 0; 164 u32 raw_width = 0;
165 u32 raw_height = 0; 165 u32 raw_height = 0;
166 Service::android::PixelFormat pixel_format{}; 166 Service::android::PixelFormat pixel_format{};
167 bool current_srgb;
168 VkFormat image_view_format;
167 169
168 std::unique_ptr<FSR> fsr; 170 std::unique_ptr<FSR> fsr;
169 std::unique_ptr<SMAA> smaa; 171 std::unique_ptr<SMAA> smaa;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 9cbcb3c8f..510602e8e 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -314,8 +314,12 @@ StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
314 return staging_pool.Request(size, MemoryUsage::Upload); 314 return staging_pool.Request(size, MemoryUsage::Upload);
315} 315}
316 316
317StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size) { 317StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
318 return staging_pool.Request(size, MemoryUsage::Download); 318 return staging_pool.Request(size, MemoryUsage::Download, deferred);
319}
320
321void BufferCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
322 staging_pool.FreeDeferred(ref);
319} 323}
320 324
321u64 BufferCacheRuntime::GetDeviceLocalMemory() const { 325u64 BufferCacheRuntime::GetDeviceLocalMemory() const {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 183b33632..879f1ed94 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -3,7 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "video_core/buffer_cache/buffer_cache.h" 6#include "video_core/buffer_cache/buffer_cache_base.h"
7#include "video_core/buffer_cache/memory_tracker_base.h"
7#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
8#include "video_core/renderer_vulkan/vk_compute_pass.h" 9#include "video_core/renderer_vulkan/vk_compute_pass.h"
9#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 10#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -75,7 +76,9 @@ public:
75 76
76 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); 77 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
77 78
78 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); 79 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
80
81 void FreeDeferredStagingBuffer(StagingBufferRef& ref);
79 82
80 void PreCopyBarrier(); 83 void PreCopyBarrier();
81 84
@@ -142,6 +145,8 @@ private:
142struct BufferCacheParams { 145struct BufferCacheParams {
143 using Runtime = Vulkan::BufferCacheRuntime; 146 using Runtime = Vulkan::BufferCacheRuntime;
144 using Buffer = Vulkan::Buffer; 147 using Buffer = Vulkan::Buffer;
148 using Async_Buffer = Vulkan::StagingBufferRef;
149 using MemoryTracker = VideoCommon::MemoryTrackerBase<VideoCore::RasterizerInterface>;
145 150
146 static constexpr bool IS_OPENGL = false; 151 static constexpr bool IS_OPENGL = false;
147 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = false; 152 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = false;
@@ -150,6 +155,7 @@ struct BufferCacheParams {
150 static constexpr bool NEEDS_BIND_STORAGE_INDEX = false; 155 static constexpr bool NEEDS_BIND_STORAGE_INDEX = false;
151 static constexpr bool USE_MEMORY_MAPS = true; 156 static constexpr bool USE_MEMORY_MAPS = true;
152 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false; 157 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
158 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
153}; 159};
154 160
155using BufferCache = VideoCommon::BufferCache<BufferCacheParams>; 161using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache_base.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache_base.cpp
new file mode 100644
index 000000000..f9e271507
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache_base.cpp
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/buffer_cache/buffer_cache.h"
5#include "video_core/renderer_vulkan/vk_buffer_cache.h"
6
7namespace VideoCommon {
8template class VideoCommon::BufferCache<Vulkan::BufferCacheParams>;
9}
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index 0214b103a..fad9e3832 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -5,6 +5,7 @@
5 5
6#include "video_core/renderer_vulkan/vk_buffer_cache.h" 6#include "video_core/renderer_vulkan/vk_buffer_cache.h"
7#include "video_core/renderer_vulkan/vk_fence_manager.h" 7#include "video_core/renderer_vulkan/vk_fence_manager.h"
8#include "video_core/renderer_vulkan/vk_query_cache.h"
8#include "video_core/renderer_vulkan/vk_scheduler.h" 9#include "video_core/renderer_vulkan/vk_scheduler.h"
9#include "video_core/renderer_vulkan/vk_texture_cache.h" 10#include "video_core/renderer_vulkan/vk_texture_cache.h"
10#include "video_core/vulkan_common/vulkan_device.h" 11#include "video_core/vulkan_common/vulkan_device.h"
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index 7fe2afcd9..145359d4e 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -40,7 +40,16 @@ private:
40}; 40};
41using Fence = std::shared_ptr<InnerFence>; 41using Fence = std::shared_ptr<InnerFence>;
42 42
43using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; 43struct FenceManagerParams {
44 using FenceType = Fence;
45 using BufferCacheType = BufferCache;
46 using TextureCacheType = TextureCache;
47 using QueryCacheType = QueryCache;
48
49 static constexpr bool HAS_ASYNC_CHECK = true;
50};
51
52using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>;
44 53
45class FenceManager final : public GenericFenceManager { 54class FenceManager final : public GenericFenceManager {
46public: 55public:
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
new file mode 100644
index 000000000..c49583013
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -0,0 +1,457 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/microprofile.h"
5#include "common/settings.h"
6#include "common/thread.h"
7#include "video_core/renderer_vulkan/vk_present_manager.h"
8#include "video_core/renderer_vulkan/vk_scheduler.h"
9#include "video_core/renderer_vulkan/vk_swapchain.h"
10#include "video_core/vulkan_common/vulkan_device.h"
11
12namespace Vulkan {
13
14MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128));
15MICROPROFILE_DEFINE(Vulkan_CopyToSwapchain, "Vulkan", "Copy to swapchain", MP_RGB(192, 255, 192));
16
17namespace {
18
19bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat format) {
20 const VkFormatProperties props{physical_device.GetFormatProperties(format)};
21 return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT);
22}
23
24[[nodiscard]] VkImageSubresourceLayers MakeImageSubresourceLayers() {
25 return VkImageSubresourceLayers{
26 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
27 .mipLevel = 0,
28 .baseArrayLayer = 0,
29 .layerCount = 1,
30 };
31}
32
33[[nodiscard]] VkImageBlit MakeImageBlit(s32 frame_width, s32 frame_height, s32 swapchain_width,
34 s32 swapchain_height) {
35 return VkImageBlit{
36 .srcSubresource = MakeImageSubresourceLayers(),
37 .srcOffsets =
38 {
39 {
40 .x = 0,
41 .y = 0,
42 .z = 0,
43 },
44 {
45 .x = frame_width,
46 .y = frame_height,
47 .z = 1,
48 },
49 },
50 .dstSubresource = MakeImageSubresourceLayers(),
51 .dstOffsets =
52 {
53 {
54 .x = 0,
55 .y = 0,
56 .z = 0,
57 },
58 {
59 .x = swapchain_width,
60 .y = swapchain_height,
61 .z = 1,
62 },
63 },
64 };
65}
66
67[[nodiscard]] VkImageCopy MakeImageCopy(u32 frame_width, u32 frame_height, u32 swapchain_width,
68 u32 swapchain_height) {
69 return VkImageCopy{
70 .srcSubresource = MakeImageSubresourceLayers(),
71 .srcOffset =
72 {
73 .x = 0,
74 .y = 0,
75 .z = 0,
76 },
77 .dstSubresource = MakeImageSubresourceLayers(),
78 .dstOffset =
79 {
80 .x = 0,
81 .y = 0,
82 .z = 0,
83 },
84 .extent =
85 {
86 .width = std::min(frame_width, swapchain_width),
87 .height = std::min(frame_height, swapchain_height),
88 .depth = 1,
89 },
90 };
91}
92
93} // Anonymous namespace
94
95PresentManager::PresentManager(Core::Frontend::EmuWindow& render_window_, const Device& device_,
96 MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
97 Swapchain& swapchain_)
98 : render_window{render_window_}, device{device_},
99 memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_},
100 blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())},
101 use_present_thread{Settings::values.async_presentation.GetValue()},
102 image_count{swapchain.GetImageCount()} {
103
104 auto& dld = device.GetLogical();
105 cmdpool = dld.CreateCommandPool({
106 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
107 .pNext = nullptr,
108 .flags =
109 VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
110 .queueFamilyIndex = device.GetGraphicsFamily(),
111 });
112 auto cmdbuffers = cmdpool.Allocate(image_count);
113
114 frames.resize(image_count);
115 for (u32 i = 0; i < frames.size(); i++) {
116 Frame& frame = frames[i];
117 frame.cmdbuf = vk::CommandBuffer{cmdbuffers[i], device.GetDispatchLoader()};
118 frame.render_ready = dld.CreateSemaphore({
119 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
120 .pNext = nullptr,
121 .flags = 0,
122 });
123 frame.present_done = dld.CreateFence({
124 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
125 .pNext = nullptr,
126 .flags = VK_FENCE_CREATE_SIGNALED_BIT,
127 });
128 free_queue.push(&frame);
129 }
130
131 if (use_present_thread) {
132 present_thread = std::jthread([this](std::stop_token token) { PresentThread(token); });
133 }
134}
135
136PresentManager::~PresentManager() = default;
137
138Frame* PresentManager::GetRenderFrame() {
139 MICROPROFILE_SCOPE(Vulkan_WaitPresent);
140
141 // Wait for free presentation frames
142 std::unique_lock lock{free_mutex};
143 free_cv.wait(lock, [this] { return !free_queue.empty(); });
144
145 // Take the frame from the queue
146 Frame* frame = free_queue.front();
147 free_queue.pop();
148
149 // Wait for the presentation to be finished so all frame resources are free
150 frame->present_done.Wait();
151 frame->present_done.Reset();
152
153 return frame;
154}
155
156void PresentManager::Present(Frame* frame) {
157 if (!use_present_thread) {
158 scheduler.WaitWorker();
159 CopyToSwapchain(frame);
160 free_queue.push(frame);
161 return;
162 }
163
164 scheduler.Record([this, frame](vk::CommandBuffer) {
165 std::unique_lock lock{queue_mutex};
166 present_queue.push(frame);
167 frame_cv.notify_one();
168 });
169}
170
171void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb,
172 VkFormat image_view_format, VkRenderPass rd) {
173 auto& dld = device.GetLogical();
174
175 frame->width = width;
176 frame->height = height;
177 frame->is_srgb = is_srgb;
178
179 frame->image = dld.CreateImage({
180 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
181 .pNext = nullptr,
182 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
183 .imageType = VK_IMAGE_TYPE_2D,
184 .format = swapchain.GetImageFormat(),
185 .extent =
186 {
187 .width = width,
188 .height = height,
189 .depth = 1,
190 },
191 .mipLevels = 1,
192 .arrayLayers = 1,
193 .samples = VK_SAMPLE_COUNT_1_BIT,
194 .tiling = VK_IMAGE_TILING_OPTIMAL,
195 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
196 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
197 .queueFamilyIndexCount = 0,
198 .pQueueFamilyIndices = nullptr,
199 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
200 });
201
202 frame->image_commit = memory_allocator.Commit(frame->image, MemoryUsage::DeviceLocal);
203
204 frame->image_view = dld.CreateImageView({
205 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
206 .pNext = nullptr,
207 .flags = 0,
208 .image = *frame->image,
209 .viewType = VK_IMAGE_VIEW_TYPE_2D,
210 .format = image_view_format,
211 .components =
212 {
213 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
214 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
215 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
216 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
217 },
218 .subresourceRange =
219 {
220 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
221 .baseMipLevel = 0,
222 .levelCount = 1,
223 .baseArrayLayer = 0,
224 .layerCount = 1,
225 },
226 });
227
228 const VkImageView image_view{*frame->image_view};
229 frame->framebuffer = dld.CreateFramebuffer({
230 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
231 .pNext = nullptr,
232 .flags = 0,
233 .renderPass = rd,
234 .attachmentCount = 1,
235 .pAttachments = &image_view,
236 .width = width,
237 .height = height,
238 .layers = 1,
239 });
240}
241
242void PresentManager::WaitPresent() {
243 if (!use_present_thread) {
244 return;
245 }
246
247 // Wait for the present queue to be empty
248 {
249 std::unique_lock queue_lock{queue_mutex};
250 frame_cv.wait(queue_lock, [this] { return present_queue.empty(); });
251 }
252
253 // The above condition will be satisfied when the last frame is taken from the queue.
254 // To ensure that frame has been presented as well take hold of the swapchain
255 // mutex.
256 std::scoped_lock swapchain_lock{swapchain_mutex};
257}
258
259void PresentManager::PresentThread(std::stop_token token) {
260 Common::SetCurrentThreadName("VulkanPresent");
261 while (!token.stop_requested()) {
262 std::unique_lock lock{queue_mutex};
263
264 // Wait for presentation frames
265 Common::CondvarWait(frame_cv, lock, token, [this] { return !present_queue.empty(); });
266 if (token.stop_requested()) {
267 return;
268 }
269
270 // Take the frame and notify anyone waiting
271 Frame* frame = present_queue.front();
272 present_queue.pop();
273 frame_cv.notify_one();
274
275 // By exchanging the lock ownership we take the swapchain lock
276 // before the queue lock goes out of scope. This way the swapchain
277 // lock in WaitPresent is guaranteed to occur after here.
278 std::exchange(lock, std::unique_lock{swapchain_mutex});
279
280 CopyToSwapchain(frame);
281
282 // Free the frame for reuse
283 std::scoped_lock fl{free_mutex};
284 free_queue.push(frame);
285 free_cv.notify_one();
286 }
287}
288
289void PresentManager::CopyToSwapchain(Frame* frame) {
290 MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain);
291
292 const auto recreate_swapchain = [&] {
293 swapchain.Create(frame->width, frame->height, frame->is_srgb);
294 image_count = swapchain.GetImageCount();
295 };
296
297 // If the size or colorspace of the incoming frames has changed, recreate the swapchain
298 // to account for that.
299 const bool srgb_changed = swapchain.NeedsRecreation(frame->is_srgb);
300 const bool size_changed =
301 swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height;
302 if (srgb_changed || size_changed) {
303 recreate_swapchain();
304 }
305
306 while (swapchain.AcquireNextImage()) {
307 recreate_swapchain();
308 }
309
310 const vk::CommandBuffer cmdbuf{frame->cmdbuf};
311 cmdbuf.Begin({
312 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
313 .pNext = nullptr,
314 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
315 .pInheritanceInfo = nullptr,
316 });
317
318 const VkImage image{swapchain.CurrentImage()};
319 const VkExtent2D extent = swapchain.GetExtent();
320 const std::array pre_barriers{
321 VkImageMemoryBarrier{
322 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
323 .pNext = nullptr,
324 .srcAccessMask = 0,
325 .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
326 .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
327 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
328 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
329 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
330 .image = image,
331 .subresourceRange{
332 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
333 .baseMipLevel = 0,
334 .levelCount = 1,
335 .baseArrayLayer = 0,
336 .layerCount = VK_REMAINING_ARRAY_LAYERS,
337 },
338 },
339 VkImageMemoryBarrier{
340 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
341 .pNext = nullptr,
342 .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
343 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
344 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
345 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
346 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
347 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
348 .image = *frame->image,
349 .subresourceRange{
350 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
351 .baseMipLevel = 0,
352 .levelCount = 1,
353 .baseArrayLayer = 0,
354 .layerCount = VK_REMAINING_ARRAY_LAYERS,
355 },
356 },
357 };
358 const std::array post_barriers{
359 VkImageMemoryBarrier{
360 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
361 .pNext = nullptr,
362 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
363 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
364 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
365 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
366 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
367 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
368 .image = image,
369 .subresourceRange{
370 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
371 .baseMipLevel = 0,
372 .levelCount = 1,
373 .baseArrayLayer = 0,
374 .layerCount = VK_REMAINING_ARRAY_LAYERS,
375 },
376 },
377 VkImageMemoryBarrier{
378 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
379 .pNext = nullptr,
380 .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
381 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
382 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
383 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
384 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
385 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
386 .image = *frame->image,
387 .subresourceRange{
388 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
389 .baseMipLevel = 0,
390 .levelCount = 1,
391 .baseArrayLayer = 0,
392 .layerCount = VK_REMAINING_ARRAY_LAYERS,
393 },
394 },
395 };
396
397 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, {},
398 {}, {}, pre_barriers);
399
400 if (blit_supported) {
401 cmdbuf.BlitImage(*frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image,
402 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
403 MakeImageBlit(frame->width, frame->height, extent.width, extent.height),
404 VK_FILTER_LINEAR);
405 } else {
406 cmdbuf.CopyImage(*frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image,
407 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
408 MakeImageCopy(frame->width, frame->height, extent.width, extent.height));
409 }
410
411 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, {},
412 {}, {}, post_barriers);
413
414 cmdbuf.End();
415
416 const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
417 const VkSemaphore render_semaphore = swapchain.CurrentRenderSemaphore();
418 const std::array wait_semaphores = {present_semaphore, *frame->render_ready};
419
420 static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
421 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
422 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
423 };
424
425 const VkSubmitInfo submit_info{
426 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
427 .pNext = nullptr,
428 .waitSemaphoreCount = 2U,
429 .pWaitSemaphores = wait_semaphores.data(),
430 .pWaitDstStageMask = wait_stage_masks.data(),
431 .commandBufferCount = 1,
432 .pCommandBuffers = cmdbuf.address(),
433 .signalSemaphoreCount = 1U,
434 .pSignalSemaphores = &render_semaphore,
435 };
436
437 // Submit the image copy/blit to the swapchain
438 {
439 std::scoped_lock lock{scheduler.submit_mutex};
440 switch (const VkResult result =
441 device.GetGraphicsQueue().Submit(submit_info, *frame->present_done)) {
442 case VK_SUCCESS:
443 break;
444 case VK_ERROR_DEVICE_LOST:
445 device.ReportLoss();
446 [[fallthrough]];
447 default:
448 vk::Check(result);
449 break;
450 }
451 }
452
453 // Present
454 swapchain.Present(render_semaphore);
455}
456
457} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h
new file mode 100644
index 000000000..420a775e2
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_present_manager.h
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <condition_variable>
7#include <mutex>
8#include <queue>
9
10#include "common/common_types.h"
11#include "common/polyfill_thread.h"
12#include "video_core/vulkan_common/vulkan_memory_allocator.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h"
14
15namespace Core::Frontend {
16class EmuWindow;
17} // namespace Core::Frontend
18
19namespace Vulkan {
20
21class Device;
22class Scheduler;
23class Swapchain;
24
25struct Frame {
26 u32 width;
27 u32 height;
28 bool is_srgb;
29 vk::Image image;
30 vk::ImageView image_view;
31 vk::Framebuffer framebuffer;
32 MemoryCommit image_commit;
33 vk::CommandBuffer cmdbuf;
34 vk::Semaphore render_ready;
35 vk::Fence present_done;
36};
37
38class PresentManager {
39public:
40 PresentManager(Core::Frontend::EmuWindow& render_window, const Device& device,
41 MemoryAllocator& memory_allocator, Scheduler& scheduler, Swapchain& swapchain);
42 ~PresentManager();
43
44 /// Returns the last used presentation frame
45 Frame* GetRenderFrame();
46
47 /// Pushes a frame for presentation
48 void Present(Frame* frame);
49
50 /// Recreates the present frame to match the provided parameters
51 void RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb,
52 VkFormat image_view_format, VkRenderPass rd);
53
54 /// Waits for the present thread to finish presenting all queued frames.
55 void WaitPresent();
56
57private:
58 void PresentThread(std::stop_token token);
59
60 void CopyToSwapchain(Frame* frame);
61
62private:
63 Core::Frontend::EmuWindow& render_window;
64 const Device& device;
65 MemoryAllocator& memory_allocator;
66 Scheduler& scheduler;
67 Swapchain& swapchain;
68 vk::CommandPool cmdpool;
69 std::vector<Frame> frames;
70 std::queue<Frame*> present_queue;
71 std::queue<Frame*> free_queue;
72 std::condition_variable_any frame_cv;
73 std::condition_variable free_cv;
74 std::mutex swapchain_mutex;
75 std::mutex queue_mutex;
76 std::mutex free_mutex;
77 std::jthread present_thread;
78 bool blit_supported;
79 bool use_present_thread;
80 std::size_t image_count;
81};
82
83} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 929c8ece6..d67490449 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -66,9 +66,10 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
66 } 66 }
67} 67}
68 68
69QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, 69QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_,
70 Core::Memory::Memory& cpu_memory_, const Device& device_,
70 Scheduler& scheduler_) 71 Scheduler& scheduler_)
71 : QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_}, 72 : QueryCacheBase{rasterizer_, cpu_memory_}, device{device_}, scheduler{scheduler_},
72 query_pools{ 73 query_pools{
73 QueryPool{device_, scheduler_, QueryType::SamplesPassed}, 74 QueryPool{device_, scheduler_, QueryType::SamplesPassed},
74 } {} 75 } {}
@@ -98,8 +99,10 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend
98 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { 99 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} {
99 const vk::Device* logical = &cache.GetDevice().GetLogical(); 100 const vk::Device* logical = &cache.GetDevice().GetLogical();
100 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { 101 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
102 const bool use_precise = Settings::IsGPULevelHigh();
101 logical->ResetQueryPool(query.first, query.second, 1); 103 logical->ResetQueryPool(query.first, query.second, 1);
102 cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); 104 cmdbuf.BeginQuery(query.first, query.second,
105 use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
103 }); 106 });
104} 107}
105 108
@@ -112,8 +115,10 @@ void HostCounter::EndQuery() {
112 [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); 115 [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); });
113} 116}
114 117
115u64 HostCounter::BlockingQuery() const { 118u64 HostCounter::BlockingQuery(bool async) const {
116 cache.GetScheduler().Wait(tick); 119 if (!async) {
120 cache.GetScheduler().Wait(tick);
121 }
117 u64 data; 122 u64 data;
118 const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults( 123 const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults(
119 query.first, query.second, 1, sizeof(data), &data, sizeof(data), 124 query.first, query.second, 1, sizeof(data), &data, sizeof(data),
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index 26762ee09..c1b9552eb 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -52,7 +52,8 @@ private:
52class QueryCache final 52class QueryCache final
53 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { 53 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
54public: 54public:
55 explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, 55 explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_,
56 Core::Memory::Memory& cpu_memory_, const Device& device_,
56 Scheduler& scheduler_); 57 Scheduler& scheduler_);
57 ~QueryCache(); 58 ~QueryCache();
58 59
@@ -83,7 +84,7 @@ public:
83 void EndQuery(); 84 void EndQuery();
84 85
85private: 86private:
86 u64 BlockingQuery() const override; 87 u64 BlockingQuery(bool async = false) const override;
87 88
88 QueryCache& cache; 89 QueryCache& cache;
89 const VideoCore::QueryType type; 90 const VideoCore::QueryType type;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 2559a3aa7..628e1376f 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -172,7 +172,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
172 buffer_cache(*this, cpu_memory_, buffer_cache_runtime), 172 buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
173 pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue, 173 pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue,
174 render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()), 174 render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()),
175 query_cache{*this, device, scheduler}, accelerate_dma(buffer_cache, texture_cache, scheduler), 175 query_cache{*this, cpu_memory_, device, scheduler},
176 accelerate_dma(buffer_cache, texture_cache, scheduler),
176 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), 177 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
177 wfi_event(device.GetLogical().CreateEvent()) { 178 wfi_event(device.GetLogical().CreateEvent()) {
178 scheduler.SetQueryCache(query_cache); 179 scheduler.SetQueryCache(query_cache);
@@ -501,6 +502,22 @@ bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheT
501 return false; 502 return false;
502} 503}
503 504
505VideoCore::RasterizerDownloadArea RasterizerVulkan::GetFlushArea(VAddr addr, u64 size) {
506 {
507 std::scoped_lock lock{texture_cache.mutex};
508 auto area = texture_cache.GetFlushArea(addr, size);
509 if (area) {
510 return *area;
511 }
512 }
513 VideoCore::RasterizerDownloadArea new_area{
514 .start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE),
515 .end_address = Common::AlignUp(addr + size, Core::Memory::YUZU_PAGESIZE),
516 .preemtive = true,
517 };
518 return new_area;
519}
520
504void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) { 521void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
505 if (addr == 0 || size == 0) { 522 if (addr == 0 || size == 0) {
506 return; 523 return;
@@ -597,7 +614,7 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) {
597} 614}
598 615
599void RasterizerVulkan::SignalReference() { 616void RasterizerVulkan::SignalReference() {
600 fence_manager.SignalOrdering(); 617 fence_manager.SignalReference();
601} 618}
602 619
603void RasterizerVulkan::ReleaseFences() { 620void RasterizerVulkan::ReleaseFences() {
@@ -630,7 +647,7 @@ void RasterizerVulkan::WaitForIdle() {
630 cmdbuf.SetEvent(event, flags); 647 cmdbuf.SetEvent(event, flags);
631 cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {}); 648 cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {});
632 }); 649 });
633 SignalReference(); 650 fence_manager.SignalOrdering();
634} 651}
635 652
636void RasterizerVulkan::FragmentBarrier() { 653void RasterizerVulkan::FragmentBarrier() {
@@ -675,7 +692,8 @@ bool RasterizerVulkan::AccelerateConditionalRendering() {
675 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()}; 692 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
676 Maxwell::ReportSemaphore::Compare cmp; 693 Maxwell::ReportSemaphore::Compare cmp;
677 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp), 694 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
678 VideoCommon::CacheType::BufferCache)) { 695 VideoCommon::CacheType::BufferCache |
696 VideoCommon::CacheType::QueryCache)) {
679 return true; 697 return true;
680 } 698 }
681 return false; 699 return false;
@@ -775,7 +793,7 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
775 const Tegra::DMA::BufferOperand& buffer_operand, 793 const Tegra::DMA::BufferOperand& buffer_operand,
776 const Tegra::DMA::ImageOperand& image_operand) { 794 const Tegra::DMA::ImageOperand& image_operand) {
777 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 795 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
778 const auto image_id = texture_cache.DmaImageId(image_operand); 796 const auto image_id = texture_cache.DmaImageId(image_operand, IS_IMAGE_UPLOAD);
779 if (image_id == VideoCommon::NULL_IMAGE_ID) { 797 if (image_id == VideoCommon::NULL_IMAGE_ID) {
780 return false; 798 return false;
781 } 799 }
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 1659fbc13..9bd422850 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -92,6 +92,7 @@ public:
92 VideoCommon::CacheType which = VideoCommon::CacheType::All) override; 92 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
93 bool MustFlushRegion(VAddr addr, u64 size, 93 bool MustFlushRegion(VAddr addr, u64 size,
94 VideoCommon::CacheType which = VideoCommon::CacheType::All) override; 94 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
95 VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override;
95 void InvalidateRegion(VAddr addr, u64 size, 96 void InvalidateRegion(VAddr addr, u64 size,
96 VideoCommon::CacheType which = VideoCommon::CacheType::All) override; 97 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
97 void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override; 98 void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 057e16967..80455ec08 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -46,10 +46,11 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
46 46
47Scheduler::~Scheduler() = default; 47Scheduler::~Scheduler() = default;
48 48
49void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 49u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
50 // When flushing, we only send data to the worker thread; no waiting is necessary. 50 // When flushing, we only send data to the worker thread; no waiting is necessary.
51 SubmitExecution(signal_semaphore, wait_semaphore); 51 const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore);
52 AllocateNewContext(); 52 AllocateNewContext();
53 return signal_value;
53} 54}
54 55
55void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 56void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
@@ -205,7 +206,7 @@ void Scheduler::AllocateWorkerCommandBuffer() {
205 }); 206 });
206} 207}
207 208
208void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 209u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
209 EndPendingOperations(); 210 EndPendingOperations();
210 InvalidateState(); 211 InvalidateState();
211 212
@@ -217,6 +218,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
217 on_submit(); 218 on_submit();
218 } 219 }
219 220
221 std::scoped_lock lock{submit_mutex};
220 switch (const VkResult result = master_semaphore->SubmitQueue( 222 switch (const VkResult result = master_semaphore->SubmitQueue(
221 cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { 223 cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
222 case VK_SUCCESS: 224 case VK_SUCCESS:
@@ -231,6 +233,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
231 }); 233 });
232 chunk->MarkSubmit(); 234 chunk->MarkSubmit();
233 DispatchWork(); 235 DispatchWork();
236 return signal_value;
234} 237}
235 238
236void Scheduler::AllocateNewContext() { 239void Scheduler::AllocateNewContext() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 8d75ce987..475c682eb 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -34,7 +34,7 @@ public:
34 ~Scheduler(); 34 ~Scheduler();
35 35
36 /// Sends the current execution context to the GPU. 36 /// Sends the current execution context to the GPU.
37 void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); 37 u64 Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
38 38
39 /// Sends the current execution context to the GPU and waits for it to complete. 39 /// Sends the current execution context to the GPU and waits for it to complete.
40 void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); 40 void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
@@ -106,6 +106,8 @@ public:
106 return *master_semaphore; 106 return *master_semaphore;
107 } 107 }
108 108
109 std::mutex submit_mutex;
110
109private: 111private:
110 class Command { 112 class Command {
111 public: 113 public:
@@ -201,7 +203,7 @@ private:
201 203
202 void AllocateWorkerCommandBuffer(); 204 void AllocateWorkerCommandBuffer();
203 205
204 void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore); 206 u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
205 207
206 void AllocateNewContext(); 208 void AllocateNewContext();
207 209
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index b1465e35c..1e80ce463 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -14,6 +14,7 @@
14#include "video_core/renderer_vulkan/vk_swapchain.h" 14#include "video_core/renderer_vulkan/vk_swapchain.h"
15#include "video_core/vulkan_common/vulkan_device.h" 15#include "video_core/vulkan_common/vulkan_device.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h" 16#include "video_core/vulkan_common/vulkan_wrapper.h"
17#include "vulkan/vulkan_core.h"
17 18
18namespace Vulkan { 19namespace Vulkan {
19 20
@@ -33,23 +34,47 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
33 return found != formats.end() ? *found : formats[0]; 34 return found != formats.end() ? *found : formats[0];
34} 35}
35 36
36VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) { 37static constexpr VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
37 // Mailbox (triple buffering) doesn't lock the application like fifo (vsync), 38 bool has_fifo_relaxed) {
38 // prefer it if vsync option is not selected 39 // Mailbox doesn't lock the application like FIFO (vsync)
39 const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); 40 // FIFO present mode locks the framerate to the monitor's refresh rate
40 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Borderless && 41 Settings::VSyncMode setting = [has_imm, has_mailbox]() {
41 found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) { 42 // Choose Mailbox or Immediate if unlocked and those modes are supported
42 return VK_PRESENT_MODE_MAILBOX_KHR; 43 const auto mode = Settings::values.vsync_mode.GetValue();
43 } 44 if (Settings::values.use_speed_limit.GetValue()) {
44 if (!Settings::values.use_speed_limit.GetValue()) { 45 return mode;
45 // FIFO present mode locks the framerate to the monitor's refresh rate, 46 }
46 // Find an alternative to surpass this limitation if FPS is unlocked. 47 switch (mode) {
47 const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR); 48 case Settings::VSyncMode::FIFO:
48 if (found_imm != modes.end()) { 49 case Settings::VSyncMode::FIFORelaxed:
49 return VK_PRESENT_MODE_IMMEDIATE_KHR; 50 if (has_mailbox) {
51 return Settings::VSyncMode::Mailbox;
52 } else if (has_imm) {
53 return Settings::VSyncMode::Immediate;
54 }
55 [[fallthrough]];
56 default:
57 return mode;
50 } 58 }
59 }();
60 if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
61 (setting == Settings::VSyncMode::Immediate && !has_imm) ||
62 (setting == Settings::VSyncMode::FIFORelaxed && !has_fifo_relaxed)) {
63 setting = Settings::VSyncMode::FIFO;
64 }
65
66 switch (setting) {
67 case Settings::VSyncMode::Immediate:
68 return VK_PRESENT_MODE_IMMEDIATE_KHR;
69 case Settings::VSyncMode::Mailbox:
70 return VK_PRESENT_MODE_MAILBOX_KHR;
71 case Settings::VSyncMode::FIFO:
72 return VK_PRESENT_MODE_FIFO_KHR;
73 case Settings::VSyncMode::FIFORelaxed:
74 return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
75 default:
76 return VK_PRESENT_MODE_FIFO_KHR;
51 } 77 }
52 return VK_PRESENT_MODE_FIFO_KHR;
53} 78}
54 79
55VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) { 80VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
@@ -99,18 +124,16 @@ void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
99 return; 124 return;
100 } 125 }
101 126
102 device.GetLogical().WaitIdle();
103 Destroy(); 127 Destroy();
104 128
105 CreateSwapchain(capabilities, srgb); 129 CreateSwapchain(capabilities, srgb);
106 CreateSemaphores(); 130 CreateSemaphores();
107 CreateImageViews();
108 131
109 resource_ticks.clear(); 132 resource_ticks.clear();
110 resource_ticks.resize(image_count); 133 resource_ticks.resize(image_count);
111} 134}
112 135
113void Swapchain::AcquireNextImage() { 136bool Swapchain::AcquireNextImage() {
114 const VkResult result = device.GetLogical().AcquireNextImageKHR( 137 const VkResult result = device.GetLogical().AcquireNextImageKHR(
115 *swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index], 138 *swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index],
116 VK_NULL_HANDLE, &image_index); 139 VK_NULL_HANDLE, &image_index);
@@ -127,8 +150,11 @@ void Swapchain::AcquireNextImage() {
127 LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result)); 150 LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result));
128 break; 151 break;
129 } 152 }
153
130 scheduler.Wait(resource_ticks[image_index]); 154 scheduler.Wait(resource_ticks[image_index]);
131 resource_ticks[image_index] = scheduler.CurrentTick(); 155 resource_ticks[image_index] = scheduler.CurrentTick();
156
157 return is_suboptimal || is_outdated;
132} 158}
133 159
134void Swapchain::Present(VkSemaphore render_semaphore) { 160void Swapchain::Present(VkSemaphore render_semaphore) {
@@ -143,6 +169,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
143 .pImageIndices = &image_index, 169 .pImageIndices = &image_index,
144 .pResults = nullptr, 170 .pResults = nullptr,
145 }; 171 };
172 std::scoped_lock lock{scheduler.submit_mutex};
146 switch (const VkResult result = present_queue.Present(present_info)) { 173 switch (const VkResult result = present_queue.Present(present_info)) {
147 case VK_SUCCESS: 174 case VK_SUCCESS:
148 break; 175 break;
@@ -165,11 +192,17 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
165void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) { 192void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) {
166 const auto physical_device{device.GetPhysical()}; 193 const auto physical_device{device.GetPhysical()};
167 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; 194 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
168 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; 195 const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface);
196 has_mailbox = std::find(present_modes.begin(), present_modes.end(),
197 VK_PRESENT_MODE_MAILBOX_KHR) != present_modes.end();
198 has_imm = std::find(present_modes.begin(), present_modes.end(),
199 VK_PRESENT_MODE_IMMEDIATE_KHR) != present_modes.end();
200 has_fifo_relaxed = std::find(present_modes.begin(), present_modes.end(),
201 VK_PRESENT_MODE_FIFO_RELAXED_KHR) != present_modes.end();
169 202
170 const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)}; 203 const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
171 const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)}; 204 surface_format = ChooseSwapSurfaceFormat(formats);
172 present_mode = ChooseSwapPresentMode(present_modes); 205 present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
173 206
174 u32 requested_image_count{capabilities.minImageCount + 1}; 207 u32 requested_image_count{capabilities.minImageCount + 1};
175 // Ensure Triple buffering if possible. 208 // Ensure Triple buffering if possible.
@@ -193,7 +226,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
193 .imageColorSpace = surface_format.colorSpace, 226 .imageColorSpace = surface_format.colorSpace,
194 .imageExtent = {}, 227 .imageExtent = {},
195 .imageArrayLayers = 1, 228 .imageArrayLayers = 1,
196 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 229 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
197 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, 230 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
198 .queueFamilyIndexCount = 0, 231 .queueFamilyIndexCount = 0,
199 .pQueueFamilyIndices = nullptr, 232 .pQueueFamilyIndices = nullptr,
@@ -230,7 +263,6 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
230 263
231 extent = swapchain_ci.imageExtent; 264 extent = swapchain_ci.imageExtent;
232 current_srgb = srgb; 265 current_srgb = srgb;
233 current_fps_unlocked = !Settings::values.use_speed_limit.GetValue();
234 266
235 images = swapchain.GetImages(); 267 images = swapchain.GetImages();
236 image_count = static_cast<u32>(images.size()); 268 image_count = static_cast<u32>(images.size());
@@ -241,56 +273,20 @@ void Swapchain::CreateSemaphores() {
241 present_semaphores.resize(image_count); 273 present_semaphores.resize(image_count);
242 std::ranges::generate(present_semaphores, 274 std::ranges::generate(present_semaphores,
243 [this] { return device.GetLogical().CreateSemaphore(); }); 275 [this] { return device.GetLogical().CreateSemaphore(); });
244} 276 render_semaphores.resize(image_count);
245 277 std::ranges::generate(render_semaphores,
246void Swapchain::CreateImageViews() { 278 [this] { return device.GetLogical().CreateSemaphore(); });
247 VkImageViewCreateInfo ci{
248 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
249 .pNext = nullptr,
250 .flags = 0,
251 .image = {},
252 .viewType = VK_IMAGE_VIEW_TYPE_2D,
253 .format = image_view_format,
254 .components =
255 {
256 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
257 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
258 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
259 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
260 },
261 .subresourceRange =
262 {
263 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
264 .baseMipLevel = 0,
265 .levelCount = 1,
266 .baseArrayLayer = 0,
267 .layerCount = 1,
268 },
269 };
270
271 image_views.resize(image_count);
272 for (std::size_t i = 0; i < image_count; i++) {
273 ci.image = images[i];
274 image_views[i] = device.GetLogical().CreateImageView(ci);
275 }
276} 279}
277 280
278void Swapchain::Destroy() { 281void Swapchain::Destroy() {
279 frame_index = 0; 282 frame_index = 0;
280 present_semaphores.clear(); 283 present_semaphores.clear();
281 framebuffers.clear();
282 image_views.clear();
283 swapchain.reset(); 284 swapchain.reset();
284} 285}
285 286
286bool Swapchain::HasFpsUnlockChanged() const {
287 return current_fps_unlocked != !Settings::values.use_speed_limit.GetValue();
288}
289
290bool Swapchain::NeedsPresentModeUpdate() const { 287bool Swapchain::NeedsPresentModeUpdate() const {
291 // Mailbox present mode is the ideal for all scenarios. If it is not available, 288 const auto requested_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
292 // A different present mode is needed to support unlocked FPS above the monitor's refresh rate. 289 return present_mode != requested_mode;
293 return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged();
294} 290}
295 291
296} // namespace Vulkan 292} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index caf1ff32b..bf1ea7254 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -27,7 +27,7 @@ public:
27 void Create(u32 width, u32 height, bool srgb); 27 void Create(u32 width, u32 height, bool srgb);
28 28
29 /// Acquires the next image in the swapchain, waits as needed. 29 /// Acquires the next image in the swapchain, waits as needed.
30 void AcquireNextImage(); 30 bool AcquireNextImage();
31 31
32 /// Presents the rendered image to the swapchain. 32 /// Presents the rendered image to the swapchain.
33 void Present(VkSemaphore render_semaphore); 33 void Present(VkSemaphore render_semaphore);
@@ -52,6 +52,11 @@ public:
52 return is_suboptimal; 52 return is_suboptimal;
53 } 53 }
54 54
55 /// Returns true when the swapchain format is in the srgb color space
56 bool IsSrgb() const {
57 return current_srgb;
58 }
59
55 VkExtent2D GetSize() const { 60 VkExtent2D GetSize() const {
56 return extent; 61 return extent;
57 } 62 }
@@ -64,22 +69,34 @@ public:
64 return image_index; 69 return image_index;
65 } 70 }
66 71
72 std::size_t GetFrameIndex() const {
73 return frame_index;
74 }
75
67 VkImage GetImageIndex(std::size_t index) const { 76 VkImage GetImageIndex(std::size_t index) const {
68 return images[index]; 77 return images[index];
69 } 78 }
70 79
71 VkImageView GetImageViewIndex(std::size_t index) const { 80 VkImage CurrentImage() const {
72 return *image_views[index]; 81 return images[image_index];
73 } 82 }
74 83
75 VkFormat GetImageViewFormat() const { 84 VkFormat GetImageViewFormat() const {
76 return image_view_format; 85 return image_view_format;
77 } 86 }
78 87
88 VkFormat GetImageFormat() const {
89 return surface_format.format;
90 }
91
79 VkSemaphore CurrentPresentSemaphore() const { 92 VkSemaphore CurrentPresentSemaphore() const {
80 return *present_semaphores[frame_index]; 93 return *present_semaphores[frame_index];
81 } 94 }
82 95
96 VkSemaphore CurrentRenderSemaphore() const {
97 return *render_semaphores[frame_index];
98 }
99
83 u32 GetWidth() const { 100 u32 GetWidth() const {
84 return width; 101 return width;
85 } 102 }
@@ -88,6 +105,10 @@ public:
88 return height; 105 return height;
89 } 106 }
90 107
108 VkExtent2D GetExtent() const {
109 return extent;
110 }
111
91private: 112private:
92 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); 113 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb);
93 void CreateSemaphores(); 114 void CreateSemaphores();
@@ -95,8 +116,6 @@ private:
95 116
96 void Destroy(); 117 void Destroy();
97 118
98 bool HasFpsUnlockChanged() const;
99
100 bool NeedsPresentModeUpdate() const; 119 bool NeedsPresentModeUpdate() const;
101 120
102 const VkSurfaceKHR surface; 121 const VkSurfaceKHR surface;
@@ -107,10 +126,9 @@ private:
107 126
108 std::size_t image_count{}; 127 std::size_t image_count{};
109 std::vector<VkImage> images; 128 std::vector<VkImage> images;
110 std::vector<vk::ImageView> image_views;
111 std::vector<vk::Framebuffer> framebuffers;
112 std::vector<u64> resource_ticks; 129 std::vector<u64> resource_ticks;
113 std::vector<vk::Semaphore> present_semaphores; 130 std::vector<vk::Semaphore> present_semaphores;
131 std::vector<vk::Semaphore> render_semaphores;
114 132
115 u32 width; 133 u32 width;
116 u32 height; 134 u32 height;
@@ -121,9 +139,12 @@ private:
121 VkFormat image_view_format{}; 139 VkFormat image_view_format{};
122 VkExtent2D extent{}; 140 VkExtent2D extent{};
123 VkPresentModeKHR present_mode{}; 141 VkPresentModeKHR present_mode{};
142 VkSurfaceFormatKHR surface_format{};
143 bool has_imm{false};
144 bool has_mailbox{false};
145 bool has_fifo_relaxed{false};
124 146
125 bool current_srgb{}; 147 bool current_srgb{};
126 bool current_fps_unlocked{};
127 bool is_outdated{}; 148 bool is_outdated{};
128 bool is_suboptimal{}; 149 bool is_suboptimal{};
129}; 150};
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index d0a7d8f35..9ca7751c5 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1268,7 +1268,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
1268 if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { 1268 if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
1269 if (Settings::values.async_astc.GetValue()) { 1269 if (Settings::values.async_astc.GetValue()) {
1270 flags |= VideoCommon::ImageFlagBits::AsynchronousDecode; 1270 flags |= VideoCommon::ImageFlagBits::AsynchronousDecode;
1271 } else if (Settings::values.accelerate_astc.GetValue()) { 1271 } else if (Settings::values.accelerate_astc.GetValue() && info.size.depth == 1) {
1272 flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; 1272 flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
1273 } 1273 }
1274 flags |= VideoCommon::ImageFlagBits::Converted; 1274 flags |= VideoCommon::ImageFlagBits::Converted;
@@ -1584,8 +1584,9 @@ bool Image::NeedsScaleHelper() const {
1584 1584
1585ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info, 1585ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
1586 ImageId image_id_, Image& image) 1586 ImageId image_id_, Image& image)
1587 : VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device}, 1587 : VideoCommon::ImageViewBase{info, image.info, image_id_, image.gpu_addr},
1588 image_handle{image.Handle()}, samples(ConvertSampleCount(image.info.num_samples)) { 1588 device{&runtime.device}, image_handle{image.Handle()},
1589 samples(ConvertSampleCount(image.info.num_samples)) {
1589 using Shader::TextureType; 1590 using Shader::TextureType;
1590 1591
1591 const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info); 1592 const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info);
@@ -1631,7 +1632,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1631 } 1632 }
1632 vk::ImageView handle = device->GetLogical().CreateImageView(ci); 1633 vk::ImageView handle = device->GetLogical().CreateImageView(ci);
1633 if (device->HasDebuggingToolAttached()) { 1634 if (device->HasDebuggingToolAttached()) {
1634 handle.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); 1635 handle.SetObjectNameEXT(VideoCommon::Name(*this, gpu_addr).c_str());
1635 } 1636 }
1636 image_views[static_cast<size_t>(tex_type)] = std::move(handle); 1637 image_views[static_cast<size_t>(tex_type)] = std::move(handle);
1637 }; 1638 };
@@ -1672,7 +1673,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1672 1673
1673ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, 1674ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
1674 const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_) 1675 const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
1675 : VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_}, 1676 : VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
1676 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} 1677 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
1677 1678
1678ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params) 1679ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params)
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index c656c5386..6f360177a 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -265,7 +265,6 @@ private:
265 VkImage image_handle = VK_NULL_HANDLE; 265 VkImage image_handle = VK_NULL_HANDLE;
266 VkImageView render_target = VK_NULL_HANDLE; 266 VkImageView render_target = VK_NULL_HANDLE;
267 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; 267 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
268 GPUVAddr gpu_addr = 0;
269 u32 buffer_size = 0; 268 u32 buffer_size = 0;
270}; 269};
271 270
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
index 009dab0b6..0630ebda5 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
@@ -14,13 +14,18 @@ namespace Vulkan {
14 14
15UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_) 15UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_)
16 : device{device_}, scheduler{scheduler_} { 16 : device{device_}, scheduler{scheduler_} {
17 payload_start = payload.data();
17 payload_cursor = payload.data(); 18 payload_cursor = payload.data();
18} 19}
19 20
20UpdateDescriptorQueue::~UpdateDescriptorQueue() = default; 21UpdateDescriptorQueue::~UpdateDescriptorQueue() = default;
21 22
22void UpdateDescriptorQueue::TickFrame() { 23void UpdateDescriptorQueue::TickFrame() {
23 payload_cursor = payload.data(); 24 if (++frame_index >= FRAMES_IN_FLIGHT) {
25 frame_index = 0;
26 }
27 payload_start = payload.data() + frame_index * FRAME_PAYLOAD_SIZE;
28 payload_cursor = payload_start;
24} 29}
25 30
26void UpdateDescriptorQueue::Acquire() { 31void UpdateDescriptorQueue::Acquire() {
@@ -28,10 +33,10 @@ void UpdateDescriptorQueue::Acquire() {
28 // This is the maximum number of entries a single draw call might use. 33 // This is the maximum number of entries a single draw call might use.
29 static constexpr size_t MIN_ENTRIES = 0x400; 34 static constexpr size_t MIN_ENTRIES = 0x400;
30 35
31 if (std::distance(payload.data(), payload_cursor) + MIN_ENTRIES >= payload.max_size()) { 36 if (std::distance(payload_start, payload_cursor) + MIN_ENTRIES >= FRAME_PAYLOAD_SIZE) {
32 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); 37 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
33 scheduler.WaitWorker(); 38 scheduler.WaitWorker();
34 payload_cursor = payload.data(); 39 payload_cursor = payload_start;
35 } 40 }
36 upload_start = payload_cursor; 41 upload_start = payload_cursor;
37} 42}
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index 625bcc809..1c1a7020b 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -29,6 +29,12 @@ struct DescriptorUpdateEntry {
29}; 29};
30 30
31class UpdateDescriptorQueue final { 31class UpdateDescriptorQueue final {
32 // This should be plenty for the vast majority of cases. Most desktop platforms only
33 // provide up to 3 swapchain images.
34 static constexpr size_t FRAMES_IN_FLIGHT = 5;
35 static constexpr size_t FRAME_PAYLOAD_SIZE = 0x10000;
36 static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT;
37
32public: 38public:
33 explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_); 39 explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_);
34 ~UpdateDescriptorQueue(); 40 ~UpdateDescriptorQueue();
@@ -73,9 +79,11 @@ private:
73 const Device& device; 79 const Device& device;
74 Scheduler& scheduler; 80 Scheduler& scheduler;
75 81
82 size_t frame_index{0};
76 DescriptorUpdateEntry* payload_cursor = nullptr; 83 DescriptorUpdateEntry* payload_cursor = nullptr;
84 DescriptorUpdateEntry* payload_start = nullptr;
77 const DescriptorUpdateEntry* upload_start = nullptr; 85 const DescriptorUpdateEntry* upload_start = nullptr;
78 std::array<DescriptorUpdateEntry, 0x10000> payload; 86 std::array<DescriptorUpdateEntry, PAYLOAD_SIZE> payload;
79}; 87};
80 88
81} // namespace Vulkan 89} // namespace Vulkan
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index d9482371b..c5213875b 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -228,14 +228,14 @@ const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu
228 auto info = std::make_unique<ShaderInfo>(); 228 auto info = std::make_unique<ShaderInfo>();
229 if (const std::optional<u64> cached_hash{env.Analyze()}) { 229 if (const std::optional<u64> cached_hash{env.Analyze()}) {
230 info->unique_hash = *cached_hash; 230 info->unique_hash = *cached_hash;
231 info->size_bytes = env.CachedSize(); 231 info->size_bytes = env.CachedSizeBytes();
232 } else { 232 } else {
233 // Slow path, not really hit on commercial games 233 // Slow path, not really hit on commercial games
234 // Build a control flow graph to get the real shader size 234 // Build a control flow graph to get the real shader size
235 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block; 235 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
236 Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()}; 236 Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()};
237 info->unique_hash = env.CalculateHash(); 237 info->unique_hash = env.CalculateHash();
238 info->size_bytes = env.ReadSize(); 238 info->size_bytes = env.ReadSizeBytes();
239 } 239 }
240 const size_t size_bytes{info->size_bytes}; 240 const size_t size_bytes{info->size_bytes};
241 const ShaderInfo* const result{info.get()}; 241 const ShaderInfo* const result{info.get()};
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 574760f80..c7cb56243 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -170,15 +170,19 @@ std::optional<u64> GenericEnvironment::Analyze() {
170void GenericEnvironment::SetCachedSize(size_t size_bytes) { 170void GenericEnvironment::SetCachedSize(size_t size_bytes) {
171 cached_lowest = start_address; 171 cached_lowest = start_address;
172 cached_highest = start_address + static_cast<u32>(size_bytes); 172 cached_highest = start_address + static_cast<u32>(size_bytes);
173 code.resize(CachedSize()); 173 code.resize(CachedSizeWords());
174 gpu_memory->ReadBlock(program_base + cached_lowest, code.data(), code.size() * sizeof(u64)); 174 gpu_memory->ReadBlock(program_base + cached_lowest, code.data(), code.size() * sizeof(u64));
175} 175}
176 176
177size_t GenericEnvironment::CachedSize() const noexcept { 177size_t GenericEnvironment::CachedSizeWords() const noexcept {
178 return cached_highest - cached_lowest + INST_SIZE; 178 return CachedSizeBytes() / INST_SIZE;
179} 179}
180 180
181size_t GenericEnvironment::ReadSize() const noexcept { 181size_t GenericEnvironment::CachedSizeBytes() const noexcept {
182 return static_cast<size_t>(cached_highest) - cached_lowest + INST_SIZE;
183}
184
185size_t GenericEnvironment::ReadSizeBytes() const noexcept {
182 return read_highest - read_lowest + INST_SIZE; 186 return read_highest - read_lowest + INST_SIZE;
183} 187}
184 188
@@ -187,7 +191,7 @@ bool GenericEnvironment::CanBeSerialized() const noexcept {
187} 191}
188 192
189u64 GenericEnvironment::CalculateHash() const { 193u64 GenericEnvironment::CalculateHash() const {
190 const size_t size{ReadSize()}; 194 const size_t size{ReadSizeBytes()};
191 const auto data{std::make_unique<char[]>(size)}; 195 const auto data{std::make_unique<char[]>(size)};
192 gpu_memory->ReadBlock(program_base + read_lowest, data.get(), size); 196 gpu_memory->ReadBlock(program_base + read_lowest, data.get(), size);
193 return Common::CityHash64(data.get(), size); 197 return Common::CityHash64(data.get(), size);
@@ -198,7 +202,7 @@ void GenericEnvironment::Dump(u64 hash) {
198} 202}
199 203
200void GenericEnvironment::Serialize(std::ofstream& file) const { 204void GenericEnvironment::Serialize(std::ofstream& file) const {
201 const u64 code_size{static_cast<u64>(CachedSize())}; 205 const u64 code_size{static_cast<u64>(CachedSizeBytes())};
202 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 206 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
203 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())}; 207 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
204 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; 208 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index d75987a52..a0f61cbda 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -48,9 +48,11 @@ public:
48 48
49 void SetCachedSize(size_t size_bytes); 49 void SetCachedSize(size_t size_bytes);
50 50
51 [[nodiscard]] size_t CachedSize() const noexcept; 51 [[nodiscard]] size_t CachedSizeWords() const noexcept;
52 52
53 [[nodiscard]] size_t ReadSize() const noexcept; 53 [[nodiscard]] size_t CachedSizeBytes() const noexcept;
54
55 [[nodiscard]] size_t ReadSizeBytes() const noexcept;
54 56
55 [[nodiscard]] bool CanBeSerialized() const noexcept; 57 [[nodiscard]] bool CanBeSerialized() const noexcept;
56 58
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 1a76d4178..cb51529e4 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -250,10 +250,13 @@ bool IsPixelFormatASTC(PixelFormat format) {
250 case PixelFormat::ASTC_2D_6X6_UNORM: 250 case PixelFormat::ASTC_2D_6X6_UNORM:
251 case PixelFormat::ASTC_2D_6X6_SRGB: 251 case PixelFormat::ASTC_2D_6X6_SRGB:
252 case PixelFormat::ASTC_2D_10X6_UNORM: 252 case PixelFormat::ASTC_2D_10X6_UNORM:
253 case PixelFormat::ASTC_2D_10X6_SRGB:
253 case PixelFormat::ASTC_2D_10X5_UNORM: 254 case PixelFormat::ASTC_2D_10X5_UNORM:
254 case PixelFormat::ASTC_2D_10X5_SRGB: 255 case PixelFormat::ASTC_2D_10X5_SRGB:
255 case PixelFormat::ASTC_2D_10X10_UNORM: 256 case PixelFormat::ASTC_2D_10X10_UNORM:
256 case PixelFormat::ASTC_2D_10X10_SRGB: 257 case PixelFormat::ASTC_2D_10X10_SRGB:
258 case PixelFormat::ASTC_2D_12X10_UNORM:
259 case PixelFormat::ASTC_2D_12X10_SRGB:
257 case PixelFormat::ASTC_2D_12X12_UNORM: 260 case PixelFormat::ASTC_2D_12X12_UNORM:
258 case PixelFormat::ASTC_2D_12X12_SRGB: 261 case PixelFormat::ASTC_2D_12X12_SRGB:
259 case PixelFormat::ASTC_2D_8X6_UNORM: 262 case PixelFormat::ASTC_2D_8X6_UNORM:
@@ -279,11 +282,13 @@ bool IsPixelFormatSRGB(PixelFormat format) {
279 case PixelFormat::ASTC_2D_8X5_SRGB: 282 case PixelFormat::ASTC_2D_8X5_SRGB:
280 case PixelFormat::ASTC_2D_5X4_SRGB: 283 case PixelFormat::ASTC_2D_5X4_SRGB:
281 case PixelFormat::ASTC_2D_5X5_SRGB: 284 case PixelFormat::ASTC_2D_5X5_SRGB:
285 case PixelFormat::ASTC_2D_10X6_SRGB:
282 case PixelFormat::ASTC_2D_10X8_SRGB: 286 case PixelFormat::ASTC_2D_10X8_SRGB:
283 case PixelFormat::ASTC_2D_6X6_SRGB: 287 case PixelFormat::ASTC_2D_6X6_SRGB:
284 case PixelFormat::ASTC_2D_10X5_SRGB: 288 case PixelFormat::ASTC_2D_10X5_SRGB:
285 case PixelFormat::ASTC_2D_10X10_SRGB: 289 case PixelFormat::ASTC_2D_10X10_SRGB:
286 case PixelFormat::ASTC_2D_12X12_SRGB: 290 case PixelFormat::ASTC_2D_12X12_SRGB:
291 case PixelFormat::ASTC_2D_12X10_SRGB:
287 case PixelFormat::ASTC_2D_8X6_SRGB: 292 case PixelFormat::ASTC_2D_8X6_SRGB:
288 case PixelFormat::ASTC_2D_6X5_SRGB: 293 case PixelFormat::ASTC_2D_6X5_SRGB:
289 return true; 294 return true;
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 44b79af20..0225d3287 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -95,10 +95,13 @@ enum class PixelFormat {
95 ASTC_2D_6X6_UNORM, 95 ASTC_2D_6X6_UNORM,
96 ASTC_2D_6X6_SRGB, 96 ASTC_2D_6X6_SRGB,
97 ASTC_2D_10X6_UNORM, 97 ASTC_2D_10X6_UNORM,
98 ASTC_2D_10X6_SRGB,
98 ASTC_2D_10X5_UNORM, 99 ASTC_2D_10X5_UNORM,
99 ASTC_2D_10X5_SRGB, 100 ASTC_2D_10X5_SRGB,
100 ASTC_2D_10X10_UNORM, 101 ASTC_2D_10X10_UNORM,
101 ASTC_2D_10X10_SRGB, 102 ASTC_2D_10X10_SRGB,
103 ASTC_2D_12X10_UNORM,
104 ASTC_2D_12X10_SRGB,
102 ASTC_2D_12X12_UNORM, 105 ASTC_2D_12X12_UNORM,
103 ASTC_2D_12X12_SRGB, 106 ASTC_2D_12X12_SRGB,
104 ASTC_2D_8X6_UNORM, 107 ASTC_2D_8X6_UNORM,
@@ -232,10 +235,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
232 6, // ASTC_2D_6X6_UNORM 235 6, // ASTC_2D_6X6_UNORM
233 6, // ASTC_2D_6X6_SRGB 236 6, // ASTC_2D_6X6_SRGB
234 10, // ASTC_2D_10X6_UNORM 237 10, // ASTC_2D_10X6_UNORM
238 10, // ASTC_2D_10X6_SRGB
235 10, // ASTC_2D_10X5_UNORM 239 10, // ASTC_2D_10X5_UNORM
236 10, // ASTC_2D_10X5_SRGB 240 10, // ASTC_2D_10X5_SRGB
237 10, // ASTC_2D_10X10_UNORM 241 10, // ASTC_2D_10X10_UNORM
238 10, // ASTC_2D_10X10_SRGB 242 10, // ASTC_2D_10X10_SRGB
243 12, // ASTC_2D_12X10_UNORM
244 12, // ASTC_2D_12X10_SRGB
239 12, // ASTC_2D_12X12_UNORM 245 12, // ASTC_2D_12X12_UNORM
240 12, // ASTC_2D_12X12_SRGB 246 12, // ASTC_2D_12X12_SRGB
241 8, // ASTC_2D_8X6_UNORM 247 8, // ASTC_2D_8X6_UNORM
@@ -338,10 +344,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
338 6, // ASTC_2D_6X6_UNORM 344 6, // ASTC_2D_6X6_UNORM
339 6, // ASTC_2D_6X6_SRGB 345 6, // ASTC_2D_6X6_SRGB
340 6, // ASTC_2D_10X6_UNORM 346 6, // ASTC_2D_10X6_UNORM
347 6, // ASTC_2D_10X6_SRGB
341 5, // ASTC_2D_10X5_UNORM 348 5, // ASTC_2D_10X5_UNORM
342 5, // ASTC_2D_10X5_SRGB 349 5, // ASTC_2D_10X5_SRGB
343 10, // ASTC_2D_10X10_UNORM 350 10, // ASTC_2D_10X10_UNORM
344 10, // ASTC_2D_10X10_SRGB 351 10, // ASTC_2D_10X10_SRGB
352 10, // ASTC_2D_12X10_UNORM
353 10, // ASTC_2D_12X10_SRGB
345 12, // ASTC_2D_12X12_UNORM 354 12, // ASTC_2D_12X12_UNORM
346 12, // ASTC_2D_12X12_SRGB 355 12, // ASTC_2D_12X12_SRGB
347 6, // ASTC_2D_8X6_UNORM 356 6, // ASTC_2D_8X6_UNORM
@@ -444,10 +453,13 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
444 128, // ASTC_2D_6X6_UNORM 453 128, // ASTC_2D_6X6_UNORM
445 128, // ASTC_2D_6X6_SRGB 454 128, // ASTC_2D_6X6_SRGB
446 128, // ASTC_2D_10X6_UNORM 455 128, // ASTC_2D_10X6_UNORM
456 128, // ASTC_2D_10X6_SRGB
447 128, // ASTC_2D_10X5_UNORM 457 128, // ASTC_2D_10X5_UNORM
448 128, // ASTC_2D_10X5_SRGB 458 128, // ASTC_2D_10X5_SRGB
449 128, // ASTC_2D_10X10_UNORM 459 128, // ASTC_2D_10X10_UNORM
450 128, // ASTC_2D_10X10_SRGB 460 128, // ASTC_2D_10X10_SRGB
461 128, // ASTC_2D_12X10_UNORM
462 128, // ASTC_2D_12X10_SRGB
451 128, // ASTC_2D_12X12_UNORM 463 128, // ASTC_2D_12X12_UNORM
452 128, // ASTC_2D_12X12_SRGB 464 128, // ASTC_2D_12X12_SRGB
453 128, // ASTC_2D_8X6_UNORM 465 128, // ASTC_2D_8X6_UNORM
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index 5fc2b2fec..11ced6c38 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -210,6 +210,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
210 return PixelFormat::ASTC_2D_6X6_SRGB; 210 return PixelFormat::ASTC_2D_6X6_SRGB;
211 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): 211 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR):
212 return PixelFormat::ASTC_2D_10X6_UNORM; 212 return PixelFormat::ASTC_2D_10X6_UNORM;
213 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, SRGB):
214 return PixelFormat::ASTC_2D_10X6_SRGB;
213 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR): 215 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR):
214 return PixelFormat::ASTC_2D_10X5_UNORM; 216 return PixelFormat::ASTC_2D_10X5_UNORM;
215 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB): 217 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB):
@@ -218,6 +220,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
218 return PixelFormat::ASTC_2D_10X10_UNORM; 220 return PixelFormat::ASTC_2D_10X10_UNORM;
219 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): 221 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB):
220 return PixelFormat::ASTC_2D_10X10_SRGB; 222 return PixelFormat::ASTC_2D_10X10_SRGB;
223 case Hash(TextureFormat::ASTC_2D_12X10, UNORM, LINEAR):
224 return PixelFormat::ASTC_2D_12X10_UNORM;
225 case Hash(TextureFormat::ASTC_2D_12X10, UNORM, SRGB):
226 return PixelFormat::ASTC_2D_12X10_SRGB;
221 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, LINEAR): 227 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, LINEAR):
222 return PixelFormat::ASTC_2D_12X12_UNORM; 228 return PixelFormat::ASTC_2D_12X12_UNORM;
223 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, SRGB): 229 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, SRGB):
diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp
index 30f72361d..6279d8e9e 100644
--- a/src/video_core/texture_cache/formatter.cpp
+++ b/src/video_core/texture_cache/formatter.cpp
@@ -46,7 +46,7 @@ std::string Name(const ImageBase& image) {
46 return "Invalid"; 46 return "Invalid";
47} 47}
48 48
49std::string Name(const ImageViewBase& image_view) { 49std::string Name(const ImageViewBase& image_view, GPUVAddr addr) {
50 const u32 width = image_view.size.width; 50 const u32 width = image_view.size.width;
51 const u32 height = image_view.size.height; 51 const u32 height = image_view.size.height;
52 const u32 depth = image_view.size.depth; 52 const u32 depth = image_view.size.depth;
@@ -56,23 +56,25 @@ std::string Name(const ImageViewBase& image_view) {
56 const std::string level = num_levels > 1 ? fmt::format(":{}", num_levels) : ""; 56 const std::string level = num_levels > 1 ? fmt::format(":{}", num_levels) : "";
57 switch (image_view.type) { 57 switch (image_view.type) {
58 case ImageViewType::e1D: 58 case ImageViewType::e1D:
59 return fmt::format("ImageView 1D {}{}", width, level); 59 return fmt::format("ImageView 1D 0x{:X} {}{}", addr, width, level);
60 case ImageViewType::e2D: 60 case ImageViewType::e2D:
61 return fmt::format("ImageView 2D {}x{}{}", width, height, level); 61 return fmt::format("ImageView 2D 0x{:X} {}x{}{}", addr, width, height, level);
62 case ImageViewType::Cube: 62 case ImageViewType::Cube:
63 return fmt::format("ImageView Cube {}x{}{}", width, height, level); 63 return fmt::format("ImageView Cube 0x{:X} {}x{}{}", addr, width, height, level);
64 case ImageViewType::e3D: 64 case ImageViewType::e3D:
65 return fmt::format("ImageView 3D {}x{}x{}{}", width, height, depth, level); 65 return fmt::format("ImageView 3D 0x{:X} {}x{}x{}{}", addr, width, height, depth, level);
66 case ImageViewType::e1DArray: 66 case ImageViewType::e1DArray:
67 return fmt::format("ImageView 1DArray {}{}|{}", width, level, num_layers); 67 return fmt::format("ImageView 1DArray 0x{:X} {}{}|{}", addr, width, level, num_layers);
68 case ImageViewType::e2DArray: 68 case ImageViewType::e2DArray:
69 return fmt::format("ImageView 2DArray {}x{}{}|{}", width, height, level, num_layers); 69 return fmt::format("ImageView 2DArray 0x{:X} {}x{}{}|{}", addr, width, height, level,
70 num_layers);
70 case ImageViewType::CubeArray: 71 case ImageViewType::CubeArray:
71 return fmt::format("ImageView CubeArray {}x{}{}|{}", width, height, level, num_layers); 72 return fmt::format("ImageView CubeArray 0x{:X} {}x{}{}|{}", addr, width, height, level,
73 num_layers);
72 case ImageViewType::Rect: 74 case ImageViewType::Rect:
73 return fmt::format("ImageView Rect {}x{}{}", width, height, level); 75 return fmt::format("ImageView Rect 0x{:X} {}x{}{}", addr, width, height, level);
74 case ImageViewType::Buffer: 76 case ImageViewType::Buffer:
75 return fmt::format("BufferView {}", width); 77 return fmt::format("BufferView 0x{:X} {}", addr, width);
76 } 78 }
77 return "Invalid"; 79 return "Invalid";
78} 80}
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index f1f0a057b..9ee57a076 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -179,6 +179,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
179 return "ASTC_2D_6X6_SRGB"; 179 return "ASTC_2D_6X6_SRGB";
180 case PixelFormat::ASTC_2D_10X6_UNORM: 180 case PixelFormat::ASTC_2D_10X6_UNORM:
181 return "ASTC_2D_10X6_UNORM"; 181 return "ASTC_2D_10X6_UNORM";
182 case PixelFormat::ASTC_2D_10X6_SRGB:
183 return "ASTC_2D_10X6_SRGB";
182 case PixelFormat::ASTC_2D_10X5_UNORM: 184 case PixelFormat::ASTC_2D_10X5_UNORM:
183 return "ASTC_2D_10X5_UNORM"; 185 return "ASTC_2D_10X5_UNORM";
184 case PixelFormat::ASTC_2D_10X5_SRGB: 186 case PixelFormat::ASTC_2D_10X5_SRGB:
@@ -187,6 +189,10 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
187 return "ASTC_2D_10X10_UNORM"; 189 return "ASTC_2D_10X10_UNORM";
188 case PixelFormat::ASTC_2D_10X10_SRGB: 190 case PixelFormat::ASTC_2D_10X10_SRGB:
189 return "ASTC_2D_10X10_SRGB"; 191 return "ASTC_2D_10X10_SRGB";
192 case PixelFormat::ASTC_2D_12X10_UNORM:
193 return "ASTC_2D_12X10_UNORM";
194 case PixelFormat::ASTC_2D_12X10_SRGB:
195 return "ASTC_2D_12X10_SRGB";
190 case PixelFormat::ASTC_2D_12X12_UNORM: 196 case PixelFormat::ASTC_2D_12X12_UNORM:
191 return "ASTC_2D_12X12_UNORM"; 197 return "ASTC_2D_12X12_UNORM";
192 case PixelFormat::ASTC_2D_12X12_SRGB: 198 case PixelFormat::ASTC_2D_12X12_SRGB:
@@ -268,7 +274,7 @@ struct RenderTargets;
268 274
269[[nodiscard]] std::string Name(const ImageBase& image); 275[[nodiscard]] std::string Name(const ImageBase& image);
270 276
271[[nodiscard]] std::string Name(const ImageViewBase& image_view); 277[[nodiscard]] std::string Name(const ImageViewBase& image_view, GPUVAddr addr);
272 278
273[[nodiscard]] std::string Name(const RenderTargets& render_targets); 279[[nodiscard]] std::string Name(const RenderTargets& render_targets);
274 280
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 11f3f78a1..e8ddde691 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -4,6 +4,7 @@
4#include <fmt/format.h> 4#include <fmt/format.h>
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/settings.h"
7#include "video_core/surface.h" 8#include "video_core/surface.h"
8#include "video_core/texture_cache/format_lookup_table.h" 9#include "video_core/texture_cache/format_lookup_table.h"
9#include "video_core/texture_cache/image_info.h" 10#include "video_core/texture_cache/image_info.h"
@@ -22,6 +23,8 @@ using VideoCore::Surface::PixelFormat;
22using VideoCore::Surface::SurfaceType; 23using VideoCore::Surface::SurfaceType;
23 24
24ImageInfo::ImageInfo(const TICEntry& config) noexcept { 25ImageInfo::ImageInfo(const TICEntry& config) noexcept {
26 forced_flushed = config.IsPitchLinear() && !Settings::values.use_reactive_flushing.GetValue();
27 dma_downloaded = forced_flushed;
25 format = PixelFormatFromTextureInfo(config.format, config.r_type, config.g_type, config.b_type, 28 format = PixelFormatFromTextureInfo(config.format, config.r_type, config.g_type, config.b_type,
26 config.a_type, config.srgb_conversion); 29 config.a_type, config.srgb_conversion);
27 num_samples = NumSamples(config.msaa_mode); 30 num_samples = NumSamples(config.msaa_mode);
@@ -117,6 +120,9 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
117 120
118ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct, 121ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct,
119 Tegra::Texture::MsaaMode msaa_mode) noexcept { 122 Tegra::Texture::MsaaMode msaa_mode) noexcept {
123 forced_flushed =
124 ct.tile_mode.is_pitch_linear && !Settings::values.use_reactive_flushing.GetValue();
125 dma_downloaded = forced_flushed;
120 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(ct.format); 126 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(ct.format);
121 rescaleable = false; 127 rescaleable = false;
122 if (ct.tile_mode.is_pitch_linear) { 128 if (ct.tile_mode.is_pitch_linear) {
@@ -155,6 +161,9 @@ ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct,
155 161
156ImageInfo::ImageInfo(const Maxwell3D::Regs::Zeta& zt, const Maxwell3D::Regs::ZetaSize& zt_size, 162ImageInfo::ImageInfo(const Maxwell3D::Regs::Zeta& zt, const Maxwell3D::Regs::ZetaSize& zt_size,
157 Tegra::Texture::MsaaMode msaa_mode) noexcept { 163 Tegra::Texture::MsaaMode msaa_mode) noexcept {
164 forced_flushed =
165 zt.tile_mode.is_pitch_linear && !Settings::values.use_reactive_flushing.GetValue();
166 dma_downloaded = forced_flushed;
158 format = VideoCore::Surface::PixelFormatFromDepthFormat(zt.format); 167 format = VideoCore::Surface::PixelFormatFromDepthFormat(zt.format);
159 size.width = zt_size.width; 168 size.width = zt_size.width;
160 size.height = zt_size.height; 169 size.height = zt_size.height;
@@ -195,6 +204,9 @@ ImageInfo::ImageInfo(const Maxwell3D::Regs::Zeta& zt, const Maxwell3D::Regs::Zet
195 204
196ImageInfo::ImageInfo(const Fermi2D::Surface& config) noexcept { 205ImageInfo::ImageInfo(const Fermi2D::Surface& config) noexcept {
197 UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero"); 206 UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero");
207 forced_flushed = config.linear == Fermi2D::MemoryLayout::Pitch &&
208 !Settings::values.use_reactive_flushing.GetValue();
209 dma_downloaded = forced_flushed;
198 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format); 210 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format);
199 rescaleable = false; 211 rescaleable = false;
200 if (config.linear == Fermi2D::MemoryLayout::Pitch) { 212 if (config.linear == Fermi2D::MemoryLayout::Pitch) {
diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h
index 4b7dfa315..8a4cb0cbd 100644
--- a/src/video_core/texture_cache/image_info.h
+++ b/src/video_core/texture_cache/image_info.h
@@ -39,6 +39,8 @@ struct ImageInfo {
39 u32 tile_width_spacing = 0; 39 u32 tile_width_spacing = 0;
40 bool rescaleable = false; 40 bool rescaleable = false;
41 bool downscaleable = false; 41 bool downscaleable = false;
42 bool forced_flushed = false;
43 bool dma_downloaded = false;
42}; 44};
43 45
44} // namespace VideoCommon 46} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index 04fb84bfa..d134b6738 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -4,7 +4,6 @@
4#include <algorithm> 4#include <algorithm>
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/settings.h"
8#include "video_core/compatible_formats.h" 7#include "video_core/compatible_formats.h"
9#include "video_core/surface.h" 8#include "video_core/surface.h"
10#include "video_core/texture_cache/formatter.h" 9#include "video_core/texture_cache/formatter.h"
@@ -16,8 +15,8 @@
16namespace VideoCommon { 15namespace VideoCommon {
17 16
18ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info, 17ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info,
19 ImageId image_id_) 18 ImageId image_id_, GPUVAddr addr)
20 : image_id{image_id_}, format{info.format}, type{info.type}, range{info.range}, 19 : image_id{image_id_}, gpu_addr{addr}, format{info.format}, type{info.type}, range{info.range},
21 size{ 20 size{
22 .width = std::max(image_info.size.width >> range.base.level, 1u), 21 .width = std::max(image_info.size.width >> range.base.level, 1u),
23 .height = std::max(image_info.size.height >> range.base.level, 1u), 22 .height = std::max(image_info.size.height >> range.base.level, 1u),
@@ -26,8 +25,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
26 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true), 25 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
27 "Image view format {} is incompatible with image format {}", info.format, 26 "Image view format {} is incompatible with image format {}", info.format,
28 image_info.format); 27 image_info.format);
29 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 28 if (image_info.forced_flushed) {
30 if (image_info.type == ImageType::Linear && is_async) {
31 flags |= ImageViewFlagBits::PreemtiveDownload; 29 flags |= ImageViewFlagBits::PreemtiveDownload;
32 } 30 }
33 if (image_info.type == ImageType::e3D && info.type != ImageViewType::e3D) { 31 if (image_info.type == ImageType::e3D && info.type != ImageViewType::e3D) {
@@ -35,8 +33,8 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
35 } 33 }
36} 34}
37 35
38ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info) 36ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info, GPUVAddr addr)
39 : image_id{NULL_IMAGE_ID}, format{info.format}, type{ImageViewType::Buffer}, 37 : image_id{NULL_IMAGE_ID}, gpu_addr{addr}, format{info.format}, type{ImageViewType::Buffer},
40 size{ 38 size{
41 .width = info.size.width, 39 .width = info.size.width,
42 .height = 1, 40 .height = 1,
diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h
index 69c9776e7..a25ae1d4a 100644
--- a/src/video_core/texture_cache/image_view_base.h
+++ b/src/video_core/texture_cache/image_view_base.h
@@ -24,9 +24,9 @@ enum class ImageViewFlagBits : u16 {
24DECLARE_ENUM_FLAG_OPERATORS(ImageViewFlagBits) 24DECLARE_ENUM_FLAG_OPERATORS(ImageViewFlagBits)
25 25
26struct ImageViewBase { 26struct ImageViewBase {
27 explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info, 27 explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info, ImageId image_id,
28 ImageId image_id); 28 GPUVAddr addr);
29 explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info); 29 explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info, GPUVAddr addr);
30 explicit ImageViewBase(const NullImageViewParams&); 30 explicit ImageViewBase(const NullImageViewParams&);
31 31
32 [[nodiscard]] bool IsBuffer() const noexcept { 32 [[nodiscard]] bool IsBuffer() const noexcept {
@@ -34,6 +34,7 @@ struct ImageViewBase {
34 } 34 }
35 35
36 ImageId image_id{}; 36 ImageId image_id{};
37 GPUVAddr gpu_addr = 0;
37 PixelFormat format{}; 38 PixelFormat format{};
38 ImageViewType type{}; 39 ImageViewType type{};
39 SubresourceRange range; 40 SubresourceRange range;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index e601f8446..e1198dcf8 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -491,6 +491,32 @@ void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
491} 491}
492 492
493template <class P> 493template <class P>
494std::optional<VideoCore::RasterizerDownloadArea> TextureCache<P>::GetFlushArea(VAddr cpu_addr,
495 u64 size) {
496 std::optional<VideoCore::RasterizerDownloadArea> area{};
497 ForEachImageInRegion(cpu_addr, size, [&](ImageId, ImageBase& image) {
498 if (False(image.flags & ImageFlagBits::GpuModified)) {
499 return;
500 }
501 if (!area) {
502 area.emplace();
503 area->start_address = cpu_addr;
504 area->end_address = cpu_addr + size;
505 area->preemtive = true;
506 }
507 area->start_address = std::min(area->start_address, image.cpu_addr);
508 area->end_address = std::max(area->end_address, image.cpu_addr_end);
509 for (auto image_view_id : image.image_view_ids) {
510 auto& image_view = slot_image_views[image_view_id];
511 image_view.flags |= ImageViewFlagBits::PreemtiveDownload;
512 }
513 area->preemtive &= image.info.forced_flushed;
514 image.info.forced_flushed = true;
515 });
516 return area;
517}
518
519template <class P>
494void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { 520void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
495 std::vector<ImageId> deleted_images; 521 std::vector<ImageId> deleted_images;
496 ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); 522 ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); });
@@ -683,6 +709,7 @@ void TextureCache<P>::CommitAsyncFlushes() {
683 download_info.async_buffer_id = last_async_buffer_id; 709 download_info.async_buffer_id = last_async_buffer_id;
684 } 710 }
685 } 711 }
712
686 if (any_none_dma) { 713 if (any_none_dma) {
687 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes, true); 714 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes, true);
688 for (const PendingDownload& download_info : download_ids) { 715 for (const PendingDownload& download_info : download_ids) {
@@ -695,6 +722,7 @@ void TextureCache<P>::CommitAsyncFlushes() {
695 } 722 }
696 uncommitted_async_buffers.emplace_back(download_map); 723 uncommitted_async_buffers.emplace_back(download_map);
697 } 724 }
725
698 async_buffers.emplace_back(std::move(uncommitted_async_buffers)); 726 async_buffers.emplace_back(std::move(uncommitted_async_buffers));
699 uncommitted_async_buffers.clear(); 727 uncommitted_async_buffers.clear();
700 } 728 }
@@ -783,17 +811,22 @@ void TextureCache<P>::PopAsyncFlushes() {
783} 811}
784 812
785template <class P> 813template <class P>
786ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand) { 814ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) {
787 const ImageInfo dst_info(operand); 815 const ImageInfo dst_info(operand);
788 const ImageId dst_id = FindDMAImage(dst_info, operand.address); 816 const ImageId dst_id = FindDMAImage(dst_info, operand.address);
789 if (!dst_id) { 817 if (!dst_id) {
790 return NULL_IMAGE_ID; 818 return NULL_IMAGE_ID;
791 } 819 }
792 const auto& image = slot_images[dst_id]; 820 auto& image = slot_images[dst_id];
793 if (False(image.flags & ImageFlagBits::GpuModified)) { 821 if (False(image.flags & ImageFlagBits::GpuModified)) {
794 // No need to waste time on an image that's synced with guest 822 // No need to waste time on an image that's synced with guest
795 return NULL_IMAGE_ID; 823 return NULL_IMAGE_ID;
796 } 824 }
825 if (!is_upload && !image.info.dma_downloaded) {
826 // Force a full sync.
827 image.info.dma_downloaded = true;
828 return NULL_IMAGE_ID;
829 }
797 const auto base = image.TryFindBase(operand.address); 830 const auto base = image.TryFindBase(operand.address);
798 if (!base) { 831 if (!base) {
799 return NULL_IMAGE_ID; 832 return NULL_IMAGE_ID;
@@ -888,7 +921,7 @@ void TextureCache<P>::DownloadImageIntoBuffer(typename TextureCache<P>::Image* i
888 buffer, 921 buffer,
889 download_map.buffer, 922 download_map.buffer,
890 }; 923 };
891 std::array buffer_offsets{ 924 std::array<u64, 2> buffer_offsets{
892 buffer_offset, 925 buffer_offset,
893 download_map.offset, 926 download_map.offset,
894 }; 927 };
@@ -1290,7 +1323,6 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1290 all_siblings.push_back(overlap_id); 1323 all_siblings.push_back(overlap_id);
1291 } else { 1324 } else {
1292 bad_overlap_ids.push_back(overlap_id); 1325 bad_overlap_ids.push_back(overlap_id);
1293 overlap.flags |= ImageFlagBits::BadOverlap;
1294 } 1326 }
1295 }; 1327 };
1296 ForEachImageInRegion(cpu_addr, size_bytes, region_check); 1328 ForEachImageInRegion(cpu_addr, size_bytes, region_check);
@@ -1359,6 +1391,12 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1359 ScaleDown(new_image); 1391 ScaleDown(new_image);
1360 } 1392 }
1361 1393
1394 std::ranges::sort(overlap_ids, [this](const ImageId lhs, const ImageId rhs) {
1395 const ImageBase& lhs_image = slot_images[lhs];
1396 const ImageBase& rhs_image = slot_images[rhs];
1397 return lhs_image.modification_tick < rhs_image.modification_tick;
1398 });
1399
1362 for (const ImageId overlap_id : overlap_ids) { 1400 for (const ImageId overlap_id : overlap_ids) {
1363 Image& overlap = slot_images[overlap_id]; 1401 Image& overlap = slot_images[overlap_id];
1364 if (True(overlap.flags & ImageFlagBits::GpuModified)) { 1402 if (True(overlap.flags & ImageFlagBits::GpuModified)) {
@@ -1395,7 +1433,12 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1395 ImageBase& aliased = slot_images[aliased_id]; 1433 ImageBase& aliased = slot_images[aliased_id];
1396 aliased.overlapping_images.push_back(new_image_id); 1434 aliased.overlapping_images.push_back(new_image_id);
1397 new_image.overlapping_images.push_back(aliased_id); 1435 new_image.overlapping_images.push_back(aliased_id);
1398 new_image.flags |= ImageFlagBits::BadOverlap; 1436 if (aliased.info.resources.levels == 1 && aliased.overlapping_images.size() > 1) {
1437 aliased.flags |= ImageFlagBits::BadOverlap;
1438 }
1439 if (new_image.info.resources.levels == 1 && new_image.overlapping_images.size() > 1) {
1440 new_image.flags |= ImageFlagBits::BadOverlap;
1441 }
1399 } 1442 }
1400 RegisterImage(new_image_id); 1443 RegisterImage(new_image_id);
1401 return new_image_id; 1444 return new_image_id;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 758b7e212..0720494e5 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -179,6 +179,8 @@ public:
179 /// Download contents of host images to guest memory in a region 179 /// Download contents of host images to guest memory in a region
180 void DownloadMemory(VAddr cpu_addr, size_t size); 180 void DownloadMemory(VAddr cpu_addr, size_t size);
181 181
182 std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size);
183
182 /// Remove images in a region 184 /// Remove images in a region
183 void UnmapMemory(VAddr cpu_addr, size_t size); 185 void UnmapMemory(VAddr cpu_addr, size_t size);
184 186
@@ -205,7 +207,7 @@ public:
205 /// Pop asynchronous downloads 207 /// Pop asynchronous downloads
206 void PopAsyncFlushes(); 208 void PopAsyncFlushes();
207 209
208 [[nodiscard]] ImageId DmaImageId(const Tegra::DMA::ImageOperand& operand); 210 [[nodiscard]] ImageId DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload);
209 211
210 [[nodiscard]] std::pair<Image*, BufferImageCopy> DmaBufferImageCopy( 212 [[nodiscard]] std::pair<Image*, BufferImageCopy> DmaBufferImageCopy(
211 const Tegra::DMA::ImageCopy& copy_info, const Tegra::DMA::BufferOperand& buffer_operand, 213 const Tegra::DMA::ImageCopy& copy_info, const Tegra::DMA::BufferOperand& buffer_operand,
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index de37db684..f1071aa23 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -896,11 +896,11 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
896 ASSERT(copy.buffer_row_length == Common::AlignUp(mip_size.width, tile_size.width)); 896 ASSERT(copy.buffer_row_length == Common::AlignUp(mip_size.width, tile_size.width));
897 ASSERT(copy.buffer_image_height == Common::AlignUp(mip_size.height, tile_size.height)); 897 ASSERT(copy.buffer_image_height == Common::AlignUp(mip_size.height, tile_size.height));
898 if (IsPixelFormatASTC(info.format)) { 898 if (IsPixelFormatASTC(info.format)) {
899 ASSERT(copy.image_extent.depth == 1); 899 Tegra::Texture::ASTC::Decompress(
900 Tegra::Texture::ASTC::Decompress(input.subspan(copy.buffer_offset), 900 input.subspan(copy.buffer_offset), copy.image_extent.width,
901 copy.image_extent.width, copy.image_extent.height, 901 copy.image_extent.height,
902 copy.image_subresource.num_layers, tile_size.width, 902 copy.image_subresource.num_layers * copy.image_extent.depth, tile_size.width,
903 tile_size.height, output.subspan(output_offset)); 903 tile_size.height, output.subspan(output_offset));
904 } else { 904 } else {
905 DecompressBC4(input.subspan(copy.buffer_offset), copy.image_extent, 905 DecompressBC4(input.subspan(copy.buffer_offset), copy.image_extent,
906 output.subspan(output_offset)); 906 output.subspan(output_offset));
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 6f288b3f8..6ffca2af2 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -617,7 +617,9 @@ bool Device::ShouldBoostClocks() const {
617 617
618 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; 618 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F;
619 619
620 return validated_driver && !is_steam_deck; 620 const bool is_debugging = this->HasDebuggingToolAttached();
621
622 return validated_driver && !is_steam_deck && !is_debugging;
621} 623}
622 624
623bool Device::GetSuitability(bool requires_swapchain) { 625bool Device::GetSuitability(bool requires_swapchain) {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 7d5018151..5f1c63ff9 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/settings.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
14 15
15// Define all features which may be used by the implementation here. 16// Define all features which may be used by the implementation here.
@@ -510,7 +511,7 @@ public:
510 511
511 /// Returns true when a known debugging tool is attached. 512 /// Returns true when a known debugging tool is attached.
512 bool HasDebuggingToolAttached() const { 513 bool HasDebuggingToolAttached() const {
513 return has_renderdoc || has_nsight_graphics; 514 return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue();
514 } 515 }
515 516
516 /// Returns true when the device does not properly support cube compatibility. 517 /// Returns true when the device does not properly support cube compatibility.
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp
index fa9bafa20..c34599365 100644
--- a/src/video_core/vulkan_common/vulkan_surface.cpp
+++ b/src/video_core/vulkan_common/vulkan_surface.cpp
@@ -23,10 +23,10 @@
23 23
24namespace Vulkan { 24namespace Vulkan {
25 25
26vk::SurfaceKHR CreateSurface(const vk::Instance& instance, 26vk::SurfaceKHR CreateSurface(
27 const Core::Frontend::EmuWindow& emu_window) { 27 const vk::Instance& instance,
28 [[maybe_unused]] const Core::Frontend::EmuWindow::WindowSystemInfo& window_info) {
28 [[maybe_unused]] const vk::InstanceDispatch& dld = instance.Dispatch(); 29 [[maybe_unused]] const vk::InstanceDispatch& dld = instance.Dispatch();
29 [[maybe_unused]] const auto& window_info = emu_window.GetWindowInfo();
30 VkSurfaceKHR unsafe_surface = nullptr; 30 VkSurfaceKHR unsafe_surface = nullptr;
31 31
32#ifdef _WIN32 32#ifdef _WIN32
diff --git a/src/video_core/vulkan_common/vulkan_surface.h b/src/video_core/vulkan_common/vulkan_surface.h
index 5725143e6..5e18c06c4 100644
--- a/src/video_core/vulkan_common/vulkan_surface.h
+++ b/src/video_core/vulkan_common/vulkan_surface.h
@@ -3,15 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/frontend/emu_window.h"
6#include "video_core/vulkan_common/vulkan_wrapper.h" 7#include "video_core/vulkan_common/vulkan_wrapper.h"
7 8
8namespace Core::Frontend {
9class EmuWindow;
10}
11
12namespace Vulkan { 9namespace Vulkan {
13 10
14[[nodiscard]] vk::SurfaceKHR CreateSurface(const vk::Instance& instance, 11[[nodiscard]] vk::SurfaceKHR CreateSurface(
15 const Core::Frontend::EmuWindow& emu_window); 12 const vk::Instance& instance, const Core::Frontend::EmuWindow::WindowSystemInfo& window_info);
16 13
17} // namespace Vulkan 14} // namespace Vulkan
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 0f8c1e6a6..2d7b9ab65 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -189,6 +189,8 @@ add_executable(yuzu
189 multiplayer/state.h 189 multiplayer/state.h
190 multiplayer/validation.h 190 multiplayer/validation.h
191 precompiled_headers.h 191 precompiled_headers.h
192 qt_common.cpp
193 qt_common.h
192 startup_checks.cpp 194 startup_checks.cpp
193 startup_checks.h 195 startup_checks.h
194 uisettings.cpp 196 uisettings.cpp
diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp
index 4559df5b1..4988fcc83 100644
--- a/src/yuzu/applets/qt_amiibo_settings.cpp
+++ b/src/yuzu/applets/qt_amiibo_settings.cpp
@@ -8,7 +8,7 @@
8 8
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/hle/service/nfp/nfp_device.h" 11#include "core/hle/service/nfc/common/device.h"
12#include "core/hle/service/nfp/nfp_result.h" 12#include "core/hle/service/nfp/nfp_result.h"
13#include "input_common/drivers/virtual_amiibo.h" 13#include "input_common/drivers/virtual_amiibo.h"
14#include "input_common/main.h" 14#include "input_common/main.h"
@@ -22,7 +22,7 @@
22QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent, 22QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
23 Core::Frontend::CabinetParameters parameters_, 23 Core::Frontend::CabinetParameters parameters_,
24 InputCommon::InputSubsystem* input_subsystem_, 24 InputCommon::InputSubsystem* input_subsystem_,
25 std::shared_ptr<Service::NFP::NfpDevice> nfp_device_) 25 std::shared_ptr<Service::NFC::NfcDevice> nfp_device_)
26 : QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()), 26 : QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()),
27 input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)}, 27 input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)},
28 parameters(std::move(parameters_)) { 28 parameters(std::move(parameters_)) {
@@ -52,11 +52,11 @@ void QtAmiiboSettingsDialog::LoadInfo() {
52 return; 52 return;
53 } 53 }
54 54
55 if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && 55 if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound &&
56 nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { 56 nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) {
57 return; 57 return;
58 } 58 }
59 nfp_device->Mount(Service::NFP::MountTarget::All); 59 nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All);
60 60
61 LoadAmiiboInfo(); 61 LoadAmiiboInfo();
62 LoadAmiiboData(); 62 LoadAmiiboData();
@@ -261,7 +261,7 @@ void QtAmiiboSettings::Close() const {
261void QtAmiiboSettings::ShowCabinetApplet( 261void QtAmiiboSettings::ShowCabinetApplet(
262 const Core::Frontend::CabinetCallback& callback_, 262 const Core::Frontend::CabinetCallback& callback_,
263 const Core::Frontend::CabinetParameters& parameters, 263 const Core::Frontend::CabinetParameters& parameters,
264 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const { 264 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const {
265 callback = std::move(callback_); 265 callback = std::move(callback_);
266 emit MainWindowShowAmiiboSettings(parameters, nfp_device); 266 emit MainWindowShowAmiiboSettings(parameters, nfp_device);
267} 267}
diff --git a/src/yuzu/applets/qt_amiibo_settings.h b/src/yuzu/applets/qt_amiibo_settings.h
index bc389a33f..ee66a0255 100644
--- a/src/yuzu/applets/qt_amiibo_settings.h
+++ b/src/yuzu/applets/qt_amiibo_settings.h
@@ -23,9 +23,9 @@ namespace Ui {
23class QtAmiiboSettingsDialog; 23class QtAmiiboSettingsDialog;
24} 24}
25 25
26namespace Service::NFP { 26namespace Service::NFC {
27class NfpDevice; 27class NfcDevice;
28} // namespace Service::NFP 28} // namespace Service::NFC
29 29
30class QtAmiiboSettingsDialog final : public QDialog { 30class QtAmiiboSettingsDialog final : public QDialog {
31 Q_OBJECT 31 Q_OBJECT
@@ -33,7 +33,7 @@ class QtAmiiboSettingsDialog final : public QDialog {
33public: 33public:
34 explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_, 34 explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_,
35 InputCommon::InputSubsystem* input_subsystem_, 35 InputCommon::InputSubsystem* input_subsystem_,
36 std::shared_ptr<Service::NFP::NfpDevice> nfp_device_); 36 std::shared_ptr<Service::NFC::NfcDevice> nfp_device_);
37 ~QtAmiiboSettingsDialog() override; 37 ~QtAmiiboSettingsDialog() override;
38 38
39 int exec() override; 39 int exec() override;
@@ -52,7 +52,7 @@ private:
52 std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui; 52 std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui;
53 53
54 InputCommon::InputSubsystem* input_subsystem; 54 InputCommon::InputSubsystem* input_subsystem;
55 std::shared_ptr<Service::NFP::NfpDevice> nfp_device; 55 std::shared_ptr<Service::NFC::NfcDevice> nfp_device;
56 56
57 // Parameters sent in from the backend HLE applet. 57 // Parameters sent in from the backend HLE applet.
58 Core::Frontend::CabinetParameters parameters; 58 Core::Frontend::CabinetParameters parameters;
@@ -71,11 +71,11 @@ public:
71 void Close() const override; 71 void Close() const override;
72 void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_, 72 void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_,
73 const Core::Frontend::CabinetParameters& parameters, 73 const Core::Frontend::CabinetParameters& parameters,
74 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override; 74 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const override;
75 75
76signals: 76signals:
77 void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters, 77 void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters,
78 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const; 78 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const;
79 void MainWindowRequestExit() const; 79 void MainWindowRequestExit() const;
80 80
81private: 81private:
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index 2448e46b6..1f3f23038 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -95,6 +95,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(
95 scroll_area->setLayout(layout); 95 scroll_area->setLayout(layout);
96 96
97 connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); 97 connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser);
98 connect(tree_view, &QTreeView::doubleClicked, this, &QtProfileSelectionDialog::accept);
98 connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, 99 connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
99 [this](Qt::Key key) { 100 [this](Qt::Key key) {
100 if (!this->isActiveWindow()) { 101 if (!this->isActiveWindow()) {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 4c7bf28d8..59d226113 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -1,36 +1,48 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5#include <array>
6#include <cmath>
7#include <cstring>
8#include <string>
9#include <tuple>
10#include <type_traits>
4#include <glad/glad.h> 11#include <glad/glad.h>
5 12
6#include <QApplication> 13#include <QtCore/qglobal.h>
7#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA 14#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
15#include <QCamera>
8#include <QCameraImageCapture> 16#include <QCameraImageCapture>
9#include <QCameraInfo> 17#include <QCameraInfo>
10#endif 18#endif
19#include <QCursor>
20#include <QEvent>
21#include <QGuiApplication>
11#include <QHBoxLayout> 22#include <QHBoxLayout>
23#include <QKeyEvent>
24#include <QLayout>
25#include <QList>
12#include <QMessageBox> 26#include <QMessageBox>
13#include <QPainter>
14#include <QScreen> 27#include <QScreen>
15#include <QString> 28#include <QSize>
16#include <QStringList> 29#include <QStringLiteral>
30#include <QSurfaceFormat>
31#include <QTimer>
17#include <QWindow> 32#include <QWindow>
33#include <QtCore/qobjectdefs.h>
18 34
19#ifdef HAS_OPENGL 35#ifdef HAS_OPENGL
20#include <QOffscreenSurface> 36#include <QOffscreenSurface>
21#include <QOpenGLContext> 37#include <QOpenGLContext>
22#endif 38#endif
23 39
24#if !defined(WIN32)
25#include <qpa/qplatformnativeinterface.h>
26#endif
27
28#include <fmt/format.h>
29
30#include "common/assert.h"
31#include "common/microprofile.h" 40#include "common/microprofile.h"
41#include "common/polyfill_thread.h"
32#include "common/scm_rev.h" 42#include "common/scm_rev.h"
33#include "common/settings.h" 43#include "common/settings.h"
44#include "common/settings_input.h"
45#include "common/thread.h"
34#include "core/core.h" 46#include "core/core.h"
35#include "core/cpu_manager.h" 47#include "core/cpu_manager.h"
36#include "core/frontend/framebuffer_layout.h" 48#include "core/frontend/framebuffer_layout.h"
@@ -40,11 +52,16 @@
40#include "input_common/drivers/tas_input.h" 52#include "input_common/drivers/tas_input.h"
41#include "input_common/drivers/touch_screen.h" 53#include "input_common/drivers/touch_screen.h"
42#include "input_common/main.h" 54#include "input_common/main.h"
55#include "video_core/gpu.h"
56#include "video_core/rasterizer_interface.h"
43#include "video_core/renderer_base.h" 57#include "video_core/renderer_base.h"
44#include "yuzu/bootmanager.h" 58#include "yuzu/bootmanager.h"
45#include "yuzu/main.h" 59#include "yuzu/main.h"
60#include "yuzu/qt_common.h"
46 61
47static Core::Frontend::WindowSystemType GetWindowSystemType(); 62class QObject;
63class QPaintEngine;
64class QSurface;
48 65
49EmuThread::EmuThread(Core::System& system) : m_system{system} {} 66EmuThread::EmuThread(Core::System& system) : m_system{system} {}
50 67
@@ -154,7 +171,10 @@ public:
154 171
155 // disable vsync for any shared contexts 172 // disable vsync for any shared contexts
156 auto format = share_context->format(); 173 auto format = share_context->format();
157 format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0); 174 const int swap_interval =
175 Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1;
176
177 format.setSwapInterval(main_surface ? swap_interval : 0);
158 178
159 context = std::make_unique<QOpenGLContext>(); 179 context = std::make_unique<QOpenGLContext>();
160 context->setShareContext(share_context); 180 context->setShareContext(share_context);
@@ -221,7 +241,7 @@ public:
221 explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { 241 explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
222 setAttribute(Qt::WA_NativeWindow); 242 setAttribute(Qt::WA_NativeWindow);
223 setAttribute(Qt::WA_PaintOnScreen); 243 setAttribute(Qt::WA_PaintOnScreen);
224 if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) { 244 if (QtCommon::GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) {
225 setAttribute(Qt::WA_DontCreateNativeAncestors); 245 setAttribute(Qt::WA_DontCreateNativeAncestors);
226 } 246 }
227 } 247 }
@@ -259,46 +279,6 @@ struct NullRenderWidget : public RenderWidget {
259 explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} 279 explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
260}; 280};
261 281
262static Core::Frontend::WindowSystemType GetWindowSystemType() {
263 // Determine WSI type based on Qt platform.
264 QString platform_name = QGuiApplication::platformName();
265 if (platform_name == QStringLiteral("windows"))
266 return Core::Frontend::WindowSystemType::Windows;
267 else if (platform_name == QStringLiteral("xcb"))
268 return Core::Frontend::WindowSystemType::X11;
269 else if (platform_name == QStringLiteral("wayland"))
270 return Core::Frontend::WindowSystemType::Wayland;
271 else if (platform_name == QStringLiteral("wayland-egl"))
272 return Core::Frontend::WindowSystemType::Wayland;
273 else if (platform_name == QStringLiteral("cocoa"))
274 return Core::Frontend::WindowSystemType::Cocoa;
275 else if (platform_name == QStringLiteral("android"))
276 return Core::Frontend::WindowSystemType::Android;
277
278 LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
279 return Core::Frontend::WindowSystemType::Windows;
280}
281
282static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
283 Core::Frontend::EmuWindow::WindowSystemInfo wsi;
284 wsi.type = GetWindowSystemType();
285
286 // Our Win32 Qt external doesn't have the private API.
287#if defined(WIN32) || defined(__APPLE__)
288 wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
289#else
290 QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
291 wsi.display_connection = pni->nativeResourceForWindow("display", window);
292 if (wsi.type == Core::Frontend::WindowSystemType::Wayland)
293 wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
294 else
295 wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
296#endif
297 wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
298
299 return wsi;
300}
301
302GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, 282GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
303 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_, 283 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
304 Core::System& system_) 284 Core::System& system_)
@@ -904,7 +884,7 @@ bool GRenderWindow::InitRenderTarget() {
904 } 884 }
905 885
906 // Update the Window System information with the new render target 886 // Update the Window System information with the new render target
907 window_info = GetWindowSystemInfo(child_widget->windowHandle()); 887 window_info = QtCommon::GetWindowSystemInfo(child_widget->windowHandle());
908 888
909 child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); 889 child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
910 layout()->addWidget(child_widget); 890 layout()->addWidget(child_widget);
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index bb4eca07f..b7b9d4141 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -5,27 +5,45 @@
5 5
6#include <atomic> 6#include <atomic>
7#include <condition_variable> 7#include <condition_variable>
8#include <cstddef>
8#include <memory> 9#include <memory>
9#include <mutex> 10#include <mutex>
11#include <utility>
12#include <vector>
10 13
14#include <QByteArray>
11#include <QImage> 15#include <QImage>
16#include <QObject>
17#include <QPoint>
18#include <QString>
12#include <QStringList> 19#include <QStringList>
13#include <QThread> 20#include <QThread>
14#include <QTouchEvent>
15#include <QWidget> 21#include <QWidget>
22#include <qglobal.h>
23#include <qnamespace.h>
24#include <qobjectdefs.h>
16 25
26#include "common/common_types.h"
27#include "common/logging/log.h"
17#include "common/polyfill_thread.h" 28#include "common/polyfill_thread.h"
18#include "common/thread.h" 29#include "common/thread.h"
19#include "core/frontend/emu_window.h" 30#include "core/frontend/emu_window.h"
20 31
21class GRenderWindow;
22class GMainWindow; 32class GMainWindow;
23class QCamera; 33class QCamera;
24class QCameraImageCapture; 34class QCameraImageCapture;
35class QCloseEvent;
36class QFocusEvent;
25class QKeyEvent; 37class QKeyEvent;
38class QMouseEvent;
39class QObject;
40class QResizeEvent;
41class QShowEvent;
42class QTimer;
43class QTouchEvent;
44class QWheelEvent;
26 45
27namespace Core { 46namespace Core {
28enum class SystemResultStatus : u32;
29class System; 47class System;
30} // namespace Core 48} // namespace Core
31 49
@@ -40,7 +58,6 @@ enum class TasState;
40 58
41namespace VideoCore { 59namespace VideoCore {
42enum class LoadCallbackStage; 60enum class LoadCallbackStage;
43class RendererBase;
44} // namespace VideoCore 61} // namespace VideoCore
45 62
46class EmuThread final : public QThread { 63class EmuThread final : public QThread {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bb731276e..a49d12266 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -6,6 +6,7 @@
6#include <QSettings> 6#include <QSettings>
7#include "common/fs/fs.h" 7#include "common/fs/fs.h"
8#include "common/fs/path_util.h" 8#include "common/fs/path_util.h"
9#include "common/settings.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
11#include "core/hle/service/hid/controllers/npad.h" 12#include "core/hle/service/hid/controllers/npad.h"
@@ -497,7 +498,7 @@ void Config::ReadCoreValues() {
497 qt_config->beginGroup(QStringLiteral("Core")); 498 qt_config->beginGroup(QStringLiteral("Core"));
498 499
499 ReadGlobalSetting(Settings::values.use_multi_core); 500 ReadGlobalSetting(Settings::values.use_multi_core);
500 ReadGlobalSetting(Settings::values.use_extended_memory_layout); 501 ReadGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
501 502
502 qt_config->endGroup(); 503 qt_config->endGroup();
503} 504}
@@ -692,6 +693,7 @@ void Config::ReadRendererValues() {
692 qt_config->beginGroup(QStringLiteral("Renderer")); 693 qt_config->beginGroup(QStringLiteral("Renderer"));
693 694
694 ReadGlobalSetting(Settings::values.renderer_backend); 695 ReadGlobalSetting(Settings::values.renderer_backend);
696 ReadGlobalSetting(Settings::values.async_presentation);
695 ReadGlobalSetting(Settings::values.renderer_force_max_clock); 697 ReadGlobalSetting(Settings::values.renderer_force_max_clock);
696 ReadGlobalSetting(Settings::values.vulkan_device); 698 ReadGlobalSetting(Settings::values.vulkan_device);
697 ReadGlobalSetting(Settings::values.fullscreen_mode); 699 ReadGlobalSetting(Settings::values.fullscreen_mode);
@@ -708,17 +710,20 @@ void Config::ReadRendererValues() {
708 ReadGlobalSetting(Settings::values.nvdec_emulation); 710 ReadGlobalSetting(Settings::values.nvdec_emulation);
709 ReadGlobalSetting(Settings::values.accelerate_astc); 711 ReadGlobalSetting(Settings::values.accelerate_astc);
710 ReadGlobalSetting(Settings::values.async_astc); 712 ReadGlobalSetting(Settings::values.async_astc);
711 ReadGlobalSetting(Settings::values.use_vsync); 713 ReadGlobalSetting(Settings::values.use_reactive_flushing);
712 ReadGlobalSetting(Settings::values.shader_backend); 714 ReadGlobalSetting(Settings::values.shader_backend);
713 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 715 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
714 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 716 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
715 ReadGlobalSetting(Settings::values.use_pessimistic_flushes);
716 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 717 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
717 ReadGlobalSetting(Settings::values.bg_red); 718 ReadGlobalSetting(Settings::values.bg_red);
718 ReadGlobalSetting(Settings::values.bg_green); 719 ReadGlobalSetting(Settings::values.bg_green);
719 ReadGlobalSetting(Settings::values.bg_blue); 720 ReadGlobalSetting(Settings::values.bg_blue);
720 721
721 if (global) { 722 if (global) {
723 Settings::values.vsync_mode.SetValue(static_cast<Settings::VSyncMode>(
724 ReadSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()),
725 static_cast<u32>(Settings::values.vsync_mode.GetDefault()))
726 .value<u32>()));
722 ReadBasicSetting(Settings::values.renderer_debug); 727 ReadBasicSetting(Settings::values.renderer_debug);
723 ReadBasicSetting(Settings::values.renderer_shader_feedback); 728 ReadBasicSetting(Settings::values.renderer_shader_feedback);
724 ReadBasicSetting(Settings::values.enable_nsight_aftermath); 729 ReadBasicSetting(Settings::values.enable_nsight_aftermath);
@@ -1161,7 +1166,7 @@ void Config::SaveCoreValues() {
1161 qt_config->beginGroup(QStringLiteral("Core")); 1166 qt_config->beginGroup(QStringLiteral("Core"));
1162 1167
1163 WriteGlobalSetting(Settings::values.use_multi_core); 1168 WriteGlobalSetting(Settings::values.use_multi_core);
1164 WriteGlobalSetting(Settings::values.use_extended_memory_layout); 1169 WriteGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
1165 1170
1166 qt_config->endGroup(); 1171 qt_config->endGroup();
1167} 1172}
@@ -1313,6 +1318,7 @@ void Config::SaveRendererValues() {
1313 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), 1318 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)),
1314 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1319 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1315 Settings::values.renderer_backend.UsingGlobal()); 1320 Settings::values.renderer_backend.UsingGlobal());
1321 WriteGlobalSetting(Settings::values.async_presentation);
1316 WriteGlobalSetting(Settings::values.renderer_force_max_clock); 1322 WriteGlobalSetting(Settings::values.renderer_force_max_clock);
1317 WriteGlobalSetting(Settings::values.vulkan_device); 1323 WriteGlobalSetting(Settings::values.vulkan_device);
1318 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()), 1324 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
@@ -1350,20 +1356,22 @@ void Config::SaveRendererValues() {
1350 Settings::values.nvdec_emulation.UsingGlobal()); 1356 Settings::values.nvdec_emulation.UsingGlobal());
1351 WriteGlobalSetting(Settings::values.accelerate_astc); 1357 WriteGlobalSetting(Settings::values.accelerate_astc);
1352 WriteGlobalSetting(Settings::values.async_astc); 1358 WriteGlobalSetting(Settings::values.async_astc);
1353 WriteGlobalSetting(Settings::values.use_vsync); 1359 WriteGlobalSetting(Settings::values.use_reactive_flushing);
1354 WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()), 1360 WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()),
1355 static_cast<u32>(Settings::values.shader_backend.GetValue(global)), 1361 static_cast<u32>(Settings::values.shader_backend.GetValue(global)),
1356 static_cast<u32>(Settings::values.shader_backend.GetDefault()), 1362 static_cast<u32>(Settings::values.shader_backend.GetDefault()),
1357 Settings::values.shader_backend.UsingGlobal()); 1363 Settings::values.shader_backend.UsingGlobal());
1358 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1364 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1359 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1365 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
1360 WriteGlobalSetting(Settings::values.use_pessimistic_flushes);
1361 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 1366 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1362 WriteGlobalSetting(Settings::values.bg_red); 1367 WriteGlobalSetting(Settings::values.bg_red);
1363 WriteGlobalSetting(Settings::values.bg_green); 1368 WriteGlobalSetting(Settings::values.bg_green);
1364 WriteGlobalSetting(Settings::values.bg_blue); 1369 WriteGlobalSetting(Settings::values.bg_blue);
1365 1370
1366 if (global) { 1371 if (global) {
1372 WriteSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()),
1373 static_cast<u32>(Settings::values.vsync_mode.GetValue()),
1374 static_cast<u32>(Settings::values.vsync_mode.GetDefault()));
1367 WriteBasicSetting(Settings::values.renderer_debug); 1375 WriteBasicSetting(Settings::values.renderer_debug);
1368 WriteBasicSetting(Settings::values.renderer_shader_feedback); 1376 WriteBasicSetting(Settings::values.renderer_shader_feedback);
1369 WriteBasicSetting(Settings::values.enable_nsight_aftermath); 1377 WriteBasicSetting(Settings::values.enable_nsight_aftermath);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 207bcdc4d..26258d744 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -35,9 +35,6 @@ void ConfigureGeneral::SetConfiguration() {
35 35
36 ui->use_multi_core->setEnabled(runtime_lock); 36 ui->use_multi_core->setEnabled(runtime_lock);
37 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); 37 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
38 ui->use_extended_memory_layout->setEnabled(runtime_lock);
39 ui->use_extended_memory_layout->setChecked(
40 Settings::values.use_extended_memory_layout.GetValue());
41 38
42 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); 39 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
43 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); 40 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
@@ -79,9 +76,6 @@ void ConfigureGeneral::ResetDefaults() {
79void ConfigureGeneral::ApplyConfiguration() { 76void ConfigureGeneral::ApplyConfiguration() {
80 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, 77 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core,
81 use_multi_core); 78 use_multi_core);
82 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_extended_memory_layout,
83 ui->use_extended_memory_layout,
84 use_extended_memory_layout);
85 79
86 if (Settings::IsConfiguringGlobal()) { 80 if (Settings::IsConfiguringGlobal()) {
87 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 81 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -141,9 +135,6 @@ void ConfigureGeneral::SetupPerGameUI() {
141 Settings::values.use_speed_limit, use_speed_limit); 135 Settings::values.use_speed_limit, use_speed_limit);
142 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, 136 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
143 use_multi_core); 137 use_multi_core);
144 ConfigurationShared::SetColoredTristate(ui->use_extended_memory_layout,
145 Settings::values.use_extended_memory_layout,
146 use_extended_memory_layout);
147 138
148 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { 139 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
149 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && 140 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index a090c1a3f..7ff63f425 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -47,7 +47,6 @@ private:
47 47
48 ConfigurationShared::CheckState use_speed_limit; 48 ConfigurationShared::CheckState use_speed_limit;
49 ConfigurationShared::CheckState use_multi_core; 49 ConfigurationShared::CheckState use_multi_core;
50 ConfigurationShared::CheckState use_extended_memory_layout;
51 50
52 const Core::System& system; 51 const Core::System& system;
53}; 52};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index add110bb0..986a1625b 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -62,13 +62,6 @@
62 </widget> 62 </widget>
63 </item> 63 </item>
64 <item> 64 <item>
65 <widget class="QCheckBox" name="use_extended_memory_layout">
66 <property name="text">
67 <string>Extended memory layout (8GB DRAM)</string>
68 </property>
69 </widget>
70 </item>
71 <item>
72 <widget class="QCheckBox" name="toggle_check_exit"> 65 <widget class="QCheckBox" name="toggle_check_exit">
73 <property name="text"> 66 <property name="text">
74 <string>Confirm exit while emulation is running</string> 67 <string>Confirm exit while emulation is running</string>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index e9388daad..76e5b7499 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -4,20 +4,76 @@
4// Include this early to include Vulkan headers how we want to 4// Include this early to include Vulkan headers how we want to
5#include "video_core/vulkan_common/vulkan_wrapper.h" 5#include "video_core/vulkan_common/vulkan_wrapper.h"
6 6
7#include <algorithm>
8#include <iosfwd>
9#include <iterator>
10#include <string>
11#include <tuple>
12#include <utility>
13#include <vector>
14#include <QBoxLayout>
15#include <QCheckBox>
7#include <QColorDialog> 16#include <QColorDialog>
8#include <QVulkanInstance> 17#include <QComboBox>
18#include <QIcon>
19#include <QLabel>
20#include <QPixmap>
21#include <QPushButton>
22#include <QSlider>
23#include <QStringLiteral>
24#include <QtCore/qobjectdefs.h>
25#include <qcoreevent.h>
26#include <qglobal.h>
27#include <vulkan/vulkan_core.h>
9 28
10#include "common/common_types.h" 29#include "common/common_types.h"
30#include "common/dynamic_library.h"
11#include "common/logging/log.h" 31#include "common/logging/log.h"
12#include "common/settings.h" 32#include "common/settings.h"
13#include "core/core.h" 33#include "core/core.h"
14#include "ui_configure_graphics.h" 34#include "ui_configure_graphics.h"
15#include "video_core/vulkan_common/vulkan_instance.h" 35#include "video_core/vulkan_common/vulkan_instance.h"
16#include "video_core/vulkan_common/vulkan_library.h" 36#include "video_core/vulkan_common/vulkan_library.h"
37#include "video_core/vulkan_common/vulkan_surface.h"
17#include "yuzu/configuration/configuration_shared.h" 38#include "yuzu/configuration/configuration_shared.h"
18#include "yuzu/configuration/configure_graphics.h" 39#include "yuzu/configuration/configure_graphics.h"
40#include "yuzu/qt_common.h"
19#include "yuzu/uisettings.h" 41#include "yuzu/uisettings.h"
20 42
43static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
44 VK_PRESENT_MODE_FIFO_KHR};
45
46// Converts a setting to a present mode (or vice versa)
47static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
48 switch (mode) {
49 case Settings::VSyncMode::Immediate:
50 return VK_PRESENT_MODE_IMMEDIATE_KHR;
51 case Settings::VSyncMode::Mailbox:
52 return VK_PRESENT_MODE_MAILBOX_KHR;
53 case Settings::VSyncMode::FIFO:
54 return VK_PRESENT_MODE_FIFO_KHR;
55 case Settings::VSyncMode::FIFORelaxed:
56 return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
57 default:
58 return VK_PRESENT_MODE_FIFO_KHR;
59 }
60}
61
62static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) {
63 switch (mode) {
64 case VK_PRESENT_MODE_IMMEDIATE_KHR:
65 return Settings::VSyncMode::Immediate;
66 case VK_PRESENT_MODE_MAILBOX_KHR:
67 return Settings::VSyncMode::Mailbox;
68 case VK_PRESENT_MODE_FIFO_KHR:
69 return Settings::VSyncMode::FIFO;
70 case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
71 return Settings::VSyncMode::FIFORelaxed;
72 default:
73 return Settings::VSyncMode::FIFO;
74 }
75}
76
21ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) 77ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
22 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { 78 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
23 vulkan_device = Settings::values.vulkan_device.GetValue(); 79 vulkan_device = Settings::values.vulkan_device.GetValue();
@@ -39,13 +95,16 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
39 95
40 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { 96 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
41 UpdateAPILayout(); 97 UpdateAPILayout();
98 PopulateVSyncModeSelection();
42 if (!Settings::IsConfiguringGlobal()) { 99 if (!Settings::IsConfiguringGlobal()) {
43 ConfigurationShared::SetHighlight( 100 ConfigurationShared::SetHighlight(
44 ui->api_widget, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX); 101 ui->api_widget, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
45 } 102 }
46 }); 103 });
47 connect(ui->device, qOverload<int>(&QComboBox::activated), this, 104 connect(ui->device, qOverload<int>(&QComboBox::activated), this, [this](int device) {
48 [this](int device) { UpdateDeviceSelection(device); }); 105 UpdateDeviceSelection(device);
106 PopulateVSyncModeSelection();
107 });
49 connect(ui->backend, qOverload<int>(&QComboBox::activated), this, 108 connect(ui->backend, qOverload<int>(&QComboBox::activated), this,
50 [this](int backend) { UpdateShaderBackendSelection(backend); }); 109 [this](int backend) { UpdateShaderBackendSelection(backend); });
51 110
@@ -70,6 +129,43 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
70 ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal()); 129 ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal());
71} 130}
72 131
132void ConfigureGraphics::PopulateVSyncModeSelection() {
133 const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
134 if (backend == Settings::RendererBackend::Null) {
135 ui->vsync_mode_combobox->setEnabled(false);
136 return;
137 }
138 ui->vsync_mode_combobox->setEnabled(true);
139
140 const int current_index = //< current selected vsync mode from combobox
141 ui->vsync_mode_combobox->currentIndex();
142 const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
143 current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
144 : vsync_mode_combobox_enum_map[current_index];
145 int index{};
146 const int device{ui->device->currentIndex()}; //< current selected Vulkan device
147 const auto& present_modes = //< relevant vector of present modes for the selected device or API
148 backend == Settings::RendererBackend::Vulkan ? device_present_modes[device]
149 : default_present_modes;
150
151 ui->vsync_mode_combobox->clear();
152 vsync_mode_combobox_enum_map.clear();
153 vsync_mode_combobox_enum_map.reserve(present_modes.size());
154 for (const auto present_mode : present_modes) {
155 const auto mode_name = TranslateVSyncMode(present_mode, backend);
156 if (mode_name.isEmpty()) {
157 continue;
158 }
159
160 ui->vsync_mode_combobox->insertItem(index, mode_name);
161 vsync_mode_combobox_enum_map.push_back(present_mode);
162 if (present_mode == current_mode) {
163 ui->vsync_mode_combobox->setCurrentIndex(index);
164 }
165 index++;
166 }
167}
168
73void ConfigureGraphics::UpdateDeviceSelection(int device) { 169void ConfigureGraphics::UpdateDeviceSelection(int device) {
74 if (device == -1) { 170 if (device == -1) {
75 return; 171 return;
@@ -99,6 +195,9 @@ void ConfigureGraphics::SetConfiguration() {
99 ui->nvdec_emulation_widget->setEnabled(runtime_lock); 195 ui->nvdec_emulation_widget->setEnabled(runtime_lock);
100 ui->resolution_combobox->setEnabled(runtime_lock); 196 ui->resolution_combobox->setEnabled(runtime_lock);
101 ui->accelerate_astc->setEnabled(runtime_lock); 197 ui->accelerate_astc->setEnabled(runtime_lock);
198 ui->vsync_mode_layout->setEnabled(runtime_lock ||
199 Settings::values.renderer_backend.GetValue() ==
200 Settings::RendererBackend::Vulkan);
102 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); 201 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
103 ui->use_asynchronous_gpu_emulation->setChecked( 202 ui->use_asynchronous_gpu_emulation->setChecked(
104 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 203 Settings::values.use_asynchronous_gpu_emulation.GetValue());
@@ -170,7 +269,24 @@ void ConfigureGraphics::SetConfiguration() {
170 Settings::values.bg_green.GetValue(), 269 Settings::values.bg_green.GetValue(),
171 Settings::values.bg_blue.GetValue())); 270 Settings::values.bg_blue.GetValue()));
172 UpdateAPILayout(); 271 UpdateAPILayout();
272 PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
173 SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition()); 273 SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition());
274
275 // VSync setting needs to be determined after populating the VSync combobox
276 if (Settings::IsConfiguringGlobal()) {
277 const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
278 const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
279 int index{};
280 for (const auto mode : vsync_mode_combobox_enum_map) {
281 if (mode == vsync_mode) {
282 break;
283 }
284 index++;
285 }
286 if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
287 ui->vsync_mode_combobox->setCurrentIndex(index);
288 }
289 }
174} 290}
175 291
176void ConfigureGraphics::SetFSRIndicatorText(int percentage) { 292void ConfigureGraphics::SetFSRIndicatorText(int percentage) {
@@ -178,6 +294,27 @@ void ConfigureGraphics::SetFSRIndicatorText(int percentage) {
178 tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2))); 294 tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2)));
179} 295}
180 296
297const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode,
298 Settings::RendererBackend backend) const {
299 switch (mode) {
300 case VK_PRESENT_MODE_IMMEDIATE_KHR:
301 return backend == Settings::RendererBackend::OpenGL
302 ? tr("Off")
303 : QStringLiteral("Immediate (%1)").arg(tr("VSync Off"));
304 case VK_PRESENT_MODE_MAILBOX_KHR:
305 return QStringLiteral("Mailbox (%1)").arg(tr("Recommended"));
306 case VK_PRESENT_MODE_FIFO_KHR:
307 return backend == Settings::RendererBackend::OpenGL
308 ? tr("On")
309 : QStringLiteral("FIFO (%1)").arg(tr("VSync On"));
310 case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
311 return QStringLiteral("FIFO Relaxed");
312 default:
313 return {};
314 break;
315 }
316}
317
181void ConfigureGraphics::ApplyConfiguration() { 318void ConfigureGraphics::ApplyConfiguration() {
182 const auto resolution_setup = static_cast<Settings::ResolutionSetup>( 319 const auto resolution_setup = static_cast<Settings::ResolutionSetup>(
183 ui->resolution_combobox->currentIndex() - 320 ui->resolution_combobox->currentIndex() -
@@ -232,6 +369,10 @@ void ConfigureGraphics::ApplyConfiguration() {
232 Settings::values.anti_aliasing.SetValue(anti_aliasing); 369 Settings::values.anti_aliasing.SetValue(anti_aliasing);
233 } 370 }
234 Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value()); 371 Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
372
373 const auto mode = vsync_mode_combobox_enum_map[ui->vsync_mode_combobox->currentIndex()];
374 const auto vsync_mode = PresentModeToSetting(mode);
375 Settings::values.vsync_mode.SetValue(vsync_mode);
235 } else { 376 } else {
236 if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 377 if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
237 Settings::values.resolution_setup.SetGlobal(true); 378 Settings::values.resolution_setup.SetGlobal(true);
@@ -345,7 +486,9 @@ void ConfigureGraphics::UpdateAPILayout() {
345 ui->backend_widget->setVisible(true); 486 ui->backend_widget->setVisible(true);
346 break; 487 break;
347 case Settings::RendererBackend::Vulkan: 488 case Settings::RendererBackend::Vulkan:
348 ui->device->setCurrentIndex(vulkan_device); 489 if (static_cast<int>(vulkan_device) < ui->device->count()) {
490 ui->device->setCurrentIndex(vulkan_device);
491 }
349 ui->device_widget->setVisible(true); 492 ui->device_widget->setVisible(true);
350 ui->backend_widget->setVisible(false); 493 ui->backend_widget->setVisible(false);
351 break; 494 break;
@@ -363,16 +506,27 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
363 506
364 using namespace Vulkan; 507 using namespace Vulkan;
365 508
509 auto* window = this->window()->windowHandle();
510 auto wsi = QtCommon::GetWindowSystemInfo(window);
511
366 vk::InstanceDispatch dld; 512 vk::InstanceDispatch dld;
367 const Common::DynamicLibrary library = OpenLibrary(); 513 const Common::DynamicLibrary library = OpenLibrary();
368 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1); 514 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1, wsi.type);
369 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); 515 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
516 vk::SurfaceKHR surface = //< needed to view present modes for a device
517 CreateSurface(instance, wsi);
370 518
371 vulkan_devices.clear(); 519 vulkan_devices.clear();
372 vulkan_devices.reserve(physical_devices.size()); 520 vulkan_devices.reserve(physical_devices.size());
521 device_present_modes.clear();
522 device_present_modes.reserve(physical_devices.size());
373 for (const VkPhysicalDevice device : physical_devices) { 523 for (const VkPhysicalDevice device : physical_devices) {
374 const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName; 524 const auto physical_device = vk::PhysicalDevice(device, dld);
525 const std::string name = physical_device.GetProperties().deviceName;
526 const std::vector<VkPresentModeKHR> present_modes =
527 physical_device.GetSurfacePresentModesKHR(*surface);
375 vulkan_devices.push_back(QString::fromStdString(name)); 528 vulkan_devices.push_back(QString::fromStdString(name));
529 device_present_modes.push_back(present_modes);
376 } 530 }
377} catch (const Vulkan::vk::Exception& exception) { 531} catch (const Vulkan::vk::Exception& exception) {
378 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); 532 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
@@ -465,4 +619,6 @@ void ConfigureGraphics::SetupPerGameUI() {
465 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 619 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
466 ConfigurationShared::InsertGlobalItem( 620 ConfigurationShared::InsertGlobalItem(
467 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); 621 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
622
623 ui->vsync_mode_layout->setVisible(false);
468} 624}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index d98d6624e..901f604a5 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -5,9 +5,21 @@
5 5
6#include <memory> 6#include <memory>
7#include <vector> 7#include <vector>
8#include <QColor>
8#include <QString> 9#include <QString>
9#include <QWidget> 10#include <QWidget>
10#include "common/settings.h" 11#include <qobjectdefs.h>
12#include <vulkan/vulkan_core.h>
13#include "common/common_types.h"
14
15class QEvent;
16class QObject;
17
18namespace Settings {
19enum class NvdecEmulation : u32;
20enum class RendererBackend : u32;
21enum class ShaderBackend : u32;
22} // namespace Settings
11 23
12namespace Core { 24namespace Core {
13class System; 25class System;
@@ -35,6 +47,7 @@ private:
35 void changeEvent(QEvent* event) override; 47 void changeEvent(QEvent* event) override;
36 void RetranslateUI(); 48 void RetranslateUI();
37 49
50 void PopulateVSyncModeSelection();
38 void UpdateBackgroundColorButton(QColor color); 51 void UpdateBackgroundColorButton(QColor color);
39 void UpdateAPILayout(); 52 void UpdateAPILayout();
40 void UpdateDeviceSelection(int device); 53 void UpdateDeviceSelection(int device);
@@ -43,6 +56,10 @@ private:
43 void RetrieveVulkanDevices(); 56 void RetrieveVulkanDevices();
44 57
45 void SetFSRIndicatorText(int percentage); 58 void SetFSRIndicatorText(int percentage);
59 /* Turns a Vulkan present mode into a textual string for a UI
60 * (and eventually for a human to read) */
61 const QString TranslateVSyncMode(VkPresentModeKHR mode,
62 Settings::RendererBackend backend) const;
46 63
47 void SetupPerGameUI(); 64 void SetupPerGameUI();
48 65
@@ -58,6 +75,10 @@ private:
58 ConfigurationShared::CheckState use_asynchronous_gpu_emulation; 75 ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
59 76
60 std::vector<QString> vulkan_devices; 77 std::vector<QString> vulkan_devices;
78 std::vector<std::vector<VkPresentModeKHR>> device_present_modes;
79 std::vector<VkPresentModeKHR>
80 vsync_mode_combobox_enum_map; //< Keeps track of which present mode corresponds to which
81 // selection in the combobox
61 u32 vulkan_device{}; 82 u32 vulkan_device{};
62 Settings::ShaderBackend shader_backend{}; 83 Settings::ShaderBackend shader_backend{};
63 84
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index a45ec69ec..39f70e406 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -189,6 +189,44 @@
189 </widget> 189 </widget>
190 </item> 190 </item>
191 <item> 191 <item>
192 <widget class="QWidget" name="vsync_mode_layout" native="true">
193 <layout class="QHBoxLayout" name="horizontalLayout_4">
194 <property name="leftMargin">
195 <number>0</number>
196 </property>
197 <property name="topMargin">
198 <number>0</number>
199 </property>
200 <property name="rightMargin">
201 <number>0</number>
202 </property>
203 <property name="bottomMargin">
204 <number>0</number>
205 </property>
206 <item>
207 <widget class="QLabel" name="vsync_mode_label">
208 <property name="text">
209 <string>VSync Mode:</string>
210 </property>
211 </widget>
212 </item>
213 <item>
214 <widget class="QComboBox" name="vsync_mode_combobox">
215 <property name="toolTip">
216 <string>FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
217FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
218Mailbox can have lower latency than FIFO and does not tear but may drop frames.
219Immediate (no synchronization) just presents whatever is available and can exhibit tearing.</string>
220 </property>
221 <property name="currentText">
222 <string/>
223 </property>
224 </widget>
225 </item>
226 </layout>
227 </widget>
228 </item>
229 <item>
192 <widget class="QWidget" name="nvdec_emulation_widget" native="true"> 230 <widget class="QWidget" name="nvdec_emulation_widget" native="true">
193 <layout class="QHBoxLayout" name="nvdec_emulation_layout"> 231 <layout class="QHBoxLayout" name="nvdec_emulation_layout">
194 <property name="leftMargin"> 232 <property name="leftMargin">
@@ -366,7 +404,7 @@
366 </item> 404 </item>
367 <item> 405 <item>
368 <property name="text"> 406 <property name="text">
369 <string>1.5X (1080p/1620p) [EXPERIMENTAL]</string> 407 <string>1.5X (1080p/1620p) [EXPERIMENTAL]</string>
370 </property> 408 </property>
371 </item> 409 </item>
372 <item> 410 <item>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 59fb1b334..627ed8b17 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -21,18 +21,19 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
21 21
22void ConfigureGraphicsAdvanced::SetConfiguration() { 22void ConfigureGraphicsAdvanced::SetConfiguration() {
23 const bool runtime_lock = !system.IsPoweredOn(); 23 const bool runtime_lock = !system.IsPoweredOn();
24 ui->use_vsync->setEnabled(runtime_lock); 24 ui->use_reactive_flushing->setEnabled(runtime_lock);
25 ui->async_present->setEnabled(runtime_lock);
25 ui->renderer_force_max_clock->setEnabled(runtime_lock); 26 ui->renderer_force_max_clock->setEnabled(runtime_lock);
26 ui->async_astc->setEnabled(runtime_lock); 27 ui->async_astc->setEnabled(runtime_lock);
27 ui->use_asynchronous_shaders->setEnabled(runtime_lock); 28 ui->use_asynchronous_shaders->setEnabled(runtime_lock);
28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 29 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
29 30
31 ui->async_present->setChecked(Settings::values.async_presentation.GetValue());
30 ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue()); 32 ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
31 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 33 ui->use_reactive_flushing->setChecked(Settings::values.use_reactive_flushing.GetValue());
32 ui->async_astc->setChecked(Settings::values.async_astc.GetValue()); 34 ui->async_astc->setChecked(Settings::values.async_astc.GetValue());
33 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 35 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
34 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 36 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
35 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue());
36 ui->use_vulkan_driver_pipeline_cache->setChecked( 37 ui->use_vulkan_driver_pipeline_cache->setChecked(
37 Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); 38 Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
38 39
@@ -54,12 +55,15 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
54 55
55void ConfigureGraphicsAdvanced::ApplyConfiguration() { 56void ConfigureGraphicsAdvanced::ApplyConfiguration() {
56 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); 57 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy);
58 ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation,
59 ui->async_present, async_present);
57 ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock, 60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock,
58 ui->renderer_force_max_clock, 61 ui->renderer_force_max_clock,
59 renderer_force_max_clock); 62 renderer_force_max_clock);
60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 63 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
61 ui->anisotropic_filtering_combobox); 64 ui->anisotropic_filtering_combobox);
62 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 65 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_reactive_flushing,
66 ui->use_reactive_flushing, use_reactive_flushing);
63 ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_astc, ui->async_astc, 67 ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_astc, ui->async_astc,
64 async_astc); 68 async_astc);
65 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, 69 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
@@ -67,8 +71,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
67 use_asynchronous_shaders); 71 use_asynchronous_shaders);
68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 72 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
69 ui->use_fast_gpu_time, use_fast_gpu_time); 73 ui->use_fast_gpu_time, use_fast_gpu_time);
70 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes,
71 ui->use_pessimistic_flushes, use_pessimistic_flushes);
72 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache, 74 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
73 ui->use_vulkan_driver_pipeline_cache, 75 ui->use_vulkan_driver_pipeline_cache,
74 use_vulkan_driver_pipeline_cache); 76 use_vulkan_driver_pipeline_cache);
@@ -90,15 +92,14 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
90 // Disable if not global (only happens during game) 92 // Disable if not global (only happens during game)
91 if (Settings::IsConfiguringGlobal()) { 93 if (Settings::IsConfiguringGlobal()) {
92 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 94 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
95 ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
93 ui->renderer_force_max_clock->setEnabled( 96 ui->renderer_force_max_clock->setEnabled(
94 Settings::values.renderer_force_max_clock.UsingGlobal()); 97 Settings::values.renderer_force_max_clock.UsingGlobal());
95 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 98 ui->use_reactive_flushing->setEnabled(Settings::values.use_reactive_flushing.UsingGlobal());
96 ui->async_astc->setEnabled(Settings::values.async_astc.UsingGlobal()); 99 ui->async_astc->setEnabled(Settings::values.async_astc.UsingGlobal());
97 ui->use_asynchronous_shaders->setEnabled( 100 ui->use_asynchronous_shaders->setEnabled(
98 Settings::values.use_asynchronous_shaders.UsingGlobal()); 101 Settings::values.use_asynchronous_shaders.UsingGlobal());
99 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 102 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
100 ui->use_pessimistic_flushes->setEnabled(
101 Settings::values.use_pessimistic_flushes.UsingGlobal());
102 ui->use_vulkan_driver_pipeline_cache->setEnabled( 103 ui->use_vulkan_driver_pipeline_cache->setEnabled(
103 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal()); 104 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
104 ui->anisotropic_filtering_combobox->setEnabled( 105 ui->anisotropic_filtering_combobox->setEnabled(
@@ -107,10 +108,13 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
107 return; 108 return;
108 } 109 }
109 110
111 ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation,
112 async_present);
110 ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock, 113 ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
111 Settings::values.renderer_force_max_clock, 114 Settings::values.renderer_force_max_clock,
112 renderer_force_max_clock); 115 renderer_force_max_clock);
113 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); 116 ConfigurationShared::SetColoredTristate(
117 ui->use_reactive_flushing, Settings::values.use_reactive_flushing, use_reactive_flushing);
114 ConfigurationShared::SetColoredTristate(ui->async_astc, Settings::values.async_astc, 118 ConfigurationShared::SetColoredTristate(ui->async_astc, Settings::values.async_astc,
115 async_astc); 119 async_astc);
116 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, 120 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
@@ -118,9 +122,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
118 use_asynchronous_shaders); 122 use_asynchronous_shaders);
119 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, 123 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
120 Settings::values.use_fast_gpu_time, use_fast_gpu_time); 124 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
121 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes,
122 Settings::values.use_pessimistic_flushes,
123 use_pessimistic_flushes);
124 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache, 125 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
125 Settings::values.use_vulkan_driver_pipeline_cache, 126 Settings::values.use_vulkan_driver_pipeline_cache,
126 use_vulkan_driver_pipeline_cache); 127 use_vulkan_driver_pipeline_cache);
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index bf1b04749..ae3c10946 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -36,12 +36,13 @@ private:
36 36
37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
38 38
39 ConfigurationShared::CheckState async_present;
39 ConfigurationShared::CheckState renderer_force_max_clock; 40 ConfigurationShared::CheckState renderer_force_max_clock;
40 ConfigurationShared::CheckState use_vsync; 41 ConfigurationShared::CheckState use_vsync;
41 ConfigurationShared::CheckState async_astc; 42 ConfigurationShared::CheckState async_astc;
43 ConfigurationShared::CheckState use_reactive_flushing;
42 ConfigurationShared::CheckState use_asynchronous_shaders; 44 ConfigurationShared::CheckState use_asynchronous_shaders;
43 ConfigurationShared::CheckState use_fast_gpu_time; 45 ConfigurationShared::CheckState use_fast_gpu_time;
44 ConfigurationShared::CheckState use_pessimistic_flushes;
45 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; 46 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
46 47
47 const Core::System& system; 48 const Core::System& system;
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index a7dbdc18c..9d8cbea09 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>404</width> 9 <width>404</width>
10 <height>321</height> 10 <height>376</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -70,22 +70,19 @@
70 </widget> 70 </widget>
71 </item> 71 </item>
72 <item> 72 <item>
73 <widget class="QCheckBox" name="renderer_force_max_clock"> 73 <widget class="QCheckBox" name="async_present">
74 <property name="toolTip">
75 <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string>
76 </property>
77 <property name="text"> 74 <property name="text">
78 <string>Force maximum clocks (Vulkan only)</string> 75 <string>Enable asynchronous presentation (Vulkan only)</string>
79 </property> 76 </property>
80 </widget> 77 </widget>
81 </item> 78 </item>
82 <item> 79 <item>
83 <widget class="QCheckBox" name="use_vsync"> 80 <widget class="QCheckBox" name="renderer_force_max_clock">
84 <property name="toolTip"> 81 <property name="toolTip">
85 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> 82 <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string>
86 </property> 83 </property>
87 <property name="text"> 84 <property name="text">
88 <string>Use VSync</string> 85 <string>Force maximum clocks (Vulkan only)</string>
89 </property> 86 </property>
90 </widget> 87 </widget>
91 </item> 88 </item>
@@ -100,39 +97,39 @@
100 </widget> 97 </widget>
101 </item> 98 </item>
102 <item> 99 <item>
103 <widget class="QCheckBox" name="use_asynchronous_shaders"> 100 <widget class="QCheckBox" name="use_reactive_flushing">
104 <property name="toolTip"> 101 <property name="toolTip">
105 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string> 102 <string>Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.</string>
106 </property> 103 </property>
107 <property name="text"> 104 <property name="text">
108 <string>Use asynchronous shader building (Hack)</string> 105 <string>Enable Reactive Flushing</string>
109 </property> 106 </property>
110 </widget> 107 </widget>
111 </item> 108 </item>
112 <item> 109 <item>
113 <widget class="QCheckBox" name="use_fast_gpu_time"> 110 <widget class="QCheckBox" name="use_asynchronous_shaders">
114 <property name="toolTip"> 111 <property name="toolTip">
115 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> 112 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
116 </property> 113 </property>
117 <property name="text"> 114 <property name="text">
118 <string>Use Fast GPU Time (Hack)</string> 115 <string>Use asynchronous shader building (Hack)</string>
119 </property> 116 </property>
120 </widget> 117 </widget>
121 </item> 118 </item>
122 <item> 119 <item>
123 <widget class="QCheckBox" name="use_pessimistic_flushes"> 120 <widget class="QCheckBox" name="use_fast_gpu_time">
124 <property name="toolTip"> 121 <property name="toolTip">
125 <string>Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance.</string> 122 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
126 </property> 123 </property>
127 <property name="text"> 124 <property name="text">
128 <string>Use pessimistic buffer flushes (Hack)</string> 125 <string>Use Fast GPU Time (Hack)</string>
129 </property> 126 </property>
130 </widget> 127 </widget>
131 </item> 128 </item>
132 <item> 129 <item>
133 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache"> 130 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache">
134 <property name="toolTip"> 131 <property name="toolTip">
135 <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string> 132 <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string>
136 </property> 133 </property>
137 <property name="text"> 134 <property name="text">
138 <string>Use Vulkan pipeline cache</string> 135 <string>Use Vulkan pipeline cache</string>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 50b62293e..2c2e7e47b 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -8,6 +8,7 @@
8#include <QInputDialog> 8#include <QInputDialog>
9#include <QMenu> 9#include <QMenu>
10#include <QMessageBox> 10#include <QMessageBox>
11#include <QMouseEvent>
11#include <QTimer> 12#include <QTimer>
12#include "common/assert.h" 13#include "common/assert.h"
13#include "common/param_package.h" 14#include "common/param_package.h"
@@ -206,7 +207,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
206 } 207 }
207 if (param.Has("axis")) { 208 if (param.Has("axis")) {
208 const QString axis = QString::fromStdString(param.Get("axis", "")); 209 const QString axis = QString::fromStdString(param.Get("axis", ""));
209 return QObject::tr("%1%2Axis %3").arg(toggle, invert, axis); 210 return QObject::tr("%1%2%3Axis %4").arg(toggle, inverted, invert, axis);
210 } 211 }
211 if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) { 212 if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
212 const QString axis_x = QString::fromStdString(param.Get("axis_x", "")); 213 const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
@@ -229,7 +230,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
229 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, button_name); 230 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, button_name);
230 } 231 }
231 if (param.Has("axis")) { 232 if (param.Has("axis")) {
232 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 233 return QObject::tr("%1%2%3Axis %4").arg(toggle, inverted, invert, button_name);
233 } 234 }
234 if (param.Has("motion")) { 235 if (param.Has("motion")) {
235 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 236 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
@@ -410,6 +411,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
410 button_map[button_id]->setText(ButtonToText(param)); 411 button_map[button_id]->setText(ButtonToText(param));
411 emulated_controller->SetButtonParam(button_id, param); 412 emulated_controller->SetButtonParam(button_id, param);
412 }); 413 });
414 context_menu.addAction(tr("Invert button"), [&] {
415 const bool invert_value = !param.Get("inverted", false);
416 param.Set("inverted", invert_value);
417 button_map[button_id]->setText(ButtonToText(param));
418 emulated_controller->SetButtonParam(button_id, param);
419 });
413 context_menu.addAction(tr("Set threshold"), [&] { 420 context_menu.addAction(tr("Set threshold"), [&] {
414 const int button_threshold = 421 const int button_threshold =
415 static_cast<int>(param.Get("threshold", 0.5f) * 100.0f); 422 static_cast<int>(param.Get("threshold", 0.5f) * 100.0f);
@@ -472,6 +479,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
472 param.Set("threshold", new_threshold / 1000.0f); 479 param.Set("threshold", new_threshold / 1000.0f);
473 emulated_controller->SetMotionParam(motion_id, param); 480 emulated_controller->SetMotionParam(motion_id, param);
474 }); 481 });
482 context_menu.addAction(tr("Calibrate sensor"), [&] {
483 emulated_controller->StartMotionCalibration();
484 });
475 } 485 }
476 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location)); 486 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
477 }); 487 });
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index c287220fc..a188eef92 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -180,6 +180,10 @@ void PlayerControlPreview::ControllerUpdate(Core::HID::ControllerTriggerType typ
180 battery_values = controller->GetBatteryValues(); 180 battery_values = controller->GetBatteryValues();
181 needs_redraw = true; 181 needs_redraw = true;
182 break; 182 break;
183 case Core::HID::ControllerTriggerType::Motion:
184 motion_values = controller->GetMotions();
185 needs_redraw = true;
186 break;
183 default: 187 default:
184 break; 188 break;
185 } 189 }
@@ -313,6 +317,15 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
313 DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0)); 317 DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
314 } 318 }
315 319
320 {
321 // Draw motion cubes
322 using namespace Settings::NativeMotion;
323 p.setPen(colors.outline);
324 p.setBrush(colors.transparent);
325 Draw3dCube(p, center + QPointF(-140, 90),
326 motion_values[Settings::NativeMotion::MotionLeft].euler, 20.0f);
327 }
328
316 using namespace Settings::NativeButton; 329 using namespace Settings::NativeButton;
317 330
318 // D-pad constants 331 // D-pad constants
@@ -435,6 +448,15 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
435 DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90)); 448 DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
436 } 449 }
437 450
451 {
452 // Draw motion cubes
453 using namespace Settings::NativeMotion;
454 p.setPen(colors.outline);
455 p.setBrush(colors.transparent);
456 Draw3dCube(p, center + QPointF(140, 90),
457 motion_values[Settings::NativeMotion::MotionRight].euler, 20.0f);
458 }
459
438 using namespace Settings::NativeButton; 460 using namespace Settings::NativeButton;
439 461
440 // Face buttons constants 462 // Face buttons constants
@@ -555,6 +577,17 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
555 DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90)); 577 DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
556 } 578 }
557 579
580 {
581 // Draw motion cubes
582 using namespace Settings::NativeMotion;
583 p.setPen(colors.outline);
584 p.setBrush(colors.transparent);
585 Draw3dCube(p, center + QPointF(-180, 90),
586 motion_values[Settings::NativeMotion::MotionLeft].euler, 20.0f);
587 Draw3dCube(p, center + QPointF(180, 90),
588 motion_values[Settings::NativeMotion::MotionRight].euler, 20.0f);
589 }
590
558 using namespace Settings::NativeButton; 591 using namespace Settings::NativeButton;
559 592
560 // Face buttons constants 593 // Face buttons constants
@@ -647,6 +680,15 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
647 DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0)); 680 DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
648 } 681 }
649 682
683 {
684 // Draw motion cubes
685 using namespace Settings::NativeMotion;
686 p.setPen(colors.outline);
687 p.setBrush(colors.transparent);
688 Draw3dCube(p, center + QPointF(0, -115),
689 motion_values[Settings::NativeMotion::MotionLeft].euler, 15.0f);
690 }
691
650 using namespace Settings::NativeButton; 692 using namespace Settings::NativeButton;
651 693
652 // Face buttons constants 694 // Face buttons constants
@@ -750,6 +792,15 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
750 DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105)); 792 DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
751 } 793 }
752 794
795 {
796 // Draw motion cubes
797 using namespace Settings::NativeMotion;
798 p.setPen(colors.button);
799 p.setBrush(colors.transparent);
800 Draw3dCube(p, center + QPointF(0, -100),
801 motion_values[Settings::NativeMotion::MotionLeft].euler, 15.0f);
802 }
803
753 using namespace Settings::NativeButton; 804 using namespace Settings::NativeButton;
754 805
755 // Face buttons constants 806 // Face buttons constants
@@ -2871,6 +2922,46 @@ void PlayerControlPreview::DrawArrow(QPainter& p, const QPointF center, const Di
2871 DrawPolygon(p, arrow_symbol); 2922 DrawPolygon(p, arrow_symbol);
2872} 2923}
2873 2924
2925// Draw motion functions
2926void PlayerControlPreview::Draw3dCube(QPainter& p, QPointF center, const Common::Vec3f& euler,
2927 float size) {
2928 std::array<Common::Vec3f, 8> cube{
2929 Common::Vec3f{-0.7f, -1, -0.5f},
2930 {-0.7f, 1, -0.5f},
2931 {0.7f, 1, -0.5f},
2932 {0.7f, -1, -0.5f},
2933 {-0.7f, -1, 0.5f},
2934 {-0.7f, 1, 0.5f},
2935 {0.7f, 1, 0.5f},
2936 {0.7f, -1, 0.5f},
2937 };
2938
2939 for (Common::Vec3f& point : cube) {
2940 point.RotateFromOrigin(euler.x, euler.y, euler.z);
2941 point *= size;
2942 }
2943
2944 const std::array<QPointF, 4> front_face{
2945 center + QPointF{cube[0].x, cube[0].y},
2946 center + QPointF{cube[1].x, cube[1].y},
2947 center + QPointF{cube[2].x, cube[2].y},
2948 center + QPointF{cube[3].x, cube[3].y},
2949 };
2950 const std::array<QPointF, 4> back_face{
2951 center + QPointF{cube[4].x, cube[4].y},
2952 center + QPointF{cube[5].x, cube[5].y},
2953 center + QPointF{cube[6].x, cube[6].y},
2954 center + QPointF{cube[7].x, cube[7].y},
2955 };
2956
2957 DrawPolygon(p, front_face);
2958 DrawPolygon(p, back_face);
2959 p.drawLine(center + QPointF{cube[0].x, cube[0].y}, center + QPointF{cube[4].x, cube[4].y});
2960 p.drawLine(center + QPointF{cube[1].x, cube[1].y}, center + QPointF{cube[5].x, cube[5].y});
2961 p.drawLine(center + QPointF{cube[2].x, cube[2].y}, center + QPointF{cube[6].x, cube[6].y});
2962 p.drawLine(center + QPointF{cube[3].x, cube[3].y}, center + QPointF{cube[7].x, cube[7].y});
2963}
2964
2874template <size_t N> 2965template <size_t N>
2875void PlayerControlPreview::DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon) { 2966void PlayerControlPreview::DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon) {
2876 p.drawPolygon(polygon.data(), static_cast<int>(polygon.size())); 2967 p.drawPolygon(polygon.data(), static_cast<int>(polygon.size()));
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index 267d134de..a16943c3c 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -9,6 +9,7 @@
9 9
10#include "common/input.h" 10#include "common/input.h"
11#include "common/settings_input.h" 11#include "common/settings_input.h"
12#include "common/vector_math.h"
12#include "core/hid/emulated_controller.h" 13#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_types.h" 14#include "core/hid/hid_types.h"
14 15
@@ -193,6 +194,9 @@ private:
193 void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size); 194 void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size);
194 void DrawArrow(QPainter& p, QPointF center, Direction direction, float size); 195 void DrawArrow(QPainter& p, QPointF center, Direction direction, float size);
195 196
197 // Draw motion functions
198 void Draw3dCube(QPainter& p, QPointF center, const Common::Vec3f& euler, float size);
199
196 // Draw primitive types 200 // Draw primitive types
197 template <size_t N> 201 template <size_t N>
198 void DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon); 202 void DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon);
@@ -222,4 +226,5 @@ private:
222 Core::HID::SticksValues stick_values{}; 226 Core::HID::SticksValues stick_values{};
223 Core::HID::TriggerValues trigger_values{}; 227 Core::HID::TriggerValues trigger_values{};
224 Core::HID::BatteryValues battery_values{}; 228 Core::HID::BatteryValues battery_values{};
229 Core::HID::MotionState motion_values{};
225}; 230};
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 6af34f793..286ccc5cd 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -111,6 +111,9 @@ void ConfigureSystem::SetConfiguration() {
111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); 111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time));
112 ui->device_name_edit->setText( 112 ui->device_name_edit->setText(
113 QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); 113 QString::fromUtf8(Settings::values.device_name.GetValue().c_str()));
114 ui->use_unsafe_extended_memory_layout->setEnabled(enabled);
115 ui->use_unsafe_extended_memory_layout->setChecked(
116 Settings::values.use_unsafe_extended_memory_layout.GetValue());
114 117
115 if (Settings::IsConfiguringGlobal()) { 118 if (Settings::IsConfiguringGlobal()) {
116 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); 119 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
@@ -160,6 +163,9 @@ void ConfigureSystem::ApplyConfiguration() {
160 ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); 163 ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region);
161 ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, 164 ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
162 ui->combo_time_zone); 165 ui->combo_time_zone);
166 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_unsafe_extended_memory_layout,
167 ui->use_unsafe_extended_memory_layout,
168 use_unsafe_extended_memory_layout);
163 169
164 if (Settings::IsConfiguringGlobal()) { 170 if (Settings::IsConfiguringGlobal()) {
165 // Guard if during game and set to game-specific value 171 // Guard if during game and set to game-specific value
@@ -215,6 +221,10 @@ void ConfigureSystem::SetupPerGameUI() {
215 Settings::values.rng_seed.GetValue().has_value(), 221 Settings::values.rng_seed.GetValue().has_value(),
216 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed); 222 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
217 223
224 ConfigurationShared::SetColoredTristate(ui->use_unsafe_extended_memory_layout,
225 Settings::values.use_unsafe_extended_memory_layout,
226 use_unsafe_extended_memory_layout);
227
218 ui->custom_rtc_checkbox->setVisible(false); 228 ui->custom_rtc_checkbox->setVisible(false);
219 ui->custom_rtc_edit->setVisible(false); 229 ui->custom_rtc_edit->setVisible(false);
220} 230}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index ec28724a1..ce1a91601 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -41,6 +41,7 @@ private:
41 bool enabled = false; 41 bool enabled = false;
42 42
43 ConfigurationShared::CheckState use_rng_seed; 43 ConfigurationShared::CheckState use_rng_seed;
44 ConfigurationShared::CheckState use_unsafe_extended_memory_layout;
44 45
45 Core::System& system; 46 Core::System& system;
46}; 47};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 9e7bc3b93..e0caecd5e 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -478,6 +478,13 @@
478 </property> 478 </property>
479 </widget> 479 </widget>
480 </item> 480 </item>
481 <item row="7" column="0">
482 <widget class="QCheckBox" name="use_unsafe_extended_memory_layout">
483 <property name="text">
484 <string>Unsafe extended memory layout (8GB DRAM)</string>
485 </property>
486 </widget>
487 </item>
481 </layout> 488 </layout>
482 </item> 489 </item>
483 </layout> 490 </layout>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b79409a68..d932e33a7 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -27,6 +27,7 @@
27#include "configuration/configure_input.h" 27#include "configuration/configure_input.h"
28#include "configuration/configure_per_game.h" 28#include "configuration/configure_per_game.h"
29#include "configuration/configure_tas.h" 29#include "configuration/configure_tas.h"
30#include "core/file_sys/romfs_factory.h"
30#include "core/file_sys/vfs.h" 31#include "core/file_sys/vfs.h"
31#include "core/file_sys/vfs_real.h" 32#include "core/file_sys/vfs_real.h"
32#include "core/frontend/applets/cabinet.h" 33#include "core/frontend/applets/cabinet.h"
@@ -570,8 +571,8 @@ void GMainWindow::RegisterMetaTypes() {
570 571
571 // Cabinet Applet 572 // Cabinet Applet
572 qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters"); 573 qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters");
573 qRegisterMetaType<std::shared_ptr<Service::NFP::NfpDevice>>( 574 qRegisterMetaType<std::shared_ptr<Service::NFC::NfcDevice>>(
574 "std::shared_ptr<Service::NFP::NfpDevice>"); 575 "std::shared_ptr<Service::NFC::NfcDevice>");
575 576
576 // Controller Applet 577 // Controller Applet
577 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); 578 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
@@ -599,7 +600,7 @@ void GMainWindow::RegisterMetaTypes() {
599} 600}
600 601
601void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, 602void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
602 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) { 603 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) {
603 cabinet_applet = 604 cabinet_applet =
604 new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device); 605 new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device);
605 SCOPE_EXIT({ 606 SCOPE_EXIT({
@@ -4171,6 +4172,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
4171 } 4172 }
4172 4173
4173 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); 4174 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
4175 bool all_keys_present{true};
4176
4174 if (keys.BaseDeriveNecessary()) { 4177 if (keys.BaseDeriveNecessary()) {
4175 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)}; 4178 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)};
4176 4179
@@ -4195,6 +4198,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
4195 errors += tr(" - Missing PRODINFO"); 4198 errors += tr(" - Missing PRODINFO");
4196 } 4199 }
4197 if (!errors.isEmpty()) { 4200 if (!errors.isEmpty()) {
4201 all_keys_present = false;
4198 QMessageBox::warning( 4202 QMessageBox::warning(
4199 this, tr("Derivation Components Missing"), 4203 this, tr("Derivation Components Missing"),
4200 tr("Encryption keys are missing. " 4204 tr("Encryption keys are missing. "
@@ -4222,11 +4226,40 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
4222 4226
4223 system->GetFileSystemController().CreateFactories(*vfs); 4227 system->GetFileSystemController().CreateFactories(*vfs);
4224 4228
4229 if (all_keys_present && !this->CheckSystemArchiveDecryption()) {
4230 LOG_WARNING(Frontend, "Mii model decryption failed");
4231 QMessageBox::warning(
4232 this, tr("System Archive Decryption Failed"),
4233 tr("Encryption keys failed to decrypt firmware. "
4234 "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
4235 "quickstart guide</a> to get all your keys, firmware and "
4236 "games."));
4237 }
4238
4225 if (behavior == ReinitializeKeyBehavior::Warning) { 4239 if (behavior == ReinitializeKeyBehavior::Warning) {
4226 game_list->PopulateAsync(UISettings::values.game_dirs); 4240 game_list->PopulateAsync(UISettings::values.game_dirs);
4227 } 4241 }
4228} 4242}
4229 4243
4244bool GMainWindow::CheckSystemArchiveDecryption() {
4245 constexpr u64 MiiModelId = 0x0100000000000802;
4246
4247 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4248 if (!bis_system) {
4249 // Not having system BIS files is not an error.
4250 return true;
4251 }
4252
4253 auto mii_nca = bis_system->GetEntry(MiiModelId, FileSys::ContentRecordType::Data);
4254 if (!mii_nca) {
4255 // Not having the Mii model is not an error.
4256 return true;
4257 }
4258
4259 // Return whether we are able to decrypt the RomFS of the Mii model.
4260 return mii_nca->GetRomFS().get() != nullptr;
4261}
4262
4230std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, 4263std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed,
4231 u64 program_id) { 4264 u64 program_id) {
4232 const auto dlc_entries = 4265 const auto dlc_entries =
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 8b5c1d747..7b23f2a59 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -93,9 +93,9 @@ enum class SwkbdReplyType : u32;
93enum class WebExitReason : u32; 93enum class WebExitReason : u32;
94} // namespace Service::AM::Applets 94} // namespace Service::AM::Applets
95 95
96namespace Service::NFP { 96namespace Service::NFC {
97class NfpDevice; 97class NfcDevice;
98} // namespace Service::NFP 98} // namespace Service::NFC
99 99
100namespace Ui { 100namespace Ui {
101class MainWindow; 101class MainWindow;
@@ -188,7 +188,7 @@ public slots:
188 void OnExit(); 188 void OnExit();
189 void OnSaveConfig(); 189 void OnSaveConfig();
190 void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, 190 void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
191 std::shared_ptr<Service::NFP::NfpDevice> nfp_device); 191 std::shared_ptr<Service::NFC::NfcDevice> nfp_device);
192 void AmiiboSettingsRequestExit(); 192 void AmiiboSettingsRequestExit();
193 void ControllerSelectorReconfigureControllers( 193 void ControllerSelectorReconfigureControllers(
194 const Core::Frontend::ControllerParameters& parameters); 194 const Core::Frontend::ControllerParameters& parameters);
@@ -392,6 +392,7 @@ private:
392 void LoadTranslation(); 392 void LoadTranslation();
393 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 393 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
394 bool CheckDarkMode(); 394 bool CheckDarkMode();
395 bool CheckSystemArchiveDecryption();
395 396
396 QString GetTasStateDescription() const; 397 QString GetTasStateDescription() const;
397 bool CreateShortcut(const std::string& shortcut_path, const std::string& title, 398 bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
diff --git a/src/yuzu/qt_common.cpp b/src/yuzu/qt_common.cpp
new file mode 100644
index 000000000..5d0fd7674
--- /dev/null
+++ b/src/yuzu/qt_common.cpp
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <QGuiApplication>
5#include <QStringLiteral>
6#include <QWindow>
7#include "common/logging/log.h"
8#include "core/frontend/emu_window.h"
9#include "yuzu/qt_common.h"
10
11#if !defined(WIN32) && !defined(__APPLE__)
12#include <qpa/qplatformnativeinterface.h>
13#endif
14
15namespace QtCommon {
16Core::Frontend::WindowSystemType GetWindowSystemType() {
17 // Determine WSI type based on Qt platform.
18 QString platform_name = QGuiApplication::platformName();
19 if (platform_name == QStringLiteral("windows"))
20 return Core::Frontend::WindowSystemType::Windows;
21 else if (platform_name == QStringLiteral("xcb"))
22 return Core::Frontend::WindowSystemType::X11;
23 else if (platform_name == QStringLiteral("wayland"))
24 return Core::Frontend::WindowSystemType::Wayland;
25 else if (platform_name == QStringLiteral("wayland-egl"))
26 return Core::Frontend::WindowSystemType::Wayland;
27 else if (platform_name == QStringLiteral("cocoa"))
28 return Core::Frontend::WindowSystemType::Cocoa;
29 else if (platform_name == QStringLiteral("android"))
30 return Core::Frontend::WindowSystemType::Android;
31
32 LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
33 return Core::Frontend::WindowSystemType::Windows;
34} // namespace Core::Frontend::WindowSystemType
35
36Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
37 Core::Frontend::EmuWindow::WindowSystemInfo wsi;
38 wsi.type = GetWindowSystemType();
39
40 // Our Win32 Qt external doesn't have the private API.
41#if defined(WIN32) || defined(__APPLE__)
42 wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
43#else
44 QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
45 wsi.display_connection = pni->nativeResourceForWindow("display", window);
46 if (wsi.type == Core::Frontend::WindowSystemType::Wayland)
47 wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
48 else
49 wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
50#endif
51 wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
52
53 return wsi;
54}
55} // namespace QtCommon
diff --git a/src/yuzu/qt_common.h b/src/yuzu/qt_common.h
new file mode 100644
index 000000000..9c63f08f3
--- /dev/null
+++ b/src/yuzu/qt_common.h
@@ -0,0 +1,15 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <QWindow>
7#include "core/frontend/emu_window.h"
8
9namespace QtCommon {
10
11Core::Frontend::WindowSystemType GetWindowSystemType();
12
13Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window);
14
15} // namespace QtCommon
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 464da3231..abe7092fc 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -4,18 +4,8 @@
4#include <memory> 4#include <memory>
5#include <optional> 5#include <optional>
6#include <sstream> 6#include <sstream>
7
8// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
9#ifdef __clang__
10#pragma clang diagnostic push
11#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
12#endif
13#include <SDL.h>
14#ifdef __clang__
15#pragma clang diagnostic pop
16#endif
17
18#include <INIReader.h> 7#include <INIReader.h>
8#include <SDL.h>
19#include "common/fs/file.h" 9#include "common/fs/file.h"
20#include "common/fs/fs.h" 10#include "common/fs/fs.h"
21#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
@@ -274,7 +264,7 @@ void Config::ReadValues() {
274 264
275 // Core 265 // Core
276 ReadSetting("Core", Settings::values.use_multi_core); 266 ReadSetting("Core", Settings::values.use_multi_core);
277 ReadSetting("Core", Settings::values.use_extended_memory_layout); 267 ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout);
278 268
279 // Cpu 269 // Cpu
280 ReadSetting("Cpu", Settings::values.cpu_accuracy); 270 ReadSetting("Cpu", Settings::values.cpu_accuracy);
@@ -300,6 +290,7 @@ void Config::ReadValues() {
300 290
301 // Renderer 291 // Renderer
302 ReadSetting("Renderer", Settings::values.renderer_backend); 292 ReadSetting("Renderer", Settings::values.renderer_backend);
293 ReadSetting("Renderer", Settings::values.async_presentation);
303 ReadSetting("Renderer", Settings::values.renderer_force_max_clock); 294 ReadSetting("Renderer", Settings::values.renderer_force_max_clock);
304 ReadSetting("Renderer", Settings::values.renderer_debug); 295 ReadSetting("Renderer", Settings::values.renderer_debug);
305 ReadSetting("Renderer", Settings::values.renderer_shader_feedback); 296 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
@@ -319,14 +310,14 @@ void Config::ReadValues() {
319 ReadSetting("Renderer", Settings::values.use_disk_shader_cache); 310 ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
320 ReadSetting("Renderer", Settings::values.gpu_accuracy); 311 ReadSetting("Renderer", Settings::values.gpu_accuracy);
321 ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation); 312 ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
322 ReadSetting("Renderer", Settings::values.use_vsync); 313 ReadSetting("Renderer", Settings::values.vsync_mode);
323 ReadSetting("Renderer", Settings::values.shader_backend); 314 ReadSetting("Renderer", Settings::values.shader_backend);
315 ReadSetting("Renderer", Settings::values.use_reactive_flushing);
324 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); 316 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
325 ReadSetting("Renderer", Settings::values.nvdec_emulation); 317 ReadSetting("Renderer", Settings::values.nvdec_emulation);
326 ReadSetting("Renderer", Settings::values.accelerate_astc); 318 ReadSetting("Renderer", Settings::values.accelerate_astc);
327 ReadSetting("Renderer", Settings::values.async_astc); 319 ReadSetting("Renderer", Settings::values.async_astc);
328 ReadSetting("Renderer", Settings::values.use_fast_gpu_time); 320 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
329 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes);
330 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); 321 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
331 322
332 ReadSetting("Renderer", Settings::values.bg_red); 323 ReadSetting("Renderer", Settings::values.bg_red);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 209cfc28a..5e7c3ac04 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -163,9 +163,9 @@ keyboard_enabled =
163# 0: Disabled, 1 (default): Enabled 163# 0: Disabled, 1 (default): Enabled
164use_multi_core = 164use_multi_core =
165 165
166# Enable extended guest system memory layout (8GB DRAM) 166# Enable unsafe extended guest system memory layout (8GB DRAM)
167# 0 (default): Disabled, 1: Enabled 167# 0 (default): Disabled, 1: Enabled
168use_extended_memory_layout = 168use_unsafe_extended_memory_layout =
169 169
170[Cpu] 170[Cpu]
171# Adjusts various optimizations. 171# Adjusts various optimizations.
@@ -264,6 +264,10 @@ cpuopt_unsafe_ignore_global_monitor =
264# 0: OpenGL, 1 (default): Vulkan 264# 0: OpenGL, 1 (default): Vulkan
265backend = 265backend =
266 266
267# Whether to enable asynchronous presentation (Vulkan only)
268# 0 (default): Off, 1: On
269async_presentation =
270
267# Enable graphics API debugging mode. 271# Enable graphics API debugging mode.
268# 0 (default): Disabled, 1: Enabled 272# 0 (default): Disabled, 1: Enabled
269debug = 273debug =
@@ -321,8 +325,14 @@ aspect_ratio =
321# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x 325# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
322max_anisotropy = 326max_anisotropy =
323 327
324# Whether to enable V-Sync (caps the framerate at 60FPS) or not. 328# Whether to enable VSync or not.
325# 0 (default): Off, 1: On 329# OpenGL: Values other than 0 enable VSync
330# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
331# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
332# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
333# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
334# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
335# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed
326use_vsync = 336use_vsync =
327 337
328# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is 338# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
@@ -330,6 +340,10 @@ use_vsync =
330# 0: GLSL, 1 (default): GLASM, 2: SPIR-V 340# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
331shader_backend = 341shader_backend =
332 342
343# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
344# 0: Off, 1 (default): On
345use_reactive_flushing =
346
333# Whether to allow asynchronous shader building. 347# Whether to allow asynchronous shader building.
334# 0 (default): Off, 1: On 348# 0 (default): Off, 1: On
335use_asynchronous_shaders = 349use_asynchronous_shaders =
@@ -370,10 +384,6 @@ use_asynchronous_gpu_emulation =
370# 0: Off, 1 (default): On 384# 0: Off, 1 (default): On
371use_fast_gpu_time = 385use_fast_gpu_time =
372 386
373# Force unmodified buffers to be flushed, which can cost performance.
374# 0: Off (default), 1: On
375use_pessimistic_flushes =
376
377# Whether to use garbage collection or not for GPU caches. 387# Whether to use garbage collection or not for GPU caches.
378# 0 (default): Off, 1: On 388# 0 (default): Off, 1: On
379use_caches_gc = 389use_caches_gc =