summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt18
-rw-r--r--src/core/file_sys/savedata_factory.cpp13
-rw-r--r--src/core/hle/kernel/svc.cpp21
-rw-r--r--src/core/hle/kernel/svc.h4
-rw-r--r--src/core/hle/kernel/vm_manager.cpp35
-rw-r--r--src/core/hle/kernel/vm_manager.h12
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp28
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h45
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h55
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h62
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h49
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h49
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp429
-rw-r--r--src/core/hle/service/hid/controllers/npad.h288
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h33
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h62
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp45
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h60
-rw-r--r--src/core/hle/service/hid/hid.cpp466
-rw-r--r--src/core/hle/service/hid/hid.h402
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.h5
-rw-r--r--src/video_core/memory_manager.cpp10
-rw-r--r--src/video_core/memory_manager.h1
-rw-r--r--src/video_core/rasterizer_cache.h127
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp28
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp328
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h84
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp76
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.ui4
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h4
44 files changed, 2278 insertions, 882 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 78986deb5..4755ec822 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -236,6 +236,24 @@ add_library(core STATIC
236 hle/service/hid/irs.h 236 hle/service/hid/irs.h
237 hle/service/hid/xcd.cpp 237 hle/service/hid/xcd.cpp
238 hle/service/hid/xcd.h 238 hle/service/hid/xcd.h
239 hle/service/hid/controllers/controller_base.cpp
240 hle/service/hid/controllers/controller_base.h
241 hle/service/hid/controllers/debug_pad.cpp
242 hle/service/hid/controllers/debug_pad.h
243 hle/service/hid/controllers/gesture.cpp
244 hle/service/hid/controllers/gesture.h
245 hle/service/hid/controllers/keyboard.cpp
246 hle/service/hid/controllers/keyboard.h
247 hle/service/hid/controllers/mouse.cpp
248 hle/service/hid/controllers/mouse.h
249 hle/service/hid/controllers/npad.cpp
250 hle/service/hid/controllers/npad.h
251 hle/service/hid/controllers/stubbed.cpp
252 hle/service/hid/controllers/stubbed.h
253 hle/service/hid/controllers/touchscreen.cpp
254 hle/service/hid/controllers/touchscreen.h
255 hle/service/hid/controllers/xpad.cpp
256 hle/service/hid/controllers/xpad.h
239 hle/service/lbl/lbl.cpp 257 hle/service/lbl/lbl.cpp
240 hle/service/lbl/lbl.h 258 hle/service/lbl/lbl.h
241 hle/service/ldn/ldn.cpp 259 hle/service/ldn/ldn.cpp
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 47f2ab9e0..ef1aaebbb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
51 meta.title_id); 51 meta.title_id);
52 } 52 }
53 53
54 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
55 LOG_WARNING(Service_FS,
56 "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
57 "non-zero ({:016X}{:016X})",
58 meta.user_id[1], meta.user_id[0]);
59 }
60
54 std::string save_directory = 61 std::string save_directory =
55 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 62 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
56 63
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
92 case SaveDataSpaceId::NandUser: 99 case SaveDataSpaceId::NandUser:
93 out = "/user/"; 100 out = "/user/";
94 break; 101 break;
102 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/";
104 break;
95 default: 105 default:
96 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
97 } 107 }
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
100 case SaveDataType::SystemSaveData: 110 case SaveDataType::SystemSaveData:
101 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); 111 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
102 case SaveDataType::SaveData: 112 case SaveDataType::SaveData:
113 case SaveDataType::DeviceSaveData:
103 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 114 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
104 title_id); 115 title_id);
105 case SaveDataType::TemporaryStorage: 116 case SaveDataType::TemporaryStorage:
106 return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 117 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
107 title_id); 118 title_id);
108 default: 119 default:
109 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 120 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 3e5f11f2b..d08b84bde 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -448,25 +448,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
448 case GetInfoType::RandomEntropy: 448 case GetInfoType::RandomEntropy:
449 *result = 0; 449 *result = 0;
450 break; 450 break;
451 case GetInfoType::AddressSpaceBaseAddr: 451 case GetInfoType::ASLRRegionBaseAddr:
452 *result = vm_manager.GetCodeRegionBaseAddress(); 452 *result = vm_manager.GetASLRRegionBaseAddress();
453 break; 453 break;
454 case GetInfoType::AddressSpaceSize: { 454 case GetInfoType::ASLRRegionSize:
455 const u64 width = vm_manager.GetAddressSpaceWidth(); 455 *result = vm_manager.GetASLRRegionSize();
456
457 switch (width) {
458 case 32:
459 *result = 0xFFE00000;
460 break;
461 case 36:
462 *result = 0xFF8000000;
463 break;
464 case 39:
465 *result = 0x7FF8000000;
466 break;
467 }
468 break; 456 break;
469 }
470 case GetInfoType::NewMapRegionBaseAddr: 457 case GetInfoType::NewMapRegionBaseAddr:
471 *result = vm_manager.GetNewMapRegionBaseAddress(); 458 *result = vm_manager.GetNewMapRegionBaseAddress();
472 break; 459 break;
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 70148c4fe..554a5e328 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -41,8 +41,8 @@ enum class GetInfoType : u64 {
41 RandomEntropy = 11, 41 RandomEntropy = 11,
42 PerformanceCounter = 0xF0000002, 42 PerformanceCounter = 0xF0000002,
43 // 2.0.0+ 43 // 2.0.0+
44 AddressSpaceBaseAddr = 12, 44 ASLRRegionBaseAddr = 12,
45 AddressSpaceSize = 13, 45 ASLRRegionSize = 13,
46 NewMapRegionBaseAddr = 14, 46 NewMapRegionBaseAddr = 14,
47 NewMapRegionSize = 15, 47 NewMapRegionSize = 15,
48 // 3.0.0+ 48 // 3.0.0+
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..1e28ccbda 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -393,30 +393,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
393 393
394 switch (type) { 394 switch (type) {
395 case FileSys::ProgramAddressSpaceType::Is32Bit: 395 case FileSys::ProgramAddressSpaceType::Is32Bit:
396 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
396 address_space_width = 32; 397 address_space_width = 32;
397 code_region_base = 0x200000; 398 code_region_base = 0x200000;
398 code_region_end = code_region_base + 0x3FE00000; 399 code_region_end = code_region_base + 0x3FE00000;
399 map_region_size = 0x40000000; 400 aslr_region_base = 0x200000;
400 heap_region_size = 0x40000000; 401 aslr_region_end = aslr_region_base + 0xFFE00000;
402 if (type == FileSys::ProgramAddressSpaceType::Is32Bit) {
403 map_region_size = 0x40000000;
404 heap_region_size = 0x40000000;
405 } else {
406 map_region_size = 0;
407 heap_region_size = 0x80000000;
408 }
401 break; 409 break;
402 case FileSys::ProgramAddressSpaceType::Is36Bit: 410 case FileSys::ProgramAddressSpaceType::Is36Bit:
403 address_space_width = 36; 411 address_space_width = 36;
404 code_region_base = 0x8000000; 412 code_region_base = 0x8000000;
405 code_region_end = code_region_base + 0x78000000; 413 code_region_end = code_region_base + 0x78000000;
414 aslr_region_base = 0x8000000;
415 aslr_region_end = aslr_region_base + 0xFF8000000;
406 map_region_size = 0x180000000; 416 map_region_size = 0x180000000;
407 heap_region_size = 0x180000000; 417 heap_region_size = 0x180000000;
408 break; 418 break;
409 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
410 address_space_width = 32;
411 code_region_base = 0x200000;
412 code_region_end = code_region_base + 0x3FE00000;
413 map_region_size = 0;
414 heap_region_size = 0x80000000;
415 break;
416 case FileSys::ProgramAddressSpaceType::Is39Bit: 419 case FileSys::ProgramAddressSpaceType::Is39Bit:
417 address_space_width = 39; 420 address_space_width = 39;
418 code_region_base = 0x8000000; 421 code_region_base = 0x8000000;
419 code_region_end = code_region_base + 0x80000000; 422 code_region_end = code_region_base + 0x80000000;
423 aslr_region_base = 0x8000000;
424 aslr_region_end = aslr_region_base + 0x7FF8000000;
420 map_region_size = 0x1000000000; 425 map_region_size = 0x1000000000;
421 heap_region_size = 0x180000000; 426 heap_region_size = 0x180000000;
422 new_map_region_size = 0x80000000; 427 new_map_region_size = 0x80000000;
@@ -490,6 +495,18 @@ u64 VMManager::GetAddressSpaceWidth() const {
490 return address_space_width; 495 return address_space_width;
491} 496}
492 497
498VAddr VMManager::GetASLRRegionBaseAddress() const {
499 return aslr_region_base;
500}
501
502VAddr VMManager::GetASLRRegionEndAddress() const {
503 return aslr_region_end;
504}
505
506u64 VMManager::GetASLRRegionSize() const {
507 return aslr_region_end - aslr_region_base;
508}
509
493VAddr VMManager::GetCodeRegionBaseAddress() const { 510VAddr VMManager::GetCodeRegionBaseAddress() const {
494 return code_region_base; 511 return code_region_base;
495} 512}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 015559a64..4accde6b3 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -205,6 +205,15 @@ public:
205 /// Gets the address space width in bits. 205 /// Gets the address space width in bits.
206 u64 GetAddressSpaceWidth() const; 206 u64 GetAddressSpaceWidth() const;
207 207
208 /// Gets the base address of the ASLR region.
209 VAddr GetASLRRegionBaseAddress() const;
210
211 /// Gets the end address of the ASLR region.
212 VAddr GetASLRRegionEndAddress() const;
213
214 /// Gets the size of the ASLR region
215 u64 GetASLRRegionSize() const;
216
208 /// Gets the base address of the code region. 217 /// Gets the base address of the code region.
209 VAddr GetCodeRegionBaseAddress() const; 218 VAddr GetCodeRegionBaseAddress() const;
210 219
@@ -306,6 +315,9 @@ private:
306 VAddr address_space_base = 0; 315 VAddr address_space_base = 0;
307 VAddr address_space_end = 0; 316 VAddr address_space_end = 0;
308 317
318 VAddr aslr_region_base = 0;
319 VAddr aslr_region_end = 0;
320
309 VAddr code_region_base = 0; 321 VAddr code_region_base = 0;
310 VAddr code_region_end = 0; 322 VAddr code_region_end = 0;
311 323
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
new file mode 100644
index 000000000..1625e9c3d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -0,0 +1,28 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/hid/controllers/controller_base.h"
6
7namespace Service::HID {
8ControllerBase::~ControllerBase() = default;
9
10void ControllerBase::ActivateController() {
11 if (is_activated) {
12 OnRelease();
13 }
14 is_activated = true;
15 OnInit();
16}
17
18void ControllerBase::DeactivateController() {
19 if (is_activated) {
20 OnRelease();
21 }
22 is_activated = false;
23}
24
25bool ControllerBase::IsControllerActivated() const {
26 return is_activated;
27}
28} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
new file mode 100644
index 000000000..fa98e2354
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/swap.h"
9
10namespace Service::HID {
11class ControllerBase {
12public:
13 ControllerBase() = default;
14 virtual ~ControllerBase() = 0;
15
16 // Called when the controller is initialized
17 virtual void OnInit() = 0;
18
19 // When the controller is released
20 virtual void OnRelease() = 0;
21
22 // When the controller is requesting an update for the shared memory
23 virtual void OnUpdate(u8* data, std::size_t size) = 0;
24
25 // Called when input devices should be loaded
26 virtual void OnLoadInputDevices() = 0;
27
28 void ActivateController();
29
30 void DeactivateController();
31
32 bool IsControllerActivated() const;
33
34protected:
35 bool is_activated{false};
36
37 struct CommonHeader {
38 s64_le timestamp;
39 s64_le total_entry_count;
40 s64_le last_entry_index;
41 s64_le entry_count;
42 };
43 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
44};
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
new file mode 100644
index 000000000..6f8ef6e3f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -0,0 +1,42 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/debug_pad.h"
10
11namespace Service::HID {
12
13Controller_DebugPad::Controller_DebugPad() = default;
14
15void Controller_DebugPad::OnInit() {}
16
17void Controller_DebugPad::OnRelease() {}
18
19void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
20 shared_memory.header.timestamp = CoreTiming::GetTicks();
21 shared_memory.header.total_entry_count = 17;
22
23 if (!IsControllerActivated()) {
24 shared_memory.header.entry_count = 0;
25 shared_memory.header.last_entry_index = 0;
26 return;
27 }
28 shared_memory.header.entry_count = 16;
29
30 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
31 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
32 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
33
34 cur_entry.sampling_number = last_entry.sampling_number + 1;
35 cur_entry.sampling_number2 = cur_entry.sampling_number;
36 // TODO(ogniK): Update debug pad states
37
38 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
39}
40
41void Controller_DebugPad::OnLoadInputDevices() {}
42} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
new file mode 100644
index 000000000..e35675fa1
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -0,0 +1,55 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase {
15public:
16 Controller_DebugPad();
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct AnalogStick {
32 s32_le x;
33 s32_le y;
34 };
35 static_assert(sizeof(AnalogStick) == 0x8);
36
37 struct PadStates {
38 s64_le sampling_number;
39 s64_le sampling_number2;
40 u32_le attribute;
41 u32_le button_state;
42 AnalogStick r_stick;
43 AnalogStick l_stick;
44 };
45 static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
46
47 struct SharedMemory {
48 CommonHeader header;
49 std::array<PadStates, 17> pad_states;
50 INSERT_PADDING_BYTES(0x138);
51 };
52 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
53 SharedMemory shared_memory{};
54};
55} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
new file mode 100644
index 000000000..b473b9e2b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/gesture.h"
10
11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
13
14Controller_Gesture::Controller_Gesture() = default;
15
16void Controller_Gesture::OnInit() {}
17
18void Controller_Gesture::OnRelease() {}
19
20void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update gesture states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Gesture::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
new file mode 100644
index 000000000..0ced50dfd
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -0,0 +1,62 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Gesture final : public ControllerBase {
14public:
15 Controller_Gesture();
16
17 // Called when the controller is initialized
18 void OnInit() override;
19
20 // When the controller is released
21 void OnRelease() override;
22
23 // When the controller is requesting an update for the shared memory
24 void OnUpdate(u8* data, size_t size) override;
25
26 // Called when input devices should be loaded
27 void OnLoadInputDevices() override;
28
29private:
30 struct Locations {
31 s32_le x;
32 s32_le y;
33 };
34
35 struct GestureState {
36 s64_le sampling_number;
37 s64_le sampling_number2;
38
39 s64_le detection_count;
40 s32_le type;
41 s32_le dir;
42 s32_le x;
43 s32_le y;
44 s32_le delta_x;
45 s32_le delta_y;
46 f32 vel_x;
47 f32 vel_y;
48 s32_le attributes;
49 f32 scale;
50 f32 rotation;
51 s32_le location_count;
52 std::array<Locations, 4> locations;
53 };
54 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
55
56 struct SharedMemory {
57 CommonHeader header;
58 std::array<GestureState, 17> gesture_states;
59 };
60 SharedMemory shared_memory{};
61};
62} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
new file mode 100644
index 000000000..089c02ac4
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/keyboard.h"
10
11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13
14Controller_Keyboard::Controller_Keyboard() = default;
15
16void Controller_Keyboard::OnInit() {}
17
18void Controller_Keyboard::OnRelease() {}
19
20void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update keyboard states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Keyboard::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
new file mode 100644
index 000000000..778e14f7e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -0,0 +1,49 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase {
15public:
16 Controller_Keyboard();
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct KeyboardState {
32 s64_le sampling_number;
33 s64_le sampling_number2;
34
35 s32_le modifier;
36 s32_le attribute;
37 std::array<u8, 32> key;
38 };
39 static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
40
41 struct SharedMemory {
42 CommonHeader header;
43 std::array<KeyboardState, 17> pad_states;
44 INSERT_PADDING_BYTES(0x28);
45 };
46 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
47 SharedMemory shared_memory{};
48};
49} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
new file mode 100644
index 000000000..78e9b5e9e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/mouse.h"
10
11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13
14Controller_Mouse::Controller_Mouse() = default;
15
16void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {}
19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update mouse states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Mouse::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
new file mode 100644
index 000000000..05358a4f5
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -0,0 +1,49 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Mouse final : public ControllerBase {
14public:
15 Controller_Mouse();
16
17 // Called when the controller is initialized
18 void OnInit() override;
19
20 // When the controller is released
21 void OnRelease() override;
22
23 // When the controller is requesting an update for the shared memory
24 void OnUpdate(u8* data, std::size_t size) override;
25
26 // Called when input devices should be loaded
27 void OnLoadInputDevices() override;
28
29private:
30 struct MouseState {
31 s64_le sampling_number;
32 s64_le sampling_number2;
33 s32_le x;
34 s32_le y;
35 s32_le delta_x;
36 s32_le delta_y;
37 s32_le mouse_wheel;
38 s32_le button;
39 s32_le attribute;
40 };
41 static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
42
43 struct SharedMemory {
44 CommonHeader header;
45 std::array<MouseState, 17> mouse_states;
46 };
47 SharedMemory shared_memory{};
48};
49} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
new file mode 100644
index 000000000..d17e64b2a
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -0,0 +1,429 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9#include <cstring>
10#include "common/assert.h"
11#include "common/bit_field.h"
12#include "common/common_types.h"
13#include "common/logging/log.h"
14#include "common/swap.h"
15#include "core/core.h"
16#include "core/core_timing.h"
17#include "core/frontend/input.h"
18#include "core/hle/kernel/event.h"
19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/settings.h"
21
22namespace Service::HID {
23constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
24constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
25constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
26constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
27constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
28constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
29constexpr std::size_t NPAD_OFFSET = 0x9A00;
30constexpr u32 BATTERY_FULL = 2;
31enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
32
33Controller_NPad::Controller_NPad() = default;
34
35void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
36 const auto controller_type = connected_controllers[controller_idx].type;
37 auto& controller = shared_memory_entries[controller_idx];
38 if (controller_type == NPadControllerType::None) {
39 return;
40 }
41 controller.joy_styles.raw = 0; // Zero out
42 controller.device_type.raw = 0;
43 switch (controller_type) {
44 case NPadControllerType::Handheld:
45 controller.joy_styles.handheld.Assign(1);
46 controller.device_type.handheld.Assign(1);
47 controller.pad_assignment = NPadAssignments::Dual;
48 break;
49 case NPadControllerType::JoyDual:
50 controller.joy_styles.joycon_dual.Assign(1);
51 controller.device_type.joycon_left.Assign(1);
52 controller.device_type.joycon_right.Assign(1);
53 controller.pad_assignment = NPadAssignments::Dual;
54 break;
55 case NPadControllerType::JoyLeft:
56 controller.joy_styles.joycon_left.Assign(1);
57 controller.device_type.joycon_left.Assign(1);
58 controller.pad_assignment = NPadAssignments::Dual;
59 break;
60 case NPadControllerType::JoyRight:
61 controller.joy_styles.joycon_right.Assign(1);
62 controller.device_type.joycon_right.Assign(1);
63 controller.pad_assignment = NPadAssignments::Dual;
64 break;
65 case NPadControllerType::Pokeball:
66 controller.joy_styles.pokeball.Assign(1);
67 controller.device_type.pokeball.Assign(1);
68 controller.pad_assignment = NPadAssignments::Single;
69 break;
70 case NPadControllerType::ProController:
71 controller.joy_styles.pro_controller.Assign(1);
72 controller.device_type.pro_controller.Assign(1);
73 controller.pad_assignment = NPadAssignments::Single;
74 break;
75 }
76
77 controller.single_color_error = ColorReadError::ReadOk;
78 controller.single_color.body_color = 0;
79 controller.single_color.button_color = 0;
80
81 controller.dual_color_error = ColorReadError::ReadOk;
82 controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
83 controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
84 controller.right_color.body_color = JOYCON_BODY_NEON_RED;
85 controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
86
87 controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
88 controller.properties.use_plus.Assign(1);
89 controller.properties.use_minus.Assign(1);
90 controller.battery_level[0] = BATTERY_FULL;
91 controller.battery_level[1] = BATTERY_FULL;
92 controller.battery_level[2] = BATTERY_FULL;
93}
94
95void Controller_NPad::OnInit() {
96 auto& kernel = Core::System::GetInstance().Kernel();
97 styleset_changed_event =
98 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
99
100 if (!IsControllerActivated())
101 return;
102 std::size_t controller{};
103 if (style.raw == 0) {
104 // We want to support all controllers
105 style.handheld.Assign(1);
106 style.joycon_left.Assign(1);
107 style.joycon_right.Assign(1);
108 style.joycon_dual.Assign(1);
109 style.pro_controller.Assign(1);
110 style.pokeball.Assign(1);
111 }
112 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
113 [](const ControllerHolder& controller) { return controller.is_connected; })) {
114 supported_npad_id_types.resize(npad_id_list.size());
115 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
116 npad_id_list.size() * sizeof(u32));
117 AddNewController(NPadControllerType::JoyDual);
118 }
119}
120
121void Controller_NPad::OnLoadInputDevices() {
122 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
123 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
124 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
125 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
126 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
127 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
128}
129
130void Controller_NPad::OnRelease() {}
131
132void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
133 if (!IsControllerActivated())
134 return;
135 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
136 auto& npad = shared_memory_entries[i];
137 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
138 &npad.handheld_states,
139 &npad.dual_states,
140 &npad.left_joy_states,
141 &npad.right_joy_states,
142 &npad.pokeball_states,
143 &npad.libnx};
144
145 for (auto* main_controller : controller_npads) {
146 main_controller->common.entry_count = 16;
147 main_controller->common.total_entry_count = 17;
148
149 const auto& last_entry =
150 main_controller->npad[main_controller->common.last_entry_index];
151
152 main_controller->common.timestamp = CoreTiming::GetTicks();
153 main_controller->common.last_entry_index =
154 (main_controller->common.last_entry_index + 1) % 17;
155
156 auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
157
158 cur_entry.timestamp = last_entry.timestamp + 1;
159 cur_entry.timestamp2 = cur_entry.timestamp;
160 }
161
162 const auto& controller_type = connected_controllers[i].type;
163
164 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
165 continue;
166 }
167
168 // Pad states
169 ControllerPadState pad_state{};
170 using namespace Settings::NativeButton;
171 pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
172 pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
173 pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
174 pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
175 pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
176 pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
177 pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
178 pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
179 pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
180 pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
181 pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
182 pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
183
184 pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
185 pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
186 pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
187 pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
188
189 pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
190 pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
191 pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
192 pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
193
194 pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
195 pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
196 pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
197 pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
198
199 pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
200 pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
201
202 AnalogPosition lstick_entry{};
203 AnalogPosition rstick_entry{};
204
205 const auto [stick_l_x_f, stick_l_y_f] =
206 sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
207 const auto [stick_r_x_f, stick_r_y_f] =
208 sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
209 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
210 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
211 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
212 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
213
214 auto& main_controller =
215 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
216 auto& handheld_entry =
217 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
218 auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
219 auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
220 auto& right_entry =
221 npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
222 auto& pokeball_entry =
223 npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
224 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
225
226 if (hold_type == NpadHoldType::Horizontal) {
227 // TODO(ogniK): Remap buttons for different orientations
228 }
229 libnx_entry.connection_status.raw = 0;
230
231 switch (controller_type) {
232 case NPadControllerType::Handheld:
233 handheld_entry.connection_status.raw = 0;
234 handheld_entry.connection_status.IsConnected.Assign(1);
235 if (!Settings::values.use_docked_mode) {
236 handheld_entry.connection_status.IsWired.Assign(1);
237 }
238 handheld_entry.pad_states.raw = pad_state.raw;
239 handheld_entry.l_stick = lstick_entry;
240 handheld_entry.r_stick = rstick_entry;
241 break;
242 case NPadControllerType::JoyDual:
243 dual_entry.connection_status.raw = 0;
244
245 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
246 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
247 dual_entry.connection_status.IsConnected.Assign(1);
248
249 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
250 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
251 libnx_entry.connection_status.IsConnected.Assign(1);
252
253 dual_entry.pad_states.raw = pad_state.raw;
254 dual_entry.l_stick = lstick_entry;
255 dual_entry.r_stick = rstick_entry;
256 case NPadControllerType::JoyLeft:
257 left_entry.connection_status.raw = 0;
258
259 left_entry.connection_status.IsConnected.Assign(1);
260 left_entry.pad_states.raw = pad_state.raw;
261 left_entry.l_stick = lstick_entry;
262 left_entry.r_stick = rstick_entry;
263 break;
264 case NPadControllerType::JoyRight:
265 right_entry.connection_status.raw = 0;
266
267 right_entry.connection_status.IsConnected.Assign(1);
268 right_entry.pad_states.raw = pad_state.raw;
269 right_entry.l_stick = lstick_entry;
270 right_entry.r_stick = rstick_entry;
271 break;
272 case NPadControllerType::Pokeball:
273 pokeball_entry.connection_status.raw = 0;
274
275 pokeball_entry.connection_status.IsConnected.Assign(1);
276 pokeball_entry.connection_status.IsWired.Assign(1);
277
278 pokeball_entry.pad_states.raw = pad_state.raw;
279 pokeball_entry.l_stick = lstick_entry;
280 pokeball_entry.r_stick = rstick_entry;
281 break;
282 case NPadControllerType::ProController:
283 main_controller.connection_status.raw = 0;
284
285 main_controller.connection_status.IsConnected.Assign(1);
286 main_controller.connection_status.IsWired.Assign(1);
287 main_controller.pad_states.raw = pad_state.raw;
288 main_controller.l_stick = lstick_entry;
289 main_controller.r_stick = rstick_entry;
290 break;
291 }
292
293 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
294 // any controllers.
295 libnx_entry.pad_states.raw = pad_state.raw;
296 libnx_entry.l_stick = lstick_entry;
297 libnx_entry.r_stick = rstick_entry;
298 }
299 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
300 shared_memory_entries.size() * sizeof(NPadEntry));
301} // namespace Service::HID
302
303void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
304 style.raw = style_set.raw;
305}
306
307Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
308 return style;
309}
310
311void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
312 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
313 supported_npad_id_types.clear();
314 supported_npad_id_types.resize(length / sizeof(u32));
315 std::memcpy(supported_npad_id_types.data(), data, length);
316}
317
318const void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
319 ASSERT(max_length < supported_npad_id_types.size());
320 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
321}
322
323std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
324 return supported_npad_id_types.size();
325}
326
327void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
328 hold_type = joy_hold_type;
329}
330Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
331 return hold_type;
332}
333
334void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
335 ASSERT(npad_id < shared_memory_entries.size());
336 shared_memory_entries[npad_id].pad_assignment = assignment_mode;
337}
338
339void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
340 const std::vector<Vibration>& vibrations) {
341 if (!can_controllers_vibrate) {
342 return;
343 }
344 for (std::size_t i = 0; i < controller_ids.size(); i++) {
345 std::size_t controller_pos = i;
346 // Handheld controller conversion
347 if (controller_pos == 32) {
348 controller_pos = 8;
349 }
350 // Unknown controller conversion
351 if (controller_pos == 16) {
352 controller_pos = 9;
353 }
354 if (connected_controllers[controller_pos].is_connected) {
355 // TODO(ogniK): Vibrate the physical controller
356 }
357 }
358 LOG_WARNING(Service_HID, "(STUBBED) called");
359 last_processed_vibration = vibrations.back();
360}
361
362Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
363 return styleset_changed_event;
364}
365
366Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
367 return last_processed_vibration;
368}
369void Controller_NPad::AddNewController(NPadControllerType controller) {
370 if (controller == NPadControllerType::Handheld) {
371 connected_controllers[8] = {controller, true};
372 InitNewlyAddedControler(8);
373 return;
374 }
375 const auto pos =
376 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
377 [](const ControllerHolder& holder) { return !holder.is_connected; });
378 if (pos == connected_controllers.end() - 2) {
379 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
380 return;
381 }
382 const auto controller_id = std::distance(connected_controllers.begin(), pos);
383 connected_controllers[controller_id] = {controller, true};
384 InitNewlyAddedControler(controller_id);
385}
386
387void Controller_NPad::ConnectNPad(u32 npad_id) {
388 if (npad_id >= connected_controllers.size())
389 return;
390 connected_controllers[npad_id].is_connected = true;
391}
392
393void Controller_NPad::DisconnectNPad(u32 npad_id) {
394 if (npad_id >= connected_controllers.size())
395 return;
396 connected_controllers[npad_id].is_connected = false;
397}
398
399Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
400 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
401 // These are controllers without led patterns
402 return LedPattern{0, 0, 0, 0};
403 }
404 switch (npad_id) {
405 case 0:
406 return LedPattern{1, 0, 0, 0};
407 case 1:
408 return LedPattern{0, 1, 0, 0};
409 case 2:
410 return LedPattern{0, 0, 1, 0};
411 case 3:
412 return LedPattern{0, 0, 0, 1};
413 case 4:
414 return LedPattern{1, 0, 0, 1};
415 case 5:
416 return LedPattern{1, 0, 1, 0};
417 case 6:
418 return LedPattern{1, 0, 1, 1};
419 case 7:
420 return LedPattern{0, 1, 1, 0};
421 default:
422 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
423 return LedPattern{0, 0, 0, 0};
424 };
425}
426void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
427 can_controllers_vibrate = can_vibrate;
428}
429} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
new file mode 100644
index 000000000..9d07d258d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -0,0 +1,288 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/settings.h"
12
13namespace Service::HID {
14
15class Controller_NPad final : public ControllerBase {
16public:
17 Controller_NPad();
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31 struct NPadType {
32 union {
33 u32_le raw{};
34
35 BitField<0, 1, u32_le> pro_controller;
36 BitField<1, 1, u32_le> handheld;
37 BitField<2, 1, u32_le> joycon_dual;
38 BitField<3, 1, u32_le> joycon_left;
39 BitField<4, 1, u32_le> joycon_right;
40
41 BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
42 };
43 };
44 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
45
46 struct Vibration {
47 f32 amp_low;
48 f32 freq_low;
49 f32 amp_high;
50 f32 freq_high;
51 };
52 static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
53
54 enum class NpadHoldType : u64 {
55 Vertical = 0,
56 Horizontal = 1,
57 };
58
59 enum class NPadAssignments : u32_le {
60 Dual = 0,
61 Single = 1,
62 };
63
64 enum class NPadControllerType {
65 None,
66 ProController,
67 Handheld,
68 JoyDual,
69 JoyLeft,
70 JoyRight,
71 Pokeball,
72 };
73
74 struct LedPattern {
75 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
76 position1.Assign(light1);
77 position1.Assign(light2);
78 position1.Assign(light3);
79 position1.Assign(light4);
80 };
81 union {
82 u64 raw{};
83 BitField<0, 1, u64> position1;
84 BitField<1, 1, u64> position2;
85 BitField<2, 1, u64> position3;
86 BitField<3, 1, u64> position4;
87 };
88 };
89
90 void SetSupportedStyleSet(NPadType style_set);
91 NPadType GetSupportedStyleSet() const;
92
93 void SetSupportedNPadIdTypes(u8* data, std::size_t length);
94 const void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
95 std::size_t GetSupportedNPadIdTypesSize() const;
96
97 void SetHoldType(NpadHoldType joy_hold_type);
98 NpadHoldType GetHoldType() const;
99
100 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
101
102 void VibrateController(const std::vector<u32>& controller_ids,
103 const std::vector<Vibration>& vibrations);
104
105 Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
106 Vibration GetLastVibration() const;
107
108 void AddNewController(NPadControllerType controller);
109
110 void ConnectNPad(u32 npad_id);
111 void DisconnectNPad(u32 npad_id);
112 LedPattern GetLedPattern(u32 npad_id);
113 void SetVibrationEnabled(bool can_vibrate);
114
115private:
116 struct CommonHeader {
117 s64_le timestamp;
118 s64_le total_entry_count;
119 s64_le last_entry_index;
120 s64_le entry_count;
121 };
122 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
123
124 struct ControllerColor {
125 u32_le body_color;
126 u32_le button_color;
127 };
128 static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
129
130 struct ControllerPadState {
131 union {
132 u64_le raw{};
133 // Button states
134 BitField<0, 1, u64_le> a;
135 BitField<1, 1, u64_le> b;
136 BitField<2, 1, u64_le> x;
137 BitField<3, 1, u64_le> y;
138 BitField<4, 1, u64_le> l_stick;
139 BitField<5, 1, u64_le> r_stick;
140 BitField<6, 1, u64_le> l;
141 BitField<7, 1, u64_le> r;
142 BitField<8, 1, u64_le> zl;
143 BitField<9, 1, u64_le> zr;
144 BitField<10, 1, u64_le> plus;
145 BitField<11, 1, u64_le> minus;
146
147 // D-Pad
148 BitField<12, 1, u64_le> d_left;
149 BitField<13, 1, u64_le> d_up;
150 BitField<14, 1, u64_le> d_right;
151 BitField<15, 1, u64_le> d_down;
152
153 // Left JoyStick
154 BitField<16, 1, u64_le> l_stick_left;
155 BitField<17, 1, u64_le> l_stick_up;
156 BitField<18, 1, u64_le> l_stick_right;
157 BitField<19, 1, u64_le> l_stick_down;
158
159 // Right JoyStick
160 BitField<20, 1, u64_le> r_stick_left;
161 BitField<21, 1, u64_le> r_stick_up;
162 BitField<22, 1, u64_le> r_stick_right;
163 BitField<23, 1, u64_le> r_stick_down;
164
165 // Not always active?
166 BitField<24, 1, u64_le> sl;
167 BitField<25, 1, u64_le> sr;
168 };
169 };
170 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
171
172 struct AnalogPosition {
173 s32_le x;
174 s32_le y;
175 };
176 static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
177
178 struct ConnectionState {
179 union {
180 u32_le raw{};
181 BitField<0, 1, u32_le> IsConnected;
182 BitField<1, 1, u32_le> IsWired;
183 BitField<2, 1, u32_le> IsLeftJoyConnected;
184 BitField<3, 1, u32_le> IsLeftJoyWired;
185 BitField<4, 1, u32_le> IsRightJoyConnected;
186 BitField<5, 1, u32_le> IsRightJoyWired;
187 };
188 };
189 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
190
191 struct GenericStates {
192 s64_le timestamp;
193 s64_le timestamp2;
194 ControllerPadState pad_states;
195 AnalogPosition l_stick;
196 AnalogPosition r_stick;
197 ConnectionState connection_status;
198 };
199 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
200
201 struct NPadGeneric {
202 CommonHeader common;
203 std::array<GenericStates, 17> npad;
204 };
205 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
206
207 enum class ColorReadError : u32_le {
208 ReadOk = 0,
209 ColorDoesntExist = 1,
210 NoController = 2,
211 };
212
213 struct NPadProperties {
214 union {
215 s64_le raw{};
216 BitField<11, 1, s64_le> is_vertical;
217 BitField<12, 1, s64_le> is_horizontal;
218 BitField<13, 1, s64_le> use_plus;
219 BitField<14, 1, s64_le> use_minus;
220 };
221 };
222
223 struct NPadDevice {
224 union {
225 u32_le raw{};
226 BitField<0, 1, s32_le> pro_controller;
227 BitField<1, 1, s32_le> handheld;
228 BitField<2, 1, s32_le> handheld_left;
229 BitField<3, 1, s32_le> handheld_right;
230 BitField<4, 1, s32_le> joycon_left;
231 BitField<5, 1, s32_le> joycon_right;
232 BitField<6, 1, s32_le> pokeball;
233 };
234 };
235
236 struct NPadEntry {
237 NPadType joy_styles;
238 NPadAssignments pad_assignment;
239
240 ColorReadError single_color_error;
241 ControllerColor single_color;
242
243 ColorReadError dual_color_error;
244 ControllerColor left_color;
245 ControllerColor right_color;
246
247 NPadGeneric main_controller_states;
248 NPadGeneric handheld_states;
249 NPadGeneric dual_states;
250 NPadGeneric left_joy_states;
251 NPadGeneric right_joy_states;
252 NPadGeneric pokeball_states;
253 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
254 // relying on this for the time being
255 INSERT_PADDING_BYTES(
256 0x708 *
257 6); // TODO(ogniK): SixAxis states, require more information before implementation
258 NPadDevice device_type;
259 NPadProperties properties;
260 INSERT_PADDING_WORDS(1);
261 std::array<u32, 3> battery_level;
262 INSERT_PADDING_BYTES(0x5c);
263 INSERT_PADDING_BYTES(0xdf8);
264 };
265 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
266
267 struct ControllerHolder {
268 Controller_NPad::NPadControllerType type;
269 bool is_connected;
270 };
271
272 NPadType style{};
273 std::array<NPadEntry, 10> shared_memory_entries{};
274 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
275 buttons;
276 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
277 std::vector<u32> supported_npad_id_types{};
278 NpadHoldType hold_type{NpadHoldType::Vertical};
279 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
280 std::size_t dump_idx{};
281 Vibration last_processed_vibration{};
282 static constexpr std::array<u32, 10> npad_id_list{0, 1, 2, 3, 4, 5, 6, 7, 32, 16};
283 std::array<ControllerHolder, 10> connected_controllers{};
284 bool can_controllers_vibrate{true};
285
286 void InitNewlyAddedControler(std::size_t controller_idx);
287};
288} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
new file mode 100644
index 000000000..3a13d5991
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -0,0 +1,39 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/stubbed.h"
10
11namespace Service::HID {
12
13Controller_Stubbed::Controller_Stubbed() = default;
14
15void Controller_Stubbed::OnInit() {}
16
17void Controller_Stubbed::OnRelease() {}
18
19void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
20 if (!smart_update) {
21 return;
22 }
23
24 CommonHeader header{};
25 header.timestamp = CoreTiming::GetTicks();
26 header.total_entry_count = 17;
27 header.entry_count = 0;
28 header.last_entry_index = 0;
29
30 std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
31}
32
33void Controller_Stubbed::OnLoadInputDevices() {}
34
35void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
36 common_offset = off;
37 smart_update = true;
38}
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
new file mode 100644
index 000000000..9c1b57f83
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -0,0 +1,33 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h"
9
10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase {
12public:
13 Controller_Stubbed();
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(u8* data, std::size_t size) override;
23
24 // Called when input devices should be loaded
25 void OnLoadInputDevices() override;
26
27 void SetCommonHeaderOffset(std::size_t off);
28
29private:
30 bool smart_update{};
31 std::size_t common_offset{};
32};
33} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
new file mode 100644
index 000000000..e97f84ea1
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -0,0 +1,65 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h"
9#include "core/frontend/emu_window.h"
10#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/touchscreen.h"
12#include "core/settings.h"
13
14namespace Service::HID {
15constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
16
17Controller_Touchscreen::Controller_Touchscreen() = default;
18
19void Controller_Touchscreen::OnInit() {}
20
21void Controller_Touchscreen::OnRelease() {}
22
23void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
24 shared_memory.header.timestamp = CoreTiming::GetTicks();
25 shared_memory.header.total_entry_count = 17;
26
27 if (!IsControllerActivated()) {
28 shared_memory.header.entry_count = 0;
29 shared_memory.header.last_entry_index = 0;
30 return;
31 }
32 shared_memory.header.entry_count = 16;
33
34 const auto& last_entry =
35 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
36 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
37 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
38
39 cur_entry.sampling_number = last_entry.sampling_number + 1;
40 cur_entry.sampling_number2 = cur_entry.sampling_number;
41
42 const auto [x, y, pressed] = touch_device->GetStatus();
43 auto& touch_entry = cur_entry.states[0];
44 if (pressed) {
45 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
46 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
47 touch_entry.diameter_x = 15;
48 touch_entry.diameter_y = 15;
49 touch_entry.rotation_angle = 0;
50 const u64 tick = CoreTiming::GetTicks();
51 touch_entry.delta_time = tick - last_touch;
52 last_touch = tick;
53 touch_entry.finger = 0;
54 cur_entry.entry_count = 1;
55 } else {
56 cur_entry.entry_count = 0;
57 }
58
59 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
60}
61
62void Controller_Touchscreen::OnLoadInputDevices() {
63 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
64}
65} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
new file mode 100644
index 000000000..1d97b6c2a
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -0,0 +1,62 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Touchscreen final : public ControllerBase {
15public:
16 Controller_Touchscreen();
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct TouchState {
32 u64_le delta_time;
33 u32_le attribute;
34 u32_le finger;
35 u32_le x;
36 u32_le y;
37 u32_le diameter_x;
38 u32_le diameter_y;
39 u32_le rotation_angle;
40 };
41 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
42
43 struct TouchScreenEntry {
44 s64_le sampling_number;
45 s64_le sampling_number2;
46 s32_le entry_count;
47 std::array<TouchState, 16> states;
48 };
49 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
50
51 struct TouchScreenSharedMemory {
52 CommonHeader header;
53 std::array<TouchScreenEntry, 17> shared_memory_entries{};
54 INSERT_PADDING_BYTES(0x3c8);
55 };
56 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
57 "TouchScreenSharedMemory is an invalid size");
58 TouchScreenSharedMemory shared_memory{};
59 std::unique_ptr<Input::TouchDevice> touch_device;
60 s64_le last_touch{};
61};
62} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
new file mode 100644
index 000000000..df0b48451
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/xpad.h"
10
11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
13
14Controller_XPad::Controller_XPad() = default;
15
16void Controller_XPad::OnInit() {}
17
18void Controller_XPad::OnRelease() {}
19
20void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
21 for (auto& xpad_entry : shared_memory.shared_memory_entries) {
22 xpad_entry.header.timestamp = CoreTiming::GetTicks();
23 xpad_entry.header.total_entry_count = 17;
24
25 if (!IsControllerActivated()) {
26 xpad_entry.header.entry_count = 0;
27 xpad_entry.header.last_entry_index = 0;
28 return;
29 }
30 xpad_entry.header.entry_count = 16;
31
32 const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
33 xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
34 auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
35
36 cur_entry.sampling_number = last_entry.sampling_number + 1;
37 cur_entry.sampling_number2 = cur_entry.sampling_number;
38 }
39 // TODO(ogniK): Update xpad states
40
41 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
42}
43
44void Controller_XPad::OnLoadInputDevices() {}
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
new file mode 100644
index 000000000..e2007183d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -0,0 +1,60 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_XPad final : public ControllerBase {
15public:
16 Controller_XPad();
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct AnalogStick {
32 s32_le x;
33 s32_le y;
34 };
35 static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
36
37 struct XPadState {
38 s64_le sampling_number;
39 s64_le sampling_number2;
40 s32_le attributes;
41 u32_le pad_states;
42 AnalogStick x_stick;
43 AnalogStick y_stick;
44 };
45 static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
46
47 struct XPadEntry {
48 CommonHeader header;
49 std::array<XPadState, 17> pad_states{};
50 INSERT_PADDING_BYTES(0x138);
51 };
52 static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
53
54 struct SharedMemory {
55 std::array<XPadEntry, 4> shared_memory_entries{};
56 };
57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
58 SharedMemory shared_memory{};
59};
60} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 7c6b0a4e6..8aca0f197 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include "common/common_types.h"
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/core_timing.h" 9#include "core/core_timing.h"
@@ -19,6 +21,16 @@
19#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
20#include "core/settings.h" 22#include "core/settings.h"
21 23
24#include "core/hle/service/hid/controllers/controller_base.h"
25#include "core/hle/service/hid/controllers/debug_pad.h"
26#include "core/hle/service/hid/controllers/gesture.h"
27#include "core/hle/service/hid/controllers/keyboard.h"
28#include "core/hle/service/hid/controllers/mouse.h"
29#include "core/hle/service/hid/controllers/npad.h"
30#include "core/hle/service/hid/controllers/stubbed.h"
31#include "core/hle/service/hid/controllers/touchscreen.h"
32#include "core/hle/service/hid/controllers/xpad.h"
33
22namespace Service::HID { 34namespace Service::HID {
23 35
24// Updating period for each HID device. 36// Updating period for each HID device.
@@ -26,6 +38,22 @@ namespace Service::HID {
26constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
27constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
28constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
42enum class HidController : std::size_t {
43 DebugPad,
44 Touchscreen,
45 Mouse,
46 Keyboard,
47 XPad,
48 Unknown1,
49 Unknown2,
50 Unknown3,
51 SixAxisSensor,
52 NPad,
53 Gesture,
54
55 MaxControllers,
56};
29 57
30class IAppletResource final : public ServiceFramework<IAppletResource> { 58class IAppletResource final : public ServiceFramework<IAppletResource> {
31public: 59public:
@@ -37,19 +65,57 @@ public:
37 65
38 auto& kernel = Core::System::GetInstance().Kernel(); 66 auto& kernel = Core::System::GetInstance().Kernel();
39 shared_mem = Kernel::SharedMemory::Create( 67 shared_mem = Kernel::SharedMemory::Create(
40 kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, 68 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
41 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); 69 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
42 70
71 MakeController<Controller_DebugPad>(HidController::DebugPad);
72 MakeController<Controller_Touchscreen>(HidController::Touchscreen);
73 MakeController<Controller_Mouse>(HidController::Mouse);
74 MakeController<Controller_Keyboard>(HidController::Keyboard);
75 MakeController<Controller_XPad>(HidController::XPad);
76 MakeController<Controller_Stubbed>(HidController::Unknown1);
77 MakeController<Controller_Stubbed>(HidController::Unknown2);
78 MakeController<Controller_Stubbed>(HidController::Unknown3);
79 MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
80 MakeController<Controller_NPad>(HidController::NPad);
81 MakeController<Controller_Gesture>(HidController::Gesture);
82
83 // Homebrew doesn't try to activate some controllers, so we activate them by default
84 GetController<Controller_NPad>(HidController::NPad).ActivateController();
85 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
86
87 GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
88 GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
89 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
90
43 // Register update callbacks 91 // Register update callbacks
44 pad_update_event = CoreTiming::RegisterEvent( 92 pad_update_event = CoreTiming::RegisterEvent(
45 "HID::UpdatePadCallback", 93 "HID::UpdatePadCallback",
46 [this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); }); 94 [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
47 95
48 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
49 97
50 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); 98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
51 } 99 }
52 100
101 void ActivateController(HidController controller) {
102 controllers[static_cast<size_t>(controller)]->ActivateController();
103 }
104
105 void DeactivateController(HidController controller) {
106 controllers[static_cast<size_t>(controller)]->DeactivateController();
107 }
108
109 template <typename T>
110 void MakeController(HidController controller) {
111 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
112 }
113
114 template <typename T>
115 T& GetController(HidController controller) {
116 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
117 }
118
53 ~IAppletResource() { 119 ~IAppletResource() {
54 CoreTiming::UnscheduleEvent(pad_update_event, 0); 120 CoreTiming::UnscheduleEvent(pad_update_event, 0);
55 } 121 }
@@ -62,200 +128,15 @@ private:
62 LOG_DEBUG(Service_HID, "called"); 128 LOG_DEBUG(Service_HID, "called");
63 } 129 }
64 130
65 void LoadInputDevices() { 131 void UpdateControllers(u64 userdata, int cycles_late) {
66 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 132 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
67 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 133 for (const auto& controller : controllers) {
68 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 134 if (should_reload) {
69 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 135 controller->OnLoadInputDevices();
70 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
71 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
72 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
73 // TODO(shinyquagsire23): gyro, mouse, keyboard
74 }
75
76 void UpdatePadCallback(u64 userdata, int cycles_late) {
77 SharedMemory mem{};
78 std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
79
80 if (Settings::values.is_device_reload_pending.exchange(false))
81 LoadInputDevices();
82
83 // Set up controllers as neon red+blue Joy-Con attached to console
84 ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
85 controller_header.type = ControllerType_Handheld;
86 controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
87 controller_header.right_color_body = JOYCON_BODY_NEON_RED;
88 controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
89 controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
90 controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
91
92 for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) {
93 for (auto& layout : mem.controllers[controller].layouts) {
94 layout.header.num_entries = HID_NUM_ENTRIES;
95 layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
96
97 // HID shared memory stores the state of the past 17 samples in a circlular buffer,
98 // each with a timestamp in number of samples since boot.
99 const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
100
101 layout.header.timestamp_ticks = CoreTiming::GetTicks();
102 layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
103
104 ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
105 entry.timestamp = last_entry.timestamp + 1;
106 // TODO(shinyquagsire23): Is this always identical to timestamp?
107 entry.timestamp_2 = entry.timestamp;
108
109 // TODO(shinyquagsire23): More than just handheld input
110 if (controller != Controller_Handheld)
111 continue;
112
113 entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
114
115 // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
116 // For now everything is just the default handheld layout, but split Joy-Con will
117 // rotate the face buttons and directions for certain layouts.
118 ControllerPadState& state = entry.buttons;
119 using namespace Settings::NativeButton;
120 state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
121 state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
122 state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
123 state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
124 state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
125 state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
126 state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
127 state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
128 state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
129 state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
130 state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
131 state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
132
133 state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
134 state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
135 state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
136 state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
137
138 state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
139 state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
140 state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
141 state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
142
143 state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
144 state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
145 state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
146 state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
147
148 state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
149 state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
150
151 const auto [stick_l_x_f, stick_l_y_f] = sticks[Joystick_Left]->GetStatus();
152 const auto [stick_r_x_f, stick_r_y_f] = sticks[Joystick_Right]->GetStatus();
153 entry.joystick_left_x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
154 entry.joystick_left_y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
155 entry.joystick_right_x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
156 entry.joystick_right_y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
157 } 136 }
137 controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
158 } 138 }
159 139
160 TouchScreen& touchscreen = mem.touchscreen;
161 const u64 last_entry = touchscreen.header.latest_entry;
162 const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
163 const u64 timestamp = CoreTiming::GetTicks();
164 const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
165 touchscreen.header.timestamp_ticks = timestamp;
166 touchscreen.header.num_entries = touchscreen.entries.size();
167 touchscreen.header.latest_entry = curr_entry;
168 touchscreen.header.max_entry_index = touchscreen.entries.size();
169 touchscreen.header.timestamp = timestamp;
170 touchscreen.entries[curr_entry].header.timestamp = sample_counter;
171
172 TouchScreenEntryTouch touch_entry{};
173 auto [x, y, pressed] = touch_device->GetStatus();
174 touch_entry.timestamp = timestamp;
175 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
176 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
177 touch_entry.touch_index = 0;
178
179 // TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
180 touch_entry.diameter_x = 15;
181 touch_entry.diameter_y = 15;
182 touch_entry.angle = 0;
183
184 // TODO(DarkLordZach): Implement multi-touch support
185 if (pressed) {
186 touchscreen.entries[curr_entry].header.num_touches = 1;
187 touchscreen.entries[curr_entry].touches[0] = touch_entry;
188 } else {
189 touchscreen.entries[curr_entry].header.num_touches = 0;
190 }
191
192 // TODO(shinyquagsire23): Properly implement mouse
193 Mouse& mouse = mem.mouse;
194 const u64 last_mouse_entry = mouse.header.latest_entry;
195 const u64 curr_mouse_entry = (mouse.header.latest_entry + 1) % mouse.entries.size();
196 const u64 mouse_sample_counter = mouse.entries[last_mouse_entry].timestamp + 1;
197 mouse.header.timestamp_ticks = timestamp;
198 mouse.header.num_entries = mouse.entries.size();
199 mouse.header.max_entry_index = mouse.entries.size();
200 mouse.header.latest_entry = curr_mouse_entry;
201
202 mouse.entries[curr_mouse_entry].timestamp = mouse_sample_counter;
203 mouse.entries[curr_mouse_entry].timestamp_2 = mouse_sample_counter;
204
205 // TODO(shinyquagsire23): Properly implement keyboard
206 Keyboard& keyboard = mem.keyboard;
207 const u64 last_keyboard_entry = keyboard.header.latest_entry;
208 const u64 curr_keyboard_entry =
209 (keyboard.header.latest_entry + 1) % keyboard.entries.size();
210 const u64 keyboard_sample_counter = keyboard.entries[last_keyboard_entry].timestamp + 1;
211 keyboard.header.timestamp_ticks = timestamp;
212 keyboard.header.num_entries = keyboard.entries.size();
213 keyboard.header.latest_entry = last_keyboard_entry;
214 keyboard.header.max_entry_index = keyboard.entries.size();
215
216 keyboard.entries[curr_keyboard_entry].timestamp = keyboard_sample_counter;
217 keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter;
218
219 // TODO(shinyquagsire23): Figure out what any of these are
220 for (auto& input : mem.unk_input_1) {
221 const u64 last_input_entry = input.header.latest_entry;
222 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
223 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
224
225 input.header.timestamp_ticks = timestamp;
226 input.header.num_entries = input.entries.size();
227 input.header.latest_entry = last_input_entry;
228 input.header.max_entry_index = input.entries.size();
229
230 input.entries[curr_input_entry].timestamp = input_sample_counter;
231 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
232 }
233
234 for (auto& input : mem.unk_input_2) {
235 input.header.timestamp_ticks = timestamp;
236 input.header.num_entries = 17;
237 input.header.latest_entry = 0;
238 input.header.max_entry_index = 0;
239 }
240
241 UnkInput3& input = mem.unk_input_3;
242 const u64 last_input_entry = input.header.latest_entry;
243 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
244 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
245
246 input.header.timestamp_ticks = timestamp;
247 input.header.num_entries = input.entries.size();
248 input.header.latest_entry = last_input_entry;
249 input.header.max_entry_index = input.entries.size();
250
251 input.entries[curr_input_entry].timestamp = input_sample_counter;
252 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
253
254 // TODO(shinyquagsire23): Signal events
255
256 std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
257
258 // Reschedule recurrent event
259 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); 140 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
260 } 141 }
261 142
@@ -265,11 +146,8 @@ private:
265 // CoreTiming update events 146 // CoreTiming update events
266 CoreTiming::EventType* pad_update_event; 147 CoreTiming::EventType* pad_update_event;
267 148
268 // Stored input state info 149 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
269 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 150 controllers{};
270 buttons;
271 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
272 std::unique_ptr<Input::TouchDevice> touch_device;
273}; 151};
274 152
275class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 153class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -301,7 +179,7 @@ public:
301 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, 179 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
302 {40, nullptr, "AcquireXpadIdEventHandle"}, 180 {40, nullptr, "AcquireXpadIdEventHandle"},
303 {41, nullptr, "ReleaseXpadIdEventHandle"}, 181 {41, nullptr, "ReleaseXpadIdEventHandle"},
304 {51, nullptr, "ActivateXpad"}, 182 {51, &Hid::ActivateXpad, "ActivateXpad"},
305 {55, nullptr, "GetXpadIds"}, 183 {55, nullptr, "GetXpadIds"},
306 {56, nullptr, "ActivateJoyXpad"}, 184 {56, nullptr, "ActivateJoyXpad"},
307 {58, nullptr, "GetJoyXpadLifoHandle"}, 185 {58, nullptr, "GetJoyXpadLifoHandle"},
@@ -362,8 +240,8 @@ public:
362 {206, &Hid::SendVibrationValues, "SendVibrationValues"}, 240 {206, &Hid::SendVibrationValues, "SendVibrationValues"},
363 {207, nullptr, "SendVibrationGcErmCommand"}, 241 {207, nullptr, "SendVibrationGcErmCommand"},
364 {208, nullptr, "GetActualVibrationGcErmCommand"}, 242 {208, nullptr, "GetActualVibrationGcErmCommand"},
365 {209, nullptr, "BeginPermitVibrationSession"}, 243 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
366 {210, nullptr, "EndPermitVibrationSession"}, 244 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
367 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 245 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
368 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 246 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
369 {302, nullptr, "StopConsoleSixAxisSensor"}, 247 {302, nullptr, "StopConsoleSixAxisSensor"},
@@ -401,16 +279,11 @@ public:
401 // clang-format on 279 // clang-format on
402 280
403 RegisterHandlers(functions); 281 RegisterHandlers(functions);
404
405 auto& kernel = Core::System::GetInstance().Kernel();
406 event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
407 } 282 }
408 ~Hid() = default; 283 ~Hid() = default;
409 284
410private: 285private:
411 std::shared_ptr<IAppletResource> applet_resource; 286 std::shared_ptr<IAppletResource> applet_resource;
412 u32 joy_hold_type{0};
413 Kernel::SharedPtr<Kernel::Event> event;
414 287
415 void CreateAppletResource(Kernel::HLERequestContext& ctx) { 288 void CreateAppletResource(Kernel::HLERequestContext& ctx) {
416 if (applet_resource == nullptr) { 289 if (applet_resource == nullptr) {
@@ -423,31 +296,59 @@ private:
423 LOG_DEBUG(Service_HID, "called"); 296 LOG_DEBUG(Service_HID, "called");
424 } 297 }
425 298
299 void ActivateXpad(Kernel::HLERequestContext& ctx) {
300 applet_resource->ActivateController(HidController::XPad);
301 IPC::ResponseBuilder rb{ctx, 2};
302 rb.Push(RESULT_SUCCESS);
303 LOG_DEBUG(Service_HID, "called");
304 }
305
426 void ActivateDebugPad(Kernel::HLERequestContext& ctx) { 306 void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
307 applet_resource->ActivateController(HidController::DebugPad);
427 IPC::ResponseBuilder rb{ctx, 2}; 308 IPC::ResponseBuilder rb{ctx, 2};
428 rb.Push(RESULT_SUCCESS); 309 rb.Push(RESULT_SUCCESS);
429 LOG_WARNING(Service_HID, "(STUBBED) called"); 310 LOG_DEBUG(Service_HID, "called");
430 } 311 }
431 312
432 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { 313 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
314 applet_resource->ActivateController(HidController::Touchscreen);
433 IPC::ResponseBuilder rb{ctx, 2}; 315 IPC::ResponseBuilder rb{ctx, 2};
434 rb.Push(RESULT_SUCCESS); 316 rb.Push(RESULT_SUCCESS);
435 LOG_WARNING(Service_HID, "(STUBBED) called"); 317 LOG_DEBUG(Service_HID, "called");
436 } 318 }
437 319
438 void ActivateMouse(Kernel::HLERequestContext& ctx) { 320 void ActivateMouse(Kernel::HLERequestContext& ctx) {
321 applet_resource->ActivateController(HidController::Mouse);
439 IPC::ResponseBuilder rb{ctx, 2}; 322 IPC::ResponseBuilder rb{ctx, 2};
440 rb.Push(RESULT_SUCCESS); 323 rb.Push(RESULT_SUCCESS);
441 LOG_WARNING(Service_HID, "(STUBBED) called"); 324 LOG_DEBUG(Service_HID, "called");
442 } 325 }
443 326
444 void ActivateKeyboard(Kernel::HLERequestContext& ctx) { 327 void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
328 applet_resource->ActivateController(HidController::Keyboard);
445 IPC::ResponseBuilder rb{ctx, 2}; 329 IPC::ResponseBuilder rb{ctx, 2};
446 rb.Push(RESULT_SUCCESS); 330 rb.Push(RESULT_SUCCESS);
447 LOG_WARNING(Service_HID, "(STUBBED) called"); 331 LOG_DEBUG(Service_HID, "called");
332 }
333
334 void ActivateGesture(Kernel::HLERequestContext& ctx) {
335 applet_resource->ActivateController(HidController::Gesture);
336 IPC::ResponseBuilder rb{ctx, 2};
337 rb.Push(RESULT_SUCCESS);
338 LOG_DEBUG(Service_HID, "called");
339 }
340
341 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
342 // Should have no effect with how our npad sets up the data
343 applet_resource->ActivateController(HidController::NPad);
344 IPC::ResponseBuilder rb{ctx, 2};
345 rb.Push(RESULT_SUCCESS);
346 LOG_DEBUG(Service_HID, "called");
448 } 347 }
449 348
450 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 349 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
350 IPC::RequestParser rp{ctx};
351 auto handle = rp.PopRaw<u32>();
451 IPC::ResponseBuilder rb{ctx, 2}; 352 IPC::ResponseBuilder rb{ctx, 2};
452 rb.Push(RESULT_SUCCESS); 353 rb.Push(RESULT_SUCCESS);
453 LOG_WARNING(Service_HID, "(STUBBED) called"); 354 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -468,84 +369,168 @@ private:
468 } 369 }
469 370
470 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 371 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
372 IPC::RequestParser rp{ctx};
373 auto supported_styleset = rp.PopRaw<u32>();
374 applet_resource->GetController<Controller_NPad>(HidController::NPad)
375 .SetSupportedStyleSet({supported_styleset});
376
471 IPC::ResponseBuilder rb{ctx, 2}; 377 IPC::ResponseBuilder rb{ctx, 2};
472 rb.Push(RESULT_SUCCESS); 378 rb.Push(RESULT_SUCCESS);
473 LOG_WARNING(Service_HID, "(STUBBED) called"); 379
380 LOG_DEBUG(Service_HID, "called");
474 } 381 }
475 382
476 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 383 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
384 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
385
477 IPC::ResponseBuilder rb{ctx, 3}; 386 IPC::ResponseBuilder rb{ctx, 3};
478 rb.Push(RESULT_SUCCESS); 387 rb.Push(RESULT_SUCCESS);
479 rb.Push<u32>(0); 388 rb.Push<u32>(controller.GetSupportedStyleSet().raw);
480 LOG_WARNING(Service_HID, "(STUBBED) called"); 389 LOG_DEBUG(Service_HID, "called");
481 } 390 }
482 391
483 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 392 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
393 applet_resource->GetController<Controller_NPad>(HidController::NPad)
394 .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
484 IPC::ResponseBuilder rb{ctx, 2}; 395 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(RESULT_SUCCESS); 396 rb.Push(RESULT_SUCCESS);
486 LOG_WARNING(Service_HID, "(STUBBED) called"); 397 LOG_DEBUG(Service_HID, "called");
487 } 398 }
488 399
489 void ActivateNpad(Kernel::HLERequestContext& ctx) { 400 void ActivateNpad(Kernel::HLERequestContext& ctx) {
490 IPC::ResponseBuilder rb{ctx, 2}; 401 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(RESULT_SUCCESS); 402 rb.Push(RESULT_SUCCESS);
492 LOG_WARNING(Service_HID, "(STUBBED) called"); 403 applet_resource->ActivateController(HidController::NPad);
404 LOG_DEBUG(Service_HID, "called");
493 } 405 }
494 406
495 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 407 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
408 IPC::RequestParser rp{ctx};
409 auto npad_id = rp.PopRaw<u32>();
496 IPC::ResponseBuilder rb{ctx, 2, 1}; 410 IPC::ResponseBuilder rb{ctx, 2, 1};
497 rb.Push(RESULT_SUCCESS); 411 rb.Push(RESULT_SUCCESS);
498 rb.PushCopyObjects(event); 412 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
499 LOG_WARNING(Service_HID, "(STUBBED) called"); 413 .GetStyleSetChangedEvent());
414 LOG_DEBUG(Service_HID, "called");
500 } 415 }
501 416
502 void DisconnectNpad(Kernel::HLERequestContext& ctx) { 417 void DisconnectNpad(Kernel::HLERequestContext& ctx) {
418 IPC::RequestParser rp{ctx};
419 auto npad_id = rp.PopRaw<u32>();
420 applet_resource->GetController<Controller_NPad>(HidController::NPad)
421 .DisconnectNPad(npad_id);
503 IPC::ResponseBuilder rb{ctx, 2}; 422 IPC::ResponseBuilder rb{ctx, 2};
504 rb.Push(RESULT_SUCCESS); 423 rb.Push(RESULT_SUCCESS);
505 LOG_WARNING(Service_HID, "(STUBBED) called"); 424 LOG_DEBUG(Service_HID, "called");
506 } 425 }
507 426
508 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 427 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
509 IPC::ResponseBuilder rb{ctx, 2}; 428 IPC::RequestParser rp{ctx};
429 auto npad_id = rp.PopRaw<u32>();
430 IPC::ResponseBuilder rb{ctx, 4};
510 rb.Push(RESULT_SUCCESS); 431 rb.Push(RESULT_SUCCESS);
511 LOG_WARNING(Service_HID, "(STUBBED) called"); 432 rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
433 .GetLedPattern(npad_id)
434 .raw);
435 LOG_DEBUG(Service_HID, "called");
512 } 436 }
513 437
514 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 438 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
439 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
440 IPC::RequestParser rp{ctx};
441 const auto hold_type = rp.PopRaw<u64>();
442 controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
443
515 IPC::ResponseBuilder rb{ctx, 2}; 444 IPC::ResponseBuilder rb{ctx, 2};
516 rb.Push(RESULT_SUCCESS); 445 rb.Push(RESULT_SUCCESS);
517 LOG_WARNING(Service_HID, "(STUBBED) called"); 446 LOG_DEBUG(Service_HID, "called");
518 } 447 }
519 448
520 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 449 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
521 IPC::ResponseBuilder rb{ctx, 3}; 450 const auto& controller =
451 applet_resource->GetController<Controller_NPad>(HidController::NPad);
452 IPC::ResponseBuilder rb{ctx, 4};
522 rb.Push(RESULT_SUCCESS); 453 rb.Push(RESULT_SUCCESS);
523 rb.Push(joy_hold_type); 454 rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
524 LOG_WARNING(Service_HID, "(STUBBED) called"); 455 LOG_DEBUG(Service_HID, "called");
525 } 456 }
526 457
527 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 458 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
459 IPC::RequestParser rp{ctx};
460 auto npad_id = rp.PopRaw<u32>();
528 IPC::ResponseBuilder rb{ctx, 2}; 461 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(RESULT_SUCCESS); 462 rb.Push(RESULT_SUCCESS);
530 LOG_WARNING(Service_HID, "(STUBBED) called"); 463 LOG_WARNING(Service_HID, "(STUBBED) called");
531 } 464 }
532 465
466 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
467 applet_resource->GetController<Controller_NPad>(HidController::NPad)
468 .SetVibrationEnabled(true);
469 IPC::ResponseBuilder rb{ctx, 2};
470 rb.Push(RESULT_SUCCESS);
471 LOG_DEBUG(Service_HID, "called");
472 }
473
474 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
475 applet_resource->GetController<Controller_NPad>(HidController::NPad)
476 .SetVibrationEnabled(false);
477 IPC::ResponseBuilder rb{ctx, 2};
478 rb.Push(RESULT_SUCCESS);
479 LOG_DEBUG(Service_HID, "called");
480 }
481
533 void SendVibrationValue(Kernel::HLERequestContext& ctx) { 482 void SendVibrationValue(Kernel::HLERequestContext& ctx) {
483 IPC::RequestParser rp{ctx};
484 const auto controller_id = rp.PopRaw<u32>();
485 const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
486
534 IPC::ResponseBuilder rb{ctx, 2}; 487 IPC::ResponseBuilder rb{ctx, 2};
535 rb.Push(RESULT_SUCCESS); 488 rb.Push(RESULT_SUCCESS);
536 LOG_WARNING(Service_HID, "(STUBBED) called"); 489
490 applet_resource->GetController<Controller_NPad>(HidController::NPad)
491 .VibrateController({controller_id}, {vibration_values});
492 LOG_DEBUG(Service_HID, "called");
537 } 493 }
538 494
539 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 495 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
496 const auto controllers = ctx.ReadBuffer(0);
497 const auto vibrations = ctx.ReadBuffer(1);
498
499 std::vector<u32> controller_list(controllers.size() / sizeof(u32));
500 std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
501 sizeof(Controller_NPad::Vibration));
502
503 std::memcpy(controller_list.data(), controllers.data(), controllers.size());
504 std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
505 std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
506 [](u32 controller_id) { return controller_id - 3; });
507
508 applet_resource->GetController<Controller_NPad>(HidController::NPad)
509 .VibrateController(controller_list, vibration_list);
510
540 IPC::ResponseBuilder rb{ctx, 2}; 511 IPC::ResponseBuilder rb{ctx, 2};
541 rb.Push(RESULT_SUCCESS); 512 rb.Push(RESULT_SUCCESS);
542 LOG_WARNING(Service_HID, "(STUBBED) called"); 513 LOG_DEBUG(Service_HID, "called");
514 }
515
516 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
517 IPC::ResponseBuilder rb{ctx, 6};
518 rb.Push(RESULT_SUCCESS);
519 rb.PushRaw<Controller_NPad::Vibration>(
520 applet_resource->GetController<Controller_NPad>(HidController::NPad)
521 .GetLastVibration());
522 LOG_DEBUG(Service_HID, "called");
543 } 523 }
544 524
545 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 525 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
526 IPC::RequestParser rp{ctx};
527 const auto npad_id = rp.PopRaw<u32>();
528 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
529 controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
530
546 IPC::ResponseBuilder rb{ctx, 2}; 531 IPC::ResponseBuilder rb{ctx, 2};
547 rb.Push(RESULT_SUCCESS); 532 rb.Push(RESULT_SUCCESS);
548 LOG_WARNING(Service_HID, "(STUBBED) called"); 533 LOG_DEBUG(Service_HID, "called");
549 } 534 }
550 535
551 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 536 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -555,6 +540,8 @@ private:
555 } 540 }
556 541
557 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { 542 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
543 IPC::RequestParser rp{ctx};
544 auto mode = rp.PopRaw<u32>();
558 IPC::ResponseBuilder rb{ctx, 2}; 545 IPC::ResponseBuilder rb{ctx, 2};
559 rb.Push(RESULT_SUCCESS); 546 rb.Push(RESULT_SUCCESS);
560 LOG_WARNING(Service_HID, "(STUBBED) called"); 547 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -563,8 +550,9 @@ private:
563 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 550 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
564 IPC::ResponseBuilder rb{ctx, 4}; 551 IPC::ResponseBuilder rb{ctx, 4};
565 rb.Push(RESULT_SUCCESS); 552 rb.Push(RESULT_SUCCESS);
566 rb.Push<u64>(0); 553 rb.Push<u32>(1);
567 LOG_WARNING(Service_HID, "(STUBBED) called"); 554 rb.Push<u32>(0);
555 LOG_DEBUG(Service_HID, "called");
568 } 556 }
569 557
570 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { 558 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
@@ -574,12 +562,6 @@ private:
574 LOG_DEBUG(Service_HID, "called"); 562 LOG_DEBUG(Service_HID, "called");
575 } 563 }
576 564
577 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
578 IPC::ResponseBuilder rb{ctx, 2};
579 rb.Push(RESULT_SUCCESS);
580 LOG_WARNING(Service_HID, "(STUBBED) called");
581 }
582
583 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 565 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
584 IPC::ResponseBuilder rb{ctx, 2}; 566 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(RESULT_SUCCESS); 567 rb.Push(RESULT_SUCCESS);
@@ -597,18 +579,6 @@ private:
597 rb.Push(RESULT_SUCCESS); 579 rb.Push(RESULT_SUCCESS);
598 LOG_WARNING(Service_HID, "(STUBBED) called"); 580 LOG_WARNING(Service_HID, "(STUBBED) called");
599 } 581 }
600
601 void ActivateGesture(Kernel::HLERequestContext& ctx) {
602 IPC::ResponseBuilder rb{ctx, 2};
603 rb.Push(RESULT_SUCCESS);
604 LOG_WARNING(Service_HID, "(STUBBED) called");
605 }
606
607 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
608 IPC::ResponseBuilder rb{ctx, 2};
609 rb.Push(RESULT_SUCCESS);
610 LOG_WARNING(Service_HID, "(STUBBED) called");
611 }
612}; 582};
613 583
614class HidDbg final : public ServiceFramework<HidDbg> { 584class HidDbg final : public ServiceFramework<HidDbg> {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 88d926808..773035460 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,408 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7namespace SM {
8#include "common/bit_field.h" 8class ServiceManager;
9#include "common/common_types.h" 9}
10#include "core/hle/service/service.h"
11 10
12namespace Service::HID { 11namespace Service::HID {
13 12
14// Begin enums and output structs
15
16constexpr u32 HID_NUM_ENTRIES = 17;
17constexpr u32 HID_NUM_LAYOUTS = 7;
18constexpr s32 HID_JOYSTICK_MAX = 0x8000;
19constexpr s32 HID_JOYSTICK_MIN = -0x8000;
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25
26enum ControllerType : u32 {
27 ControllerType_ProController = 1 << 0,
28 ControllerType_Handheld = 1 << 1,
29 ControllerType_JoyconPair = 1 << 2,
30 ControllerType_JoyconLeft = 1 << 3,
31 ControllerType_JoyconRight = 1 << 4,
32};
33
34enum ControllerLayoutType : u32 {
35 Layout_ProController = 0, // Pro Controller or HID gamepad
36 Layout_Handheld = 1, // Two Joy-Con docked to rails
37 Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation
38 Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment
39 Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment
40 Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only
41 Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation
42};
43
44enum ControllerColorDescription {
45 ColorDesc_ColorsNonexistent = 1 << 1,
46};
47
48enum ControllerConnectionState {
49 ConnectionState_Connected = 1 << 0,
50 ConnectionState_Wired = 1 << 1,
51};
52
53enum ControllerJoystick {
54 Joystick_Left = 0,
55 Joystick_Right = 1,
56};
57
58enum ControllerID {
59 Controller_Player1 = 0,
60 Controller_Player2 = 1,
61 Controller_Player3 = 2,
62 Controller_Player4 = 3,
63 Controller_Player5 = 4,
64 Controller_Player6 = 5,
65 Controller_Player7 = 6,
66 Controller_Player8 = 7,
67 Controller_Handheld = 8,
68 Controller_Unknown = 9,
69};
70
71// End enums and output structs
72
73// Begin UnkInput3
74
75struct UnkInput3Header {
76 u64 timestamp_ticks;
77 u64 num_entries;
78 u64 latest_entry;
79 u64 max_entry_index;
80};
81static_assert(sizeof(UnkInput3Header) == 0x20, "HID UnkInput3 header structure has incorrect size");
82
83struct UnkInput3Entry {
84 u64 timestamp;
85 u64 timestamp_2;
86 u64 unk_8;
87 u64 unk_10;
88 u64 unk_18;
89};
90static_assert(sizeof(UnkInput3Entry) == 0x28, "HID UnkInput3 entry structure has incorrect size");
91
92struct UnkInput3 {
93 UnkInput3Header header;
94 std::array<UnkInput3Entry, 17> entries;
95 std::array<u8, 0x138> padding;
96};
97static_assert(sizeof(UnkInput3) == 0x400, "HID UnkInput3 structure has incorrect size");
98
99// End UnkInput3
100
101// Begin TouchScreen
102
103struct TouchScreenHeader {
104 u64 timestamp_ticks;
105 u64 num_entries;
106 u64 latest_entry;
107 u64 max_entry_index;
108 u64 timestamp;
109};
110static_assert(sizeof(TouchScreenHeader) == 0x28,
111 "HID touch screen header structure has incorrect size");
112
113struct TouchScreenEntryHeader {
114 u64 timestamp;
115 u64 num_touches;
116};
117static_assert(sizeof(TouchScreenEntryHeader) == 0x10,
118 "HID touch screen entry header structure has incorrect size");
119
120struct TouchScreenEntryTouch {
121 u64 timestamp;
122 u32 padding;
123 u32 touch_index;
124 u32 x;
125 u32 y;
126 u32 diameter_x;
127 u32 diameter_y;
128 u32 angle;
129 u32 padding_2;
130};
131static_assert(sizeof(TouchScreenEntryTouch) == 0x28,
132 "HID touch screen touch structure has incorrect size");
133
134struct TouchScreenEntry {
135 TouchScreenEntryHeader header;
136 std::array<TouchScreenEntryTouch, 16> touches;
137 u64 unk;
138};
139static_assert(sizeof(TouchScreenEntry) == 0x298,
140 "HID touch screen entry structure has incorrect size");
141
142struct TouchScreen {
143 TouchScreenHeader header;
144 std::array<TouchScreenEntry, 17> entries;
145 std::array<u8, 0x3c0> padding;
146};
147static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size");
148
149// End TouchScreen
150
151// Begin Mouse
152
153struct MouseHeader {
154 u64 timestamp_ticks;
155 u64 num_entries;
156 u64 latest_entry;
157 u64 max_entry_index;
158};
159static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size");
160
161struct MouseButtonState {
162 union {
163 u64 hex{};
164
165 // Buttons
166 BitField<0, 1, u64> left;
167 BitField<1, 1, u64> right;
168 BitField<2, 1, u64> middle;
169 BitField<3, 1, u64> forward;
170 BitField<4, 1, u64> back;
171 };
172};
173
174struct MouseEntry {
175 u64 timestamp;
176 u64 timestamp_2;
177 u32 x;
178 u32 y;
179 u32 velocity_x;
180 u32 velocity_y;
181 u32 scroll_velocity_x;
182 u32 scroll_velocity_y;
183 MouseButtonState buttons;
184};
185static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size");
186
187struct Mouse {
188 MouseHeader header;
189 std::array<MouseEntry, 17> entries;
190 std::array<u8, 0xB0> padding;
191};
192static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size");
193
194// End Mouse
195
196// Begin Keyboard
197
198struct KeyboardHeader {
199 u64 timestamp_ticks;
200 u64 num_entries;
201 u64 latest_entry;
202 u64 max_entry_index;
203};
204static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size");
205
206struct KeyboardModifierKeyState {
207 union {
208 u64 hex{};
209
210 // Buttons
211 BitField<0, 1, u64> lctrl;
212 BitField<1, 1, u64> lshift;
213 BitField<2, 1, u64> lalt;
214 BitField<3, 1, u64> lmeta;
215 BitField<4, 1, u64> rctrl;
216 BitField<5, 1, u64> rshift;
217 BitField<6, 1, u64> ralt;
218 BitField<7, 1, u64> rmeta;
219 BitField<8, 1, u64> capslock;
220 BitField<9, 1, u64> scrolllock;
221 BitField<10, 1, u64> numlock;
222 };
223};
224
225struct KeyboardEntry {
226 u64 timestamp;
227 u64 timestamp_2;
228 KeyboardModifierKeyState modifier;
229 u32 keys[8];
230};
231static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size");
232
233struct Keyboard {
234 KeyboardHeader header;
235 std::array<KeyboardEntry, 17> entries;
236 std::array<u8, 0x28> padding;
237};
238static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size");
239
240// End Keyboard
241
242// Begin UnkInput1
243
244struct UnkInput1Header {
245 u64 timestamp_ticks;
246 u64 num_entries;
247 u64 latest_entry;
248 u64 max_entry_index;
249};
250static_assert(sizeof(UnkInput1Header) == 0x20, "HID UnkInput1 header structure has incorrect size");
251
252struct UnkInput1Entry {
253 u64 timestamp;
254 u64 timestamp_2;
255 u64 unk_8;
256 u64 unk_10;
257 u64 unk_18;
258};
259static_assert(sizeof(UnkInput1Entry) == 0x28, "HID UnkInput1 entry structure has incorrect size");
260
261struct UnkInput1 {
262 UnkInput1Header header;
263 std::array<UnkInput1Entry, 17> entries;
264 std::array<u8, 0x138> padding;
265};
266static_assert(sizeof(UnkInput1) == 0x400, "HID UnkInput1 structure has incorrect size");
267
268// End UnkInput1
269
270// Begin UnkInput2
271
272struct UnkInput2Header {
273 u64 timestamp_ticks;
274 u64 num_entries;
275 u64 latest_entry;
276 u64 max_entry_index;
277};
278static_assert(sizeof(UnkInput2Header) == 0x20, "HID UnkInput2 header structure has incorrect size");
279
280struct UnkInput2 {
281 UnkInput2Header header;
282 std::array<u8, 0x1E0> padding;
283};
284static_assert(sizeof(UnkInput2) == 0x200, "HID UnkInput2 structure has incorrect size");
285
286// End UnkInput2
287
288// Begin Controller
289
290struct ControllerMAC {
291 u64 timestamp;
292 std::array<u8, 0x8> mac;
293 u64 unk;
294 u64 timestamp_2;
295};
296static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size");
297
298struct ControllerHeader {
299 u32 type;
300 u32 is_half;
301 u32 single_colors_descriptor;
302 u32 single_color_body;
303 u32 single_color_buttons;
304 u32 split_colors_descriptor;
305 u32 left_color_body;
306 u32 left_color_buttons;
307 u32 right_color_body;
308 u32 right_color_buttons;
309};
310static_assert(sizeof(ControllerHeader) == 0x28,
311 "HID controller header structure has incorrect size");
312
313struct ControllerLayoutHeader {
314 u64 timestamp_ticks;
315 u64 num_entries;
316 u64 latest_entry;
317 u64 max_entry_index;
318};
319static_assert(sizeof(ControllerLayoutHeader) == 0x20,
320 "HID controller layout header structure has incorrect size");
321
322struct ControllerPadState {
323 union {
324 u64 hex{};
325
326 // Buttons
327 BitField<0, 1, u64> a;
328 BitField<1, 1, u64> b;
329 BitField<2, 1, u64> x;
330 BitField<3, 1, u64> y;
331 BitField<4, 1, u64> lstick;
332 BitField<5, 1, u64> rstick;
333 BitField<6, 1, u64> l;
334 BitField<7, 1, u64> r;
335 BitField<8, 1, u64> zl;
336 BitField<9, 1, u64> zr;
337 BitField<10, 1, u64> plus;
338 BitField<11, 1, u64> minus;
339
340 // D-pad buttons
341 BitField<12, 1, u64> dleft;
342 BitField<13, 1, u64> dup;
343 BitField<14, 1, u64> dright;
344 BitField<15, 1, u64> ddown;
345
346 // Left stick directions
347 BitField<16, 1, u64> lstick_left;
348 BitField<17, 1, u64> lstick_up;
349 BitField<18, 1, u64> lstick_right;
350 BitField<19, 1, u64> lstick_down;
351
352 // Right stick directions
353 BitField<20, 1, u64> rstick_left;
354 BitField<21, 1, u64> rstick_up;
355 BitField<22, 1, u64> rstick_right;
356 BitField<23, 1, u64> rstick_down;
357
358 BitField<24, 1, u64> sl;
359 BitField<25, 1, u64> sr;
360 };
361};
362
363struct ControllerInputEntry {
364 u64 timestamp;
365 u64 timestamp_2;
366 ControllerPadState buttons;
367 s32 joystick_left_x;
368 s32 joystick_left_y;
369 s32 joystick_right_x;
370 s32 joystick_right_y;
371 u64 connection_state;
372};
373static_assert(sizeof(ControllerInputEntry) == 0x30,
374 "HID controller input entry structure has incorrect size");
375
376struct ControllerLayout {
377 ControllerLayoutHeader header;
378 std::array<ControllerInputEntry, 17> entries;
379};
380static_assert(sizeof(ControllerLayout) == 0x350,
381 "HID controller layout structure has incorrect size");
382
383struct Controller {
384 ControllerHeader header;
385 std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts;
386 std::array<u8, 0x2a70> unk_1;
387 ControllerMAC mac_left;
388 ControllerMAC mac_right;
389 std::array<u8, 0xdf8> unk_2;
390};
391static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size");
392
393// End Controller
394
395struct SharedMemory {
396 UnkInput3 unk_input_3;
397 TouchScreen touchscreen;
398 Mouse mouse;
399 Keyboard keyboard;
400 std::array<UnkInput1, 4> unk_input_1;
401 std::array<UnkInput2, 3> unk_input_2;
402 std::array<u8, 0x800> unk_section_8;
403 std::array<u8, 0x4000> controller_serials;
404 std::array<Controller, 10> controllers;
405 std::array<u8, 0x4600> unk_section_9;
406};
407static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size");
408
409/// Reload input devices. Used when input configuration changed 13/// Reload input devices. Used when input configuration changed
410void ReloadInputDevices(); 14void ReloadInputDevices();
411 15
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 8c07a05c2..39c0c1e63 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -144,7 +144,7 @@ private:
144 } 144 }
145 145
146 const u64 device_handle{0xDEAD}; 146 const u64 device_handle{0xDEAD};
147 const HID::ControllerID npad_id{HID::Controller_Player1}; 147 const u32 npad_id{0}; // This is the first player controller id
148 State state{State::NonInitialized}; 148 State state{State::NonInitialized};
149 DeviceState device_state{DeviceState::Initialized}; 149 DeviceState device_state{DeviceState::Initialized};
150 Kernel::SharedPtr<Kernel::Event> activate_event; 150 Kernel::SharedPtr<Kernel::Event> activate_event;
diff --git a/src/core/settings.h b/src/core/settings.h
index 83b9a04c8..8f2da01c8 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -136,7 +136,7 @@ struct Values {
136 float resolution_factor; 136 float resolution_factor;
137 bool use_frame_limit; 137 bool use_frame_limit;
138 u16 frame_limit; 138 u16 frame_limit;
139 bool use_accurate_framebuffers; 139 bool use_accurate_gpu_emulation;
140 140
141 float bg_red; 141 float bg_red;
142 float bg_green; 142 float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 7b04792b5..0de13edd3 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -163,8 +163,8 @@ TelemetrySession::TelemetrySession() {
163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", 163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
164 Settings::values.use_frame_limit); 164 Settings::values.use_frame_limit);
165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); 165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", 166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
167 Settings::values.use_accurate_framebuffers); 167 Settings::values.use_accurate_gpu_emulation);
168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
169 Settings::values.use_docked_mode); 169 Settings::values.use_docked_mode);
170} 170}
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c8d1b6478..c8af1c6b6 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -448,7 +448,10 @@ public:
448 BitField<8, 3, u32> block_depth; 448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type; 449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout; 450 } memory_layout;
451 u32 array_mode; 451 union {
452 BitField<0, 16, u32> array_mode;
453 BitField<16, 1, u32> volume;
454 };
452 u32 layer_stride; 455 u32 layer_stride;
453 u32 base_layer; 456 u32 base_layer;
454 INSERT_PADDING_WORDS(7); 457 INSERT_PADDING_WORDS(7);
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index ca923d17d..022d4ab74 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -87,6 +87,16 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
87 return gpu_addr; 87 return gpu_addr;
88} 88}
89 89
90GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const {
91 for (const auto& region : mapped_regions) {
92 const GPUVAddr region_end{region.gpu_addr + region.size};
93 if (region_start >= region.gpu_addr && region_start < region_end) {
94 return region_end;
95 }
96 }
97 return {};
98}
99
90boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { 100boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
91 GPUVAddr gpu_addr = 0; 101 GPUVAddr gpu_addr = 0;
92 u64 free_space = 0; 102 u64 free_space = 0;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 86765e72a..caf80093f 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -26,6 +26,7 @@ public:
26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
29 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
29 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 30 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; 31 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
31 32
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 083b283b0..0a3b3951e 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -11,32 +11,77 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/settings.h"
14#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
16 17
18class RasterizerCacheObject {
19public:
20 /// Gets the address of the shader in guest memory, required for cache management
21 virtual VAddr GetAddr() const = 0;
22
23 /// Gets the size of the shader in guest memory, required for cache management
24 virtual std::size_t GetSizeInBytes() const = 0;
25
26 /// Wriets any cached resources back to memory
27 virtual void Flush() = 0;
28
29 /// Sets whether the cached object should be considered registered
30 void SetIsRegistered(bool registered) {
31 is_registered = registered;
32 }
33
34 /// Returns true if the cached object is registered
35 bool IsRegistered() const {
36 return is_registered;
37 }
38
39 /// Returns true if the cached object is dirty
40 bool IsDirty() const {
41 return is_dirty;
42 }
43
44 /// Returns ticks from when this cached object was last modified
45 u64 GetLastModifiedTicks() const {
46 return last_modified_ticks;
47 }
48
49 /// Marks an object as recently modified, used to specify whether it is clean or dirty
50 template <class T>
51 void MarkAsModified(bool dirty, T& cache) {
52 is_dirty = dirty;
53 last_modified_ticks = cache.GetModifiedTicks();
54 }
55
56private:
57 bool is_registered{}; ///< Whether the object is currently registered with the cache
58 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
59 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
60};
61
17template <class T> 62template <class T>
18class RasterizerCache : NonCopyable { 63class RasterizerCache : NonCopyable {
64 friend class RasterizerCacheObject;
65
19public: 66public:
67 /// Write any cached resources overlapping the specified region back to memory
68 void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
69 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
70 for (auto& object : objects) {
71 FlushObject(object);
72 }
73 }
74
20 /// Mark the specified region as being invalidated 75 /// Mark the specified region as being invalidated
21 void InvalidateRegion(VAddr addr, u64 size) { 76 void InvalidateRegion(VAddr addr, u64 size) {
22 if (size == 0) 77 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
23 return; 78 for (auto& object : objects) {
24 79 if (!object->IsRegistered()) {
25 const ObjectInterval interval{addr, addr + size}; 80 // Skip duplicates
26 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 81 continue;
27 for (auto& cached_object : pair.second) {
28 if (!cached_object)
29 continue;
30
31 remove_objects.emplace(cached_object);
32 } 82 }
83 Unregister(object);
33 } 84 }
34
35 for (auto& remove_object : remove_objects) {
36 Unregister(remove_object);
37 }
38
39 remove_objects.clear();
40 } 85 }
41 86
42 /// Invalidates everything in the cache 87 /// Invalidates everything in the cache
@@ -62,6 +107,7 @@ protected:
62 107
63 /// Register an object into the cache 108 /// Register an object into the cache
64 void Register(const T& object) { 109 void Register(const T& object) {
110 object->SetIsRegistered(true);
65 object_cache.add({GetInterval(object), ObjectSet{object}}); 111 object_cache.add({GetInterval(object), ObjectSet{object}});
66 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 112 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
67 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 113 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
@@ -69,12 +115,57 @@ protected:
69 115
70 /// Unregisters an object from the cache 116 /// Unregisters an object from the cache
71 void Unregister(const T& object) { 117 void Unregister(const T& object) {
118 object->SetIsRegistered(false);
72 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 119 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
73 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); 120 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
121
122 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
123 if (Settings::values.use_accurate_gpu_emulation) {
124 FlushObject(object);
125 }
126
74 object_cache.subtract({GetInterval(object), ObjectSet{object}}); 127 object_cache.subtract({GetInterval(object), ObjectSet{object}});
75 } 128 }
76 129
130 /// Returns a ticks counter used for tracking when cached objects were last modified
131 u64 GetModifiedTicks() {
132 return ++modified_ticks;
133 }
134
77private: 135private:
136 /// Returns a list of cached objects from the specified memory region, ordered by access time
137 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
138 if (size == 0) {
139 return {};
140 }
141
142 std::vector<T> objects;
143 const ObjectInterval interval{addr, addr + size};
144 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
145 for (auto& cached_object : pair.second) {
146 if (!cached_object) {
147 continue;
148 }
149 objects.push_back(cached_object);
150 }
151 }
152
153 std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
154 return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
155 });
156
157 return objects;
158 }
159
160 /// Flushes the specified object, updating appropriate cache state as needed
161 void FlushObject(const T& object) {
162 if (!object->IsDirty()) {
163 return;
164 }
165 object->Flush();
166 object->MarkAsModified(false, *this);
167 }
168
78 using ObjectSet = std::set<T>; 169 using ObjectSet = std::set<T>;
79 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; 170 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
80 using ObjectInterval = typename ObjectCache::interval_type; 171 using ObjectInterval = typename ObjectCache::interval_type;
@@ -84,6 +175,6 @@ private:
84 object->GetAddr() + object->GetSizeInBytes()); 175 object->GetAddr() + object->GetSizeInBytes());
85 } 176 }
86 177
87 ObjectCache object_cache; 178 ObjectCache object_cache; ///< Cache of objects
88 ObjectSet remove_objects; 179 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
89}; 180};
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 965976334..be29dc8be 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -15,15 +15,18 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18struct CachedBufferEntry final { 18struct CachedBufferEntry final : public RasterizerCacheObject {
19 VAddr GetAddr() const { 19 VAddr GetAddr() const override {
20 return addr; 20 return addr;
21 } 21 }
22 22
23 std::size_t GetSizeInBytes() const { 23 std::size_t GetSizeInBytes() const override {
24 return size; 24 return size;
25 } 25 }
26 26
27 // We do not have to flush this cache as things in it are never modified by us.
28 void Flush() override {}
29
27 VAddr addr; 30 VAddr addr;
28 std::size_t size; 31 std::size_t size;
29 GLintptr offset; 32 GLintptr offset;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8d5f277e2..468253033 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -424,6 +424,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
424 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer 424 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
425 Surface color_surface = 425 Surface color_surface =
426 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents); 426 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents);
427
428 if (color_surface) {
429 // Assume that a surface will be written to if it is used as a framebuffer, even if
430 // the shader doesn't actually write to it.
431 color_surface->MarkAsModified(true, res_cache);
432 }
433
427 glFramebufferTexture2D( 434 glFramebufferTexture2D(
428 GL_DRAW_FRAMEBUFFER, 435 GL_DRAW_FRAMEBUFFER,
429 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, 436 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D,
@@ -434,6 +441,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
434 std::array<GLenum, Maxwell::NumRenderTargets> buffers; 441 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
435 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 442 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
436 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); 443 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
444
445 if (color_surface) {
446 // Assume that a surface will be written to if it is used as a framebuffer, even
447 // if the shader doesn't actually write to it.
448 color_surface->MarkAsModified(true, res_cache);
449 }
450
437 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 451 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
438 glFramebufferTexture2D( 452 glFramebufferTexture2D(
439 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), 453 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
@@ -453,6 +467,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
453 } 467 }
454 468
455 if (depth_surface) { 469 if (depth_surface) {
470 // Assume that a surface will be written to if it is used as a framebuffer, even if
471 // the shader doesn't actually write to it.
472 depth_surface->MarkAsModified(true, res_cache);
473
456 if (regs.stencil_enable) { 474 if (regs.stencil_enable) {
457 // Attach both depth and stencil 475 // Attach both depth and stencil
458 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 476 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
@@ -617,7 +635,14 @@ void RasterizerOpenGL::DrawArrays() {
617 635
618void RasterizerOpenGL::FlushAll() {} 636void RasterizerOpenGL::FlushAll() {}
619 637
620void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {} 638void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
639 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
640
641 if (Settings::values.use_accurate_gpu_emulation) {
642 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
643 res_cache.FlushRegion(addr, size);
644 }
645}
621 646
622void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 647void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
623 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 648 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -627,6 +652,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
627} 652}
628 653
629void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 654void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
655 FlushRegion(addr, size);
630 InvalidateRegion(addr, size); 656 InvalidateRegion(addr, size);
631} 657}
632 658
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 801d45144..9c8925383 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -34,16 +34,53 @@ struct FormatTuple {
34 bool compressed; 34 bool compressed;
35}; 35};
36 36
37static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { 37static bool IsPixelFormatASTC(PixelFormat format) {
38 auto& gpu{Core::System::GetInstance().GPU()}; 38 switch (format) {
39 const auto cpu_addr{gpu.MemoryManager().GpuToCpuAddress(gpu_addr)}; 39 case PixelFormat::ASTC_2D_4X4:
40 return cpu_addr ? *cpu_addr : 0; 40 case PixelFormat::ASTC_2D_5X4:
41 case PixelFormat::ASTC_2D_8X8:
42 case PixelFormat::ASTC_2D_8X5:
43 return true;
44 default:
45 return false;
46 }
47}
48
49static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
50 switch (format) {
51 case PixelFormat::ASTC_2D_4X4:
52 return {4, 4};
53 case PixelFormat::ASTC_2D_5X4:
54 return {5, 4};
55 case PixelFormat::ASTC_2D_8X8:
56 return {8, 8};
57 case PixelFormat::ASTC_2D_8X5:
58 return {8, 5};
59 default:
60 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
61 UNREACHABLE();
62 }
63}
64
65void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
66 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
67 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
68
69 addr = cpu_addr ? *cpu_addr : 0;
70 gpu_addr = gpu_addr_;
71 size_in_bytes = SizeInBytesRaw();
72
73 if (IsPixelFormatASTC(pixel_format)) {
74 // ASTC is uncompressed in software, in emulated as RGBA8
75 size_in_bytes_gl = width * height * depth * 4;
76 } else {
77 size_in_bytes_gl = SizeInBytesGL();
78 }
41} 79}
42 80
43/*static*/ SurfaceParams SurfaceParams::CreateForTexture( 81/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
44 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { 82 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
45 SurfaceParams params{}; 83 SurfaceParams params{};
46 params.addr = TryGetCpuAddr(config.tic.Address());
47 params.is_tiled = config.tic.IsTiled(); 84 params.is_tiled = config.tic.IsTiled();
48 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, 85 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
49 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 86 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
@@ -87,18 +124,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
87 break; 124 break;
88 } 125 }
89 126
90 params.size_in_bytes_total = params.SizeInBytesTotal();
91 params.size_in_bytes_2d = params.SizeInBytes2D();
92 params.max_mip_level = config.tic.max_mip_level + 1; 127 params.max_mip_level = config.tic.max_mip_level + 1;
93 params.rt = {}; 128 params.rt = {};
94 129
130 params.InitCacheParameters(config.tic.Address());
131
95 return params; 132 return params;
96} 133}
97 134
98/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { 135/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {
99 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 136 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
100 SurfaceParams params{}; 137 SurfaceParams params{};
101 params.addr = TryGetCpuAddr(config.Address()); 138
102 params.is_tiled = 139 params.is_tiled =
103 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 140 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
104 params.block_width = 1 << config.memory_layout.block_width; 141 params.block_width = 1 << config.memory_layout.block_width;
@@ -112,16 +149,17 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
112 params.unaligned_height = config.height; 149 params.unaligned_height = config.height;
113 params.target = SurfaceTarget::Texture2D; 150 params.target = SurfaceTarget::Texture2D;
114 params.depth = 1; 151 params.depth = 1;
115 params.size_in_bytes_total = params.SizeInBytesTotal();
116 params.size_in_bytes_2d = params.SizeInBytes2D();
117 params.max_mip_level = 0; 152 params.max_mip_level = 0;
118 153
119 // Render target specific parameters, not used for caching 154 // Render target specific parameters, not used for caching
120 params.rt.index = static_cast<u32>(index); 155 params.rt.index = static_cast<u32>(index);
121 params.rt.array_mode = config.array_mode; 156 params.rt.array_mode = config.array_mode;
122 params.rt.layer_stride = config.layer_stride; 157 params.rt.layer_stride = config.layer_stride;
158 params.rt.volume = config.volume;
123 params.rt.base_layer = config.base_layer; 159 params.rt.base_layer = config.base_layer;
124 160
161 params.InitCacheParameters(config.Address());
162
125 return params; 163 return params;
126} 164}
127 165
@@ -130,7 +168,7 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
130 u32 block_width, u32 block_height, u32 block_depth, 168 u32 block_width, u32 block_height, u32 block_depth,
131 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { 169 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
132 SurfaceParams params{}; 170 SurfaceParams params{};
133 params.addr = TryGetCpuAddr(zeta_address); 171
134 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 172 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
135 params.block_width = 1 << std::min(block_width, 5U); 173 params.block_width = 1 << std::min(block_width, 5U);
136 params.block_height = 1 << std::min(block_height, 5U); 174 params.block_height = 1 << std::min(block_height, 5U);
@@ -143,18 +181,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
143 params.unaligned_height = zeta_height; 181 params.unaligned_height = zeta_height;
144 params.target = SurfaceTarget::Texture2D; 182 params.target = SurfaceTarget::Texture2D;
145 params.depth = 1; 183 params.depth = 1;
146 params.size_in_bytes_total = params.SizeInBytesTotal();
147 params.size_in_bytes_2d = params.SizeInBytes2D();
148 params.max_mip_level = 0; 184 params.max_mip_level = 0;
149 params.rt = {}; 185 params.rt = {};
150 186
187 params.InitCacheParameters(zeta_address);
188
151 return params; 189 return params;
152} 190}
153 191
154/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface( 192/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
155 const Tegra::Engines::Fermi2D::Regs::Surface& config) { 193 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
156 SurfaceParams params{}; 194 SurfaceParams params{};
157 params.addr = TryGetCpuAddr(config.Address()); 195
158 params.is_tiled = !config.linear; 196 params.is_tiled = !config.linear;
159 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, 197 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
160 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, 198 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
@@ -167,11 +205,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
167 params.unaligned_height = config.height; 205 params.unaligned_height = config.height;
168 params.target = SurfaceTarget::Texture2D; 206 params.target = SurfaceTarget::Texture2D;
169 params.depth = 1; 207 params.depth = 1;
170 params.size_in_bytes_total = params.SizeInBytesTotal();
171 params.size_in_bytes_2d = params.SizeInBytes2D();
172 params.max_mip_level = 0; 208 params.max_mip_level = 0;
173 params.rt = {}; 209 params.rt = {};
174 210
211 params.InitCacheParameters(config.Address());
212
175 return params; 213 return params;
176} 214}
177 215
@@ -276,34 +314,6 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
276 return format; 314 return format;
277} 315}
278 316
279static bool IsPixelFormatASTC(PixelFormat format) {
280 switch (format) {
281 case PixelFormat::ASTC_2D_4X4:
282 case PixelFormat::ASTC_2D_5X4:
283 case PixelFormat::ASTC_2D_8X8:
284 case PixelFormat::ASTC_2D_8X5:
285 return true;
286 default:
287 return false;
288 }
289}
290
291static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
292 switch (format) {
293 case PixelFormat::ASTC_2D_4X4:
294 return {4, 4};
295 case PixelFormat::ASTC_2D_5X4:
296 return {5, 4};
297 case PixelFormat::ASTC_2D_8X8:
298 return {8, 8};
299 case PixelFormat::ASTC_2D_8X5:
300 return {8, 5};
301 default:
302 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
303 UNREACHABLE();
304 }
305}
306
307MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { 317MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
308 u32 actual_height{unaligned_height}; 318 u32 actual_height{unaligned_height};
309 if (IsPixelFormatASTC(pixel_format)) { 319 if (IsPixelFormatASTC(pixel_format)) {
@@ -333,23 +343,21 @@ static bool IsFormatBCn(PixelFormat format) {
333template <bool morton_to_gl, PixelFormat format> 343template <bool morton_to_gl, PixelFormat format>
334void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer, 344void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
335 std::size_t gl_buffer_size, VAddr addr) { 345 std::size_t gl_buffer_size, VAddr addr) {
336 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 346 constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format);
337 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 347
348 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
349 // pixel values.
350 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
338 351
339 if (morton_to_gl) { 352 if (morton_to_gl) {
340 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
341 // pixel values.
342 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
343 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 353 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
344 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth); 354 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
345 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 355 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
346 memcpy(gl_buffer, data.data(), size_to_copy); 356 memcpy(gl_buffer, data.data(), size_to_copy);
347 } else { 357 } else {
348 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 358 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth,
349 // check the configuration for this and perform more generic un/swizzle 359 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
350 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 360 gl_buffer, false, block_height, block_depth);
351 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
352 Memory::GetPointer(addr), gl_buffer, morton_to_gl);
353 } 361 }
354} 362}
355 363
@@ -430,17 +438,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
430 MortonCopy<false, PixelFormat::RGBA16UI>, 438 MortonCopy<false, PixelFormat::RGBA16UI>,
431 MortonCopy<false, PixelFormat::R11FG11FB10F>, 439 MortonCopy<false, PixelFormat::R11FG11FB10F>,
432 MortonCopy<false, PixelFormat::RGBA32UI>, 440 MortonCopy<false, PixelFormat::RGBA32UI>,
433 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4 441 MortonCopy<false, PixelFormat::DXT1>,
434 // formats are not supported 442 MortonCopy<false, PixelFormat::DXT23>,
435 nullptr, 443 MortonCopy<false, PixelFormat::DXT45>,
436 nullptr, 444 MortonCopy<false, PixelFormat::DXN1>,
437 nullptr, 445 MortonCopy<false, PixelFormat::DXN2UNORM>,
438 nullptr, 446 MortonCopy<false, PixelFormat::DXN2SNORM>,
439 nullptr, 447 MortonCopy<false, PixelFormat::BC7U>,
440 nullptr, 448 MortonCopy<false, PixelFormat::BC6H_UF16>,
441 nullptr, 449 MortonCopy<false, PixelFormat::BC6H_SF16>,
442 nullptr, 450 // TODO(Subv): Swizzling ASTC formats are not supported
443 nullptr,
444 nullptr, 451 nullptr,
445 MortonCopy<false, PixelFormat::G8R8U>, 452 MortonCopy<false, PixelFormat::G8R8U>,
446 MortonCopy<false, PixelFormat::G8R8S>, 453 MortonCopy<false, PixelFormat::G8R8S>,
@@ -626,22 +633,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
626 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 633 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
627 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 634 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
628 635
629 std::size_t buffer_size = 636 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
630 std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total);
631 637
632 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 638 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
633 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 639 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
634 if (source_format.compressed) { 640 if (source_format.compressed) {
635 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 641 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
636 static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr); 642 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
637 } else { 643 } else {
638 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format, 644 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format,
639 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total), 645 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes),
640 nullptr); 646 nullptr);
641 } 647 }
642 // If the new texture is bigger than the previous one, we need to fill in the rest with data 648 // If the new texture is bigger than the previous one, we need to fill in the rest with data
643 // from the CPU. 649 // from the CPU.
644 if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) { 650 if (src_params.size_in_bytes < dst_params.size_in_bytes) {
645 // Upload the rest of the memory. 651 // Upload the rest of the memory.
646 if (dst_params.is_tiled) { 652 if (dst_params.is_tiled) {
647 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest 653 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
@@ -651,12 +657,12 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
651 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 657 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
652 "reinterpretation but the texture is tiled."); 658 "reinterpretation but the texture is tiled.");
653 } 659 }
654 std::size_t remaining_size = 660 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
655 dst_params.size_in_bytes_total - src_params.size_in_bytes_total;
656 std::vector<u8> data(remaining_size); 661 std::vector<u8> data(remaining_size);
657 Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(), 662 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
658 data.size()); 663 data.size());
659 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size, 664
665 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
660 data.data()); 666 data.data());
661 } 667 }
662 668
@@ -702,7 +708,8 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
702} 708}
703 709
704CachedSurface::CachedSurface(const SurfaceParams& params) 710CachedSurface::CachedSurface(const SurfaceParams& params)
705 : params(params), gl_target(SurfaceTargetToGL(params.target)) { 711 : params(params), gl_target(SurfaceTargetToGL(params.target)),
712 cached_size_in_bytes(params.size_in_bytes) {
706 texture.Create(); 713 texture.Create();
707 const auto& rect{params.GetRect()}; 714 const auto& rect{params.GetRect()};
708 715
@@ -752,9 +759,21 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
752 759
753 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, 760 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
754 SurfaceParams::SurfaceTargetName(params.target)); 761 SurfaceParams::SurfaceTargetName(params.target));
762
763 // Clamp size to mapped GPU memory region
764 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
765 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
766 // check is necessary to prevent flushing from overwriting unmapped memory.
767
768 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
769 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
770 if (cached_size_in_bytes > max_size) {
771 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
772 cached_size_in_bytes = max_size;
773 }
755} 774}
756 775
757static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 776static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
758 union S8Z24 { 777 union S8Z24 {
759 BitField<0, 24, u32> z24; 778 BitField<0, 24, u32> z24;
760 BitField<24, 8, u32> s8; 779 BitField<24, 8, u32> s8;
@@ -767,22 +786,29 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
767 }; 786 };
768 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); 787 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
769 788
770 S8Z24 input_pixel{}; 789 S8Z24 s8z24_pixel{};
771 Z24S8 output_pixel{}; 790 Z24S8 z24s8_pixel{};
772 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; 791 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)};
773 for (std::size_t y = 0; y < height; ++y) { 792 for (std::size_t y = 0; y < height; ++y) {
774 for (std::size_t x = 0; x < width; ++x) { 793 for (std::size_t x = 0; x < width; ++x) {
775 const std::size_t offset{bpp * (y * width + x)}; 794 const std::size_t offset{bpp * (y * width + x)};
776 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 795 if (reverse) {
777 output_pixel.s8.Assign(input_pixel.s8); 796 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
778 output_pixel.z24.Assign(input_pixel.z24); 797 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
779 std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); 798 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
799 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
800 } else {
801 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
802 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
803 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
804 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
805 }
780 } 806 }
781 } 807 }
782} 808}
783 809
784static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 810static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
785 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; 811 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)};
786 for (std::size_t y = 0; y < height; ++y) { 812 for (std::size_t y = 0; y < height; ++y) {
787 for (std::size_t x = 0; x < width; ++x) { 813 for (std::size_t x = 0; x < width; ++x) {
788 const std::size_t offset{bpp * (y * width + x)}; 814 const std::size_t offset{bpp * (y * width + x)};
@@ -814,7 +840,7 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
814 } 840 }
815 case PixelFormat::S8Z24: 841 case PixelFormat::S8Z24:
816 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. 842 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
817 ConvertS8Z24ToZ24S8(data, width, height); 843 ConvertS8Z24ToZ24S8(data, width, height, false);
818 break; 844 break;
819 845
820 case PixelFormat::G8R8U: 846 case PixelFormat::G8R8U:
@@ -825,22 +851,36 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
825 } 851 }
826} 852}
827 853
854/**
855 * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
856 * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
857 * with typical desktop GPUs.
858 */
859static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
860 u32 width, u32 height) {
861 switch (pixel_format) {
862 case PixelFormat::G8R8U:
863 case PixelFormat::G8R8S:
864 case PixelFormat::ASTC_2D_4X4:
865 case PixelFormat::ASTC_2D_8X8: {
866 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
867 static_cast<u32>(pixel_format));
868 UNREACHABLE();
869 break;
870 }
871 case PixelFormat::S8Z24:
872 // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
873 ConvertS8Z24ToZ24S8(data, width, height, true);
874 break;
875 }
876}
877
828MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 878MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
829void CachedSurface::LoadGLBuffer() { 879void CachedSurface::LoadGLBuffer() {
830 ASSERT(params.type != SurfaceType::Fill);
831
832 const u8* const texture_src_data = Memory::GetPointer(params.addr);
833
834 ASSERT(texture_src_data);
835
836 const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
837 const u32 copy_size = params.width * params.height * bytes_per_pixel;
838 const std::size_t total_size = copy_size * params.depth;
839
840 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 880 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
841 881
882 gl_buffer.resize(params.size_in_bytes_gl);
842 if (params.is_tiled) { 883 if (params.is_tiled) {
843 gl_buffer.resize(total_size);
844 u32 depth = params.depth; 884 u32 depth = params.depth;
845 u32 block_depth = params.block_depth; 885 u32 block_depth = params.block_depth;
846 886
@@ -853,13 +893,12 @@ void CachedSurface::LoadGLBuffer() {
853 block_depth = 1U; 893 block_depth = 1U;
854 } 894 }
855 895
856 const std::size_t size = copy_size * depth;
857
858 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( 896 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
859 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), 897 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
860 size, params.addr); 898 gl_buffer.size(), params.addr);
861 } else { 899 } else {
862 const u8* const texture_src_data_end{texture_src_data + total_size}; 900 const auto texture_src_data{Memory::GetPointer(params.addr)};
901 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
863 gl_buffer.assign(texture_src_data, texture_src_data_end); 902 gl_buffer.assign(texture_src_data, texture_src_data_end);
864 } 903 }
865 904
@@ -868,7 +907,44 @@ void CachedSurface::LoadGLBuffer() {
868 907
869MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 908MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
870void CachedSurface::FlushGLBuffer() { 909void CachedSurface::FlushGLBuffer() {
871 ASSERT_MSG(false, "Unimplemented"); 910 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
911
912 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
913
914 // OpenGL temporary buffer needs to be big enough to store raw texture size
915 gl_buffer.resize(GetSizeInBytes());
916
917 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
918 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
919 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
920 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
921 ASSERT(!tuple.compressed);
922 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
923 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(),
924 gl_buffer.data());
925 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
926 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
927 params.height);
928 ASSERT(params.type != SurfaceType::Fill);
929 const u8* const texture_src_data = Memory::GetPointer(params.addr);
930 ASSERT(texture_src_data);
931 if (params.is_tiled) {
932 u32 depth = params.depth;
933 u32 block_depth = params.block_depth;
934
935 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
936 params.block_width, static_cast<u32>(params.target));
937
938 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
939 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
940 depth = 1U;
941 }
942 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
943 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
944 gl_buffer.size(), GetAddr());
945 } else {
946 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
947 }
872} 948}
873 949
874MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 950MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -878,9 +954,6 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
878 954
879 MICROPROFILE_SCOPE(OpenGL_TextureUL); 955 MICROPROFILE_SCOPE(OpenGL_TextureUL);
880 956
881 ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height *
882 GetGLBytesPerPixel(params.pixel_format) * params.depth);
883
884 const auto& rect{params.GetRect()}; 957 const auto& rect{params.GetRect()};
885 958
886 // Load data from memory to the surface 959 // Load data from memory to the surface
@@ -889,7 +962,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
889 std::size_t buffer_offset = 962 std::size_t buffer_offset =
890 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + 963 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +
891 static_cast<std::size_t>(x0)) * 964 static_cast<std::size_t>(x0)) *
892 GetGLBytesPerPixel(params.pixel_format); 965 SurfaceParams::GetBytesPerPixel(params.pixel_format);
893 966
894 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 967 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
895 const GLuint target_tex = texture.handle; 968 const GLuint target_tex = texture.handle;
@@ -905,7 +978,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
905 cur_state.Apply(); 978 cur_state.Apply();
906 979
907 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 980 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
908 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); 981 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
909 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 982 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width));
910 983
911 glActiveTexture(GL_TEXTURE0); 984 glActiveTexture(GL_TEXTURE0);
@@ -915,7 +988,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
915 glCompressedTexImage2D( 988 glCompressedTexImage2D(
916 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 989 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
917 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, 990 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
918 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 991 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
919 break; 992 break;
920 case SurfaceParams::SurfaceTarget::Texture3D: 993 case SurfaceParams::SurfaceTarget::Texture3D:
921 case SurfaceParams::SurfaceTarget::Texture2DArray: 994 case SurfaceParams::SurfaceTarget::Texture2DArray:
@@ -923,16 +996,16 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
923 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 996 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
924 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 997 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
925 static_cast<GLsizei>(params.depth), 0, 998 static_cast<GLsizei>(params.depth), 0,
926 static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]); 999 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
927 break; 1000 break;
928 case SurfaceParams::SurfaceTarget::TextureCubemap: 1001 case SurfaceParams::SurfaceTarget::TextureCubemap:
929 for (std::size_t face = 0; face < params.depth; ++face) { 1002 for (std::size_t face = 0; face < params.depth; ++face) {
930 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 1003 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
931 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1004 0, tuple.internal_format, static_cast<GLsizei>(params.width),
932 static_cast<GLsizei>(params.height), 0, 1005 static_cast<GLsizei>(params.height), 0,
933 static_cast<GLsizei>(params.size_in_bytes_2d), 1006 static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()),
934 &gl_buffer[buffer_offset]); 1007 &gl_buffer[buffer_offset]);
935 buffer_offset += params.size_in_bytes_2d; 1008 buffer_offset += params.SizeInBytesCubeFace();
936 } 1009 }
937 break; 1010 break;
938 default: 1011 default:
@@ -942,7 +1015,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
942 glCompressedTexImage2D( 1015 glCompressedTexImage2D(
943 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1016 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
944 static_cast<GLsizei>(params.height), 0, 1017 static_cast<GLsizei>(params.height), 0,
945 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 1018 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
946 } 1019 }
947 } else { 1020 } else {
948 1021
@@ -971,7 +1044,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
971 y0, static_cast<GLsizei>(rect.GetWidth()), 1044 y0, static_cast<GLsizei>(rect.GetWidth()),
972 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1045 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
973 &gl_buffer[buffer_offset]); 1046 &gl_buffer[buffer_offset]);
974 buffer_offset += params.size_in_bytes_2d; 1047 buffer_offset += params.SizeInBytesCubeFace();
975 } 1048 }
976 break; 1049 break;
977 default: 1050 default:
@@ -1033,10 +1106,7 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre
1033void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { 1106void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
1034 surface->LoadGLBuffer(); 1107 surface->LoadGLBuffer();
1035 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); 1108 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
1036} 1109 surface->MarkAsModified(false, *this);
1037
1038void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
1039 surface->FlushGLBuffer();
1040} 1110}
1041 1111
1042Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 1112Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
@@ -1053,8 +1123,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
1053 } else if (preserve_contents) { 1123 } else if (preserve_contents) {
1054 // If surface parameters changed and we care about keeping the previous data, recreate 1124 // If surface parameters changed and we care about keeping the previous data, recreate
1055 // the surface from the old one 1125 // the surface from the old one
1056 Unregister(surface);
1057 Surface new_surface{RecreateSurface(surface, params)}; 1126 Surface new_surface{RecreateSurface(surface, params)};
1127 Unregister(surface);
1058 Register(new_surface); 1128 Register(new_surface);
1059 return new_surface; 1129 return new_surface;
1060 } else { 1130 } else {
@@ -1105,6 +1175,14 @@ void RasterizerCacheOpenGL::FermiCopySurface(
1105 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false)); 1175 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
1106} 1176}
1107 1177
1178void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1179 const Surface& dst_surface) {
1180 const auto& src_params{src_surface->GetSurfaceParams()};
1181 const auto& dst_params{dst_surface->GetSurfaceParams()};
1182 FlushRegion(src_params.addr, dst_params.size_in_bytes);
1183 LoadSurface(dst_surface);
1184}
1185
1108Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, 1186Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1109 const SurfaceParams& new_params) { 1187 const SurfaceParams& new_params) {
1110 // Verify surface is compatible for blitting 1188 // Verify surface is compatible for blitting
@@ -1113,6 +1191,12 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1113 // Get a new surface with the new parameters, and blit the previous surface to it 1191 // Get a new surface with the new parameters, and blit the previous surface to it
1114 Surface new_surface{GetUncachedSurface(new_params)}; 1192 Surface new_surface{GetUncachedSurface(new_params)};
1115 1193
1194 // With use_accurate_gpu_emulation enabled, do an accurate surface copy
1195 if (Settings::values.use_accurate_gpu_emulation) {
1196 AccurateCopySurface(old_surface, new_surface);
1197 return new_surface;
1198 }
1199
1116 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1200 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1117 if (old_params.target == new_params.target && old_params.type == new_params.type && 1201 if (old_params.target == new_params.target && old_params.type == new_params.type &&
1118 old_params.depth == new_params.depth && old_params.depth == 1 && 1202 old_params.depth == new_params.depth && old_params.depth == 1 &&
@@ -1124,11 +1208,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1124 1208
1125 // If the format is the same, just do a framebuffer blit. This is significantly faster than 1209 // If the format is the same, just do a framebuffer blit. This is significantly faster than
1126 // using PBOs. The is also likely less accurate, as textures will be converted rather than 1210 // using PBOs. The is also likely less accurate, as textures will be converted rather than
1127 // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate 1211 // reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate
1128 // surface copy, where pixels are reinterpreted as a new format (without conversion). This 1212 // surface copy, where pixels are reinterpreted as a new format (without conversion). This
1129 // code path uses OpenGL PBOs and is quite slow. 1213 // code path uses OpenGL PBOs and is quite slow.
1130 const bool is_blit{old_params.pixel_format == new_params.pixel_format || 1214 const bool is_blit{old_params.pixel_format == new_params.pixel_format};
1131 !Settings::values.use_accurate_framebuffers};
1132 1215
1133 switch (new_params.target) { 1216 switch (new_params.target) {
1134 case SurfaceParams::SurfaceTarget::Texture2D: 1217 case SurfaceParams::SurfaceTarget::Texture2D:
@@ -1138,6 +1221,9 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1138 CopySurface(old_surface, new_surface, copy_pbo.handle); 1221 CopySurface(old_surface, new_surface, copy_pbo.handle);
1139 } 1222 }
1140 break; 1223 break;
1224 case SurfaceParams::SurfaceTarget::Texture3D:
1225 AccurateCopySurface(old_surface, new_surface);
1226 break;
1141 case SurfaceParams::SurfaceTarget::TextureCubemap: { 1227 case SurfaceParams::SurfaceTarget::TextureCubemap: {
1142 if (old_params.rt.array_mode != 1) { 1228 if (old_params.rt.array_mode != 1) {
1143 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this 1229 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0b8ae3eb4..0dd0d90a3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -18,6 +18,7 @@
18#include "video_core/rasterizer_cache.h" 18#include "video_core/rasterizer_cache.h"
19#include "video_core/renderer_opengl/gl_resource_manager.h" 19#include "video_core/renderer_opengl/gl_resource_manager.h"
20#include "video_core/renderer_opengl/gl_shader_gen.h" 20#include "video_core/renderer_opengl/gl_shader_gen.h"
21#include "video_core/textures/decoders.h"
21#include "video_core/textures/texture.h" 22#include "video_core/textures/texture.h"
22 23
23namespace OpenGL { 24namespace OpenGL {
@@ -131,6 +132,8 @@ struct SurfaceParams {
131 case Tegra::Texture::TextureType::Texture2D: 132 case Tegra::Texture::TextureType::Texture2D:
132 case Tegra::Texture::TextureType::Texture2DNoMipmap: 133 case Tegra::Texture::TextureType::Texture2DNoMipmap:
133 return SurfaceTarget::Texture2D; 134 return SurfaceTarget::Texture2D;
135 case Tegra::Texture::TextureType::Texture3D:
136 return SurfaceTarget::Texture3D;
134 case Tegra::Texture::TextureType::TextureCubemap: 137 case Tegra::Texture::TextureType::TextureCubemap:
135 return SurfaceTarget::TextureCubemap; 138 return SurfaceTarget::TextureCubemap;
136 case Tegra::Texture::TextureType::Texture1DArray: 139 case Tegra::Texture::TextureType::Texture1DArray:
@@ -701,21 +704,42 @@ struct SurfaceParams {
701 return SurfaceType::Invalid; 704 return SurfaceType::Invalid;
702 } 705 }
703 706
707 /// Returns the sizer in bytes of the specified pixel format
708 static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
709 if (pixel_format == SurfaceParams::PixelFormat::Invalid) {
710 return 0;
711 }
712 return GetFormatBpp(pixel_format) / CHAR_BIT;
713 }
714
704 /// Returns the rectangle corresponding to this surface 715 /// Returns the rectangle corresponding to this surface
705 MathUtil::Rectangle<u32> GetRect() const; 716 MathUtil::Rectangle<u32> GetRect() const;
706 717
707 /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression 718 /// Returns the total size of this surface in bytes, adjusted for compression
708 std::size_t SizeInBytes2D() const { 719 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {
709 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 720 const u32 compression_factor{GetCompressionFactor(pixel_format)};
710 ASSERT(width % compression_factor == 0); 721 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
711 ASSERT(height % compression_factor == 0); 722 const size_t uncompressed_size{
712 return (width / compression_factor) * (height / compression_factor) * 723 Tegra::Texture::CalculateSize((ignore_tiled ? false : is_tiled), bytes_per_pixel, width,
713 GetFormatBpp(pixel_format) / CHAR_BIT; 724 height, depth, block_height, block_depth)};
725
726 // Divide by compression_factor^2, as height and width are factored by this
727 return uncompressed_size / (compression_factor * compression_factor);
714 } 728 }
715 729
716 /// Returns the total size of this surface in bytes, adjusted for compression 730 /// Returns the size of this surface as an OpenGL texture in bytes
717 std::size_t SizeInBytesTotal() const { 731 std::size_t SizeInBytesGL() const {
718 return SizeInBytes2D() * depth; 732 return SizeInBytesRaw(true);
733 }
734
735 /// Returns the size of this surface as a cube face in bytes
736 std::size_t SizeInBytesCubeFace() const {
737 return size_in_bytes / 6;
738 }
739
740 /// Returns the size of this surface as an OpenGL cube face in bytes
741 std::size_t SizeInBytesCubeFaceGL() const {
742 return size_in_bytes_gl / 6;
719 } 743 }
720 744
721 /// Creates SurfaceParams from a texture configuration 745 /// Creates SurfaceParams from a texture configuration
@@ -742,7 +766,9 @@ struct SurfaceParams {
742 other.depth); 766 other.depth);
743 } 767 }
744 768
745 VAddr addr; 769 /// Initializes parameters for caching, should be called after everything has been initialized
770 void InitCacheParameters(Tegra::GPUVAddr gpu_addr);
771
746 bool is_tiled; 772 bool is_tiled;
747 u32 block_width; 773 u32 block_width;
748 u32 block_height; 774 u32 block_height;
@@ -754,15 +780,20 @@ struct SurfaceParams {
754 u32 height; 780 u32 height;
755 u32 depth; 781 u32 depth;
756 u32 unaligned_height; 782 u32 unaligned_height;
757 std::size_t size_in_bytes_total;
758 std::size_t size_in_bytes_2d;
759 SurfaceTarget target; 783 SurfaceTarget target;
760 u32 max_mip_level; 784 u32 max_mip_level;
761 785
786 // Parameters used for caching
787 VAddr addr;
788 Tegra::GPUVAddr gpu_addr;
789 std::size_t size_in_bytes;
790 std::size_t size_in_bytes_gl;
791
762 // Render target specific parameters, not used in caching 792 // Render target specific parameters, not used in caching
763 struct { 793 struct {
764 u32 index; 794 u32 index;
765 u32 array_mode; 795 u32 array_mode;
796 u32 volume;
766 u32 layer_stride; 797 u32 layer_stride;
767 u32 base_layer; 798 u32 base_layer;
768 } rt; 799 } rt;
@@ -775,7 +806,8 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
775 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) { 806 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
776 SurfaceReserveKey res; 807 SurfaceReserveKey res;
777 res.state = params; 808 res.state = params;
778 res.state.rt = {}; // Ignore rt config in caching 809 res.state.gpu_addr = {}; // Ignore GPU vaddr in caching
810 res.state.rt = {}; // Ignore rt config in caching
779 return res; 811 return res;
780 } 812 }
781}; 813};
@@ -790,16 +822,20 @@ struct hash<SurfaceReserveKey> {
790 822
791namespace OpenGL { 823namespace OpenGL {
792 824
793class CachedSurface final { 825class CachedSurface final : public RasterizerCacheObject {
794public: 826public:
795 CachedSurface(const SurfaceParams& params); 827 CachedSurface(const SurfaceParams& params);
796 828
797 VAddr GetAddr() const { 829 VAddr GetAddr() const override {
798 return params.addr; 830 return params.addr;
799 } 831 }
800 832
801 std::size_t GetSizeInBytes() const { 833 std::size_t GetSizeInBytes() const override {
802 return params.size_in_bytes_total; 834 return cached_size_in_bytes;
835 }
836
837 void Flush() override {
838 FlushGLBuffer();
803 } 839 }
804 840
805 const OGLTexture& Texture() const { 841 const OGLTexture& Texture() const {
@@ -810,13 +846,6 @@ public:
810 return gl_target; 846 return gl_target;
811 } 847 }
812 848
813 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
814 if (format == SurfaceParams::PixelFormat::Invalid)
815 return 0;
816
817 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
818 }
819
820 const SurfaceParams& GetSurfaceParams() const { 849 const SurfaceParams& GetSurfaceParams() const {
821 return params; 850 return params;
822 } 851 }
@@ -833,6 +862,7 @@ private:
833 std::vector<u8> gl_buffer; 862 std::vector<u8> gl_buffer;
834 SurfaceParams params; 863 SurfaceParams params;
835 GLenum gl_target; 864 GLenum gl_target;
865 std::size_t cached_size_in_bytes;
836}; 866};
837 867
838class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 868class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -849,9 +879,6 @@ public:
849 /// Get the color surface based on the framebuffer configuration and the specified render target 879 /// Get the color surface based on the framebuffer configuration and the specified render target
850 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 880 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
851 881
852 /// Flushes the surface to Switch memory
853 void FlushSurface(const Surface& surface);
854
855 /// Tries to find a framebuffer using on the provided CPU address 882 /// Tries to find a framebuffer using on the provided CPU address
856 Surface TryFindFramebufferSurface(VAddr addr) const; 883 Surface TryFindFramebufferSurface(VAddr addr) const;
857 884
@@ -875,6 +902,9 @@ private:
875 /// Tries to get a reserved surface for the specified parameters 902 /// Tries to get a reserved surface for the specified parameters
876 Surface TryGetReservedSurface(const SurfaceParams& params); 903 Surface TryGetReservedSurface(const SurfaceParams& params);
877 904
905 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
906 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
907
878 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 908 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
879 /// previously been used. This is to prevent surfaces from being constantly created and 909 /// previously been used. This is to prevent surfaces from being constantly created and
880 /// destroyed when used with different surface parameters. 910 /// destroyed when used with different surface parameters.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 7bb287f56..a210f1731 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -19,20 +19,21 @@ class CachedShader;
19using Shader = std::shared_ptr<CachedShader>; 19using Shader = std::shared_ptr<CachedShader>;
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 21
22class CachedShader final { 22class CachedShader final : public RasterizerCacheObject {
23public: 23public:
24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); 24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type);
25 25
26 /// Gets the address of the shader in guest memory, required for cache management 26 VAddr GetAddr() const override {
27 VAddr GetAddr() const {
28 return addr; 27 return addr;
29 } 28 }
30 29
31 /// Gets the size of the shader in guest memory, required for cache management 30 std::size_t GetSizeInBytes() const override {
32 std::size_t GetSizeInBytes() const {
33 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 31 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
34 } 32 }
35 33
34 // We do not have to flush this cache as things in it are never modified by us.
35 void Flush() override {}
36
36 /// Gets the shader entries for the shader 37 /// Gets the shader entries for the shader
37 const GLShader::ShaderEntries& GetShaderEntries() const { 38 const GLShader::ShaderEntries& GetShaderEntries() const {
38 return entries; 39 return entries;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index ca063d90d..f4340a017 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1142,6 +1142,7 @@ private:
1142 case Tegra::Shader::TextureType::Texture2D: { 1142 case Tegra::Shader::TextureType::Texture2D: {
1143 return 2; 1143 return 2;
1144 } 1144 }
1145 case Tegra::Shader::TextureType::Texture3D:
1145 case Tegra::Shader::TextureType::TextureCube: { 1146 case Tegra::Shader::TextureType::TextureCube: {
1146 return 3; 1147 return 3;
1147 } 1148 }
@@ -2036,9 +2037,9 @@ private:
2036 break; 2037 break;
2037 } 2038 }
2038 case OpCode::Id::TEX: { 2039 case OpCode::Id::TEX: {
2039 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
2040 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; 2040 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
2041 std::string coord; 2041 std::string coord;
2042 const bool is_array = instr.tex.array != 0;
2042 2043
2043 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2044 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2044 "NODEP is not implemented"); 2045 "NODEP is not implemented");
@@ -2053,21 +2054,59 @@ private:
2053 2054
2054 switch (num_coordinates) { 2055 switch (num_coordinates) {
2055 case 1: { 2056 case 1: {
2056 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2057 if (is_array) {
2057 coord = "float coords = " + x + ';'; 2058 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2059 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2060 coord = "vec2 coords = vec2(" + x + ", " + index + ");";
2061 } else {
2062 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2063 coord = "float coords = " + x + ';';
2064 }
2058 break; 2065 break;
2059 } 2066 }
2060 case 2: { 2067 case 2: {
2061 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2068 if (is_array) {
2062 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2069 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2063 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2070 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2071 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2072 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
2073 } else {
2074 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2075 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2076 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2077 }
2064 break; 2078 break;
2065 } 2079 }
2066 case 3: { 2080 case 3: {
2067 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2081 if (depth_compare) {
2068 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2082 if (is_array) {
2069 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2083 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2070 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2084 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2085 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2086 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2087 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2088 ");";
2089 } else {
2090 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2091 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2092 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2093 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2094 }
2095 } else {
2096 if (is_array) {
2097 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2098 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2099 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2100 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3);
2101 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2102 ");";
2103 } else {
2104 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2105 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2106 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2107 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2108 }
2109 }
2071 break; 2110 break;
2072 } 2111 }
2073 default: 2112 default:
@@ -2086,7 +2125,7 @@ private:
2086 std::string op_c; 2125 std::string op_c;
2087 2126
2088 const std::string sampler = 2127 const std::string sampler =
2089 GetSampler(instr.sampler, texture_type, false, depth_compare); 2128 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
2090 // Add an extra scope and declare the texture coords inside to prevent 2129 // Add an extra scope and declare the texture coords inside to prevent
2091 // overwriting them in case they are used as outputs of the texs instruction. 2130 // overwriting them in case they are used as outputs of the texs instruction.
2092 2131
@@ -2106,10 +2145,13 @@ private:
2106 } 2145 }
2107 case Tegra::Shader::TextureProcessMode::LB: 2146 case Tegra::Shader::TextureProcessMode::LB:
2108 case Tegra::Shader::TextureProcessMode::LBA: { 2147 case Tegra::Shader::TextureProcessMode::LBA: {
2109 if (num_coordinates <= 2) { 2148 if (depth_compare) {
2110 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2149 if (is_array)
2150 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2151 else
2152 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2111 } else { 2153 } else {
2112 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2154 op_c = regs.GetRegisterAsFloat(instr.gpr20);
2113 } 2155 }
2114 // TODO: Figure if A suffix changes the equation at all. 2156 // TODO: Figure if A suffix changes the equation at all.
2115 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2157 texture = "texture(" + sampler + ", coords, " + op_c + ')';
@@ -2252,6 +2294,8 @@ private:
2252 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2294 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2253 "MZ is not implemented"); 2295 "MZ is not implemented");
2254 2296
2297 u32 op_c_offset = 0;
2298
2255 switch (texture_type) { 2299 switch (texture_type) {
2256 case Tegra::Shader::TextureType::Texture1D: { 2300 case Tegra::Shader::TextureType::Texture1D: {
2257 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2301 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
@@ -2266,6 +2310,7 @@ private:
2266 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2310 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2267 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2311 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2268 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; 2312 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2313 op_c_offset = 1;
2269 } 2314 }
2270 break; 2315 break;
2271 } 2316 }
@@ -2277,13 +2322,14 @@ private:
2277 const std::string sampler = 2322 const std::string sampler =
2278 GetSampler(instr.sampler, texture_type, is_array, false); 2323 GetSampler(instr.sampler, texture_type, is_array, false);
2279 std::string texture = "texelFetch(" + sampler + ", coords, 0)"; 2324 std::string texture = "texelFetch(" + sampler + ", coords, 0)";
2280 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr20.Value() + 1);
2281 switch (instr.tlds.GetTextureProcessMode()) { 2325 switch (instr.tlds.GetTextureProcessMode()) {
2282 case Tegra::Shader::TextureProcessMode::LZ: { 2326 case Tegra::Shader::TextureProcessMode::LZ: {
2283 texture = "texelFetch(" + sampler + ", coords, 0)"; 2327 texture = "texelFetch(" + sampler + ", coords, 0)";
2284 break; 2328 break;
2285 } 2329 }
2286 case Tegra::Shader::TextureProcessMode::LL: { 2330 case Tegra::Shader::TextureProcessMode::LL: {
2331 const std::string op_c =
2332 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset);
2287 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; 2333 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')';
2288 break; 2334 break;
2289 } 2335 }
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7fec15991..71c6ebb41 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -85,8 +85,8 @@ void Config::ReadValues() {
85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); 86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); 87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt();
88 Settings::values.use_accurate_framebuffers = 88 Settings::values.use_accurate_gpu_emulation =
89 qt_config->value("use_accurate_framebuffers", false).toBool(); 89 qt_config->value("use_accurate_gpu_emulation", false).toBool();
90 90
91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -233,7 +233,7 @@ void Config::SaveValues() {
233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);
235 qt_config->setValue("frame_limit", Settings::values.frame_limit); 235 qt_config->setValue("frame_limit", Settings::values.frame_limit);
236 qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers); 236 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
237 237
238 // Cast to double because Qt's written float values are not human-readable 238 // Cast to double because Qt's written float values are not human-readable
239 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 239 qt_config->setValue("bg_red", (double)Settings::values.bg_red);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index cd1549462..8290b4384 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -75,7 +75,7 @@ void ConfigureGraphics::setConfiguration() {
75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
77 ui->frame_limit->setValue(Settings::values.frame_limit); 77 ui->frame_limit->setValue(Settings::values.frame_limit);
78 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); 78 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
80 Settings::values.bg_blue); 80 Settings::values.bg_blue);
81 ui->bg_button->setStyleSheet( 81 ui->bg_button->setStyleSheet(
@@ -87,7 +87,7 @@ void ConfigureGraphics::applyConfiguration() {
87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
89 Settings::values.frame_limit = ui->frame_limit->value(); 89 Settings::values.frame_limit = ui->frame_limit->value();
90 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); 90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 91 Settings::values.bg_red = static_cast<float>(bg_color.redF());
92 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 92 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 8fc00af1b..91fcad994 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -50,9 +50,9 @@
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_accurate_framebuffers"> 53 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
54 <property name="text"> 54 <property name="text">
55 <string>Use accurate framebuffers (slow)</string> 55 <string>Use accurate GPU emulation (slow)</string>
56 </property> 56 </property>
57 </widget> 57 </widget>
58 </item> 58 </item>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 2470f4640..5e42e48b2 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -99,8 +99,8 @@ void Config::ReadValues() {
99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
100 Settings::values.frame_limit = 100 Settings::values.frame_limit =
101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
102 Settings::values.use_accurate_framebuffers = 102 Settings::values.use_accurate_gpu_emulation =
103 sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false); 103 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
104 104
105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); 105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 762396e3b..a97b75f7b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -110,9 +110,9 @@ use_frame_limit =
110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) 110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
111frame_limit = 111frame_limit =
112 112
113# Whether to use accurate framebuffers 113# Whether to use accurate GPU emulation
114# 0 (default): Off (fast), 1 : On (slow) 114# 0 (default): Off (fast), 1 : On (slow)
115use_accurate_framebuffers = 115use_accurate_gpu_emulation =
116 116
117# The clear color for the renderer. What shows up on the sides of the bottom screen. 117# The clear color for the renderer. What shows up on the sides of the bottom screen.
118# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 118# Must be in range of 0.0-1.0. Defaults to 1.0 for all.