summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.h2
-rw-r--r--src/audio_core/stream.cpp4
-rw-r--r--src/common/bit_field.h19
-rw-r--r--src/common/logging/backend.cpp11
-rw-r--r--src/common/logging/backend.h14
-rw-r--r--src/common/string_util.cpp57
-rw-r--r--src/common/string_util.h33
-rw-r--r--src/core/file_sys/bis_factory.cpp9
-rw-r--r--src/core/file_sys/bis_factory.h4
-rw-r--r--src/core/file_sys/card_image.cpp2
-rw-r--r--src/core/file_sys/card_image.h3
-rw-r--r--src/core/file_sys/content_archive.cpp5
-rw-r--r--src/core/file_sys/content_archive.h3
-rw-r--r--src/core/file_sys/control_metadata.cpp6
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/errors.h25
-rw-r--r--src/core/file_sys/patch_manager.cpp13
-rw-r--r--src/core/file_sys/registered_cache.cpp13
-rw-r--r--src/core/file_sys/registered_cache.h3
-rw-r--r--src/core/file_sys/savedata_factory.cpp32
-rw-r--r--src/core/file_sys/savedata_factory.h3
-rw-r--r--src/core/file_sys/submission_package.cpp2
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/hle/kernel/errors.h74
-rw-r--r--src/core/hle/kernel/process.cpp84
-rw-r--r--src/core/hle/kernel/process.h25
-rw-r--r--src/core/hle/kernel/svc.cpp104
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/thread.cpp48
-rw-r--r--src/core/hle/kernel/thread.h2
-rw-r--r--src/core/hle/kernel/vm_manager.cpp82
-rw-r--r--src/core/hle/kernel/vm_manager.h16
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp24
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp25
-rw-r--r--src/core/hle/service/acc/profile_manager.h13
-rw-r--r--src/core/hle/service/am/am.cpp100
-rw-r--r--src/core/hle/service/am/am.h30
-rw-r--r--src/core/hle/service/am/applet_ae.cpp34
-rw-r--r--src/core/hle/service/am/applet_ae.h6
-rw-r--r--src/core/hle/service/am/applet_oe.cpp21
-rw-r--r--src/core/hle/service/am/applet_oe.h6
-rw-r--r--src/core/hle/service/audio/audren_u.cpp31
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp38
-rw-r--r--src/core/hle/service/btm/btm.cpp108
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp52
-rw-r--r--src/core/hle/service/filesystem/filesystem.h4
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp169
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp64
-rw-r--r--src/core/hle/service/hid/hid.cpp18
-rw-r--r--src/core/hle/service/ldr/ldr.cpp397
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/ns/ns.cpp64
-rw-r--r--src/core/hle/service/ns/pl_u.cpp8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h4
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/spl/module.cpp10
-rw-r--r--src/core/hle/service/spl/module.h4
-rw-r--r--src/core/hle/service/time/interface.cpp5
-rw-r--r--src/core/hle/service/time/time.cpp147
-rw-r--r--src/core/hle/service/time/time.h20
-rw-r--r--src/core/hle/service/vi/vi.cpp22
-rw-r--r--src/core/loader/nro.cpp12
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/settings.h7
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp37
-rw-r--r--src/video_core/engines/maxwell_3d.h51
-rw-r--r--src/video_core/memory_manager.cpp75
-rw-r--r--src/video_core/memory_manager.h8
-rw-r--r--src/video_core/rasterizer_cache.cpp7
-rw-r--r--src/video_core/rasterizer_cache.h44
-rw-r--r--src/video_core/renderer_base.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp242
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h19
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp105
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h8
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp186
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h132
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp32
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp261
-rw-r--r--src/video_core/renderer_opengl/gl_state.h64
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp5
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h66
-rw-r--r--src/video_core/surface.cpp33
-rw-r--r--src/video_core/surface.h118
-rw-r--r--src/video_core/textures/astc.cpp32
-rw-r--r--src/video_core/textures/astc.h2
-rw-r--r--src/video_core/textures/decoders.cpp48
-rw-r--r--src/video_core/textures/decoders.h4
-rw-r--r--src/video_core/textures/texture.h13
-rw-r--r--src/yuzu/configuration/config.cpp16
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui21
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp2
-rw-r--r--src/yuzu/configuration/configure_gamelist.ui223
-rw-r--r--src/yuzu/configuration/configure_general.cpp35
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_system.cpp21
-rw-r--r--src/yuzu/configuration/configure_system.ui228
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp6
-rw-r--r--src/yuzu/game_list.cpp33
-rw-r--r--src/yuzu/game_list_worker.cpp24
-rw-r--r--src/yuzu/main.cpp26
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui7
-rw-r--r--src/yuzu/ui_settings.h1
-rw-r--r--src/yuzu_cmd/config.cpp10
-rw-r--r--src/yuzu_cmd/default_ini.h7
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
123 files changed, 3209 insertions, 1265 deletions
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 046417da3..71ba4be40 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -143,7 +143,7 @@ struct AuxInfo {
143 std::array<u8, 24> output_mix_buffers; 143 std::array<u8, 24> output_mix_buffers;
144 u32_le mix_buffer_count; 144 u32_le mix_buffer_count;
145 u32_le sample_rate; // Stored in the aux buffer currently 145 u32_le sample_rate; // Stored in the aux buffer currently
146 u32_le sampe_count; 146 u32_le sample_count;
147 u64_le send_buffer_info; 147 u64_le send_buffer_info;
148 u64_le send_buffer_base; 148 u64_le send_buffer_base;
149 149
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 742a5e0a0..f35628e45 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -11,7 +11,6 @@
11#include "audio_core/stream.h" 11#include "audio_core/stream.h"
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/microprofile.h"
15#include "core/core_timing.h" 14#include "core/core_timing.h"
16#include "core/core_timing_util.h" 15#include "core/core_timing_util.h"
17#include "core/settings.h" 16#include "core/settings.h"
@@ -104,10 +103,7 @@ void Stream::PlayNextBuffer() {
104 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 103 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
105} 104}
106 105
107MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
108
109void Stream::ReleaseActiveBuffer() { 106void Stream::ReleaseActiveBuffer() {
110 MICROPROFILE_SCOPE(AudioOutput);
111 ASSERT(active_buffer); 107 ASSERT(active_buffer);
112 released_buffers.push(std::move(active_buffer)); 108 released_buffers.push(std::move(active_buffer));
113 release_callback(); 109 release_callback();
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index bf803da8d..21e07925d 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -117,21 +117,21 @@ private:
117 // We don't delete it because we want BitField to be trivially copyable. 117 // We don't delete it because we want BitField to be trivially copyable.
118 constexpr BitField& operator=(const BitField&) = default; 118 constexpr BitField& operator=(const BitField&) = default;
119 119
120 // StorageType is T for non-enum types and the underlying type of T if 120 // UnderlyingType is T for non-enum types and the underlying type of T if
121 // T is an enumeration. Note that T is wrapped within an enable_if in the 121 // T is an enumeration. Note that T is wrapped within an enable_if in the
122 // former case to workaround compile errors which arise when using 122 // former case to workaround compile errors which arise when using
123 // std::underlying_type<T>::type directly. 123 // std::underlying_type<T>::type directly.
124 using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>, 124 using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
125 std::enable_if<true, T>>::type; 125 std::enable_if<true, T>>::type;
126 126
127 // Unsigned version of StorageType 127 // We store the value as the unsigned type to avoid undefined behaviour on value shifting
128 using StorageTypeU = std::make_unsigned_t<StorageType>; 128 using StorageType = std::make_unsigned_t<UnderlyingType>;
129 129
130public: 130public:
131 /// Constants to allow limited introspection of fields if needed 131 /// Constants to allow limited introspection of fields if needed
132 static constexpr std::size_t position = Position; 132 static constexpr std::size_t position = Position;
133 static constexpr std::size_t bits = Bits; 133 static constexpr std::size_t bits = Bits;
134 static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; 134 static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
135 135
136 /** 136 /**
137 * Formats a value by masking and shifting it according to the field parameters. A value 137 * Formats a value by masking and shifting it according to the field parameters. A value
@@ -148,11 +148,12 @@ public:
148 * union in a constexpr context. 148 * union in a constexpr context.
149 */ 149 */
150 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) { 150 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
151 if (std::numeric_limits<T>::is_signed) { 151 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
152 std::size_t shift = 8 * sizeof(T) - bits; 152 std::size_t shift = 8 * sizeof(T) - bits;
153 return (T)((storage << (shift - position)) >> shift); 153 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
154 shift);
154 } else { 155 } else {
155 return (T)((storage & mask) >> position); 156 return static_cast<T>((storage & mask) >> position);
156 } 157 }
157 } 158 }
158 159
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 6d5218465..5753b871a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -12,7 +12,8 @@
12#include <thread> 12#include <thread>
13#include <vector> 13#include <vector>
14#ifdef _WIN32 14#ifdef _WIN32
15#include <share.h> // For _SH_DENYWR 15#include <share.h> // For _SH_DENYWR
16#include <windows.h> // For OutputDebugStringA
16#else 17#else
17#define _SH_DENYWR 0 18#define _SH_DENYWR 0
18#endif 19#endif
@@ -139,12 +140,18 @@ void FileBackend::Write(const Entry& entry) {
139 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { 140 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
140 return; 141 return;
141 } 142 }
142 bytes_written += file.WriteString(FormatLogMessage(entry) + '\n'); 143 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
143 if (entry.log_level >= Level::Error) { 144 if (entry.log_level >= Level::Error) {
144 file.Flush(); 145 file.Flush();
145 } 146 }
146} 147}
147 148
149void DebuggerBackend::Write(const Entry& entry) {
150#ifdef _WIN32
151 ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
152#endif
153}
154
148/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. 155/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
149#define ALL_LOG_CLASSES() \ 156#define ALL_LOG_CLASSES() \
150 CLS(Log) \ 157 CLS(Log) \
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 11edbf1b6..91bb0c309 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -103,6 +103,20 @@ private:
103 std::size_t bytes_written; 103 std::size_t bytes_written;
104}; 104};
105 105
106/**
107 * Backend that writes to Visual Studio's output window
108 */
109class DebuggerBackend : public Backend {
110public:
111 static const char* Name() {
112 return "debugger";
113 }
114 const char* GetName() const override {
115 return Name();
116 }
117 void Write(const Entry& entry) override;
118};
119
106void AddBackend(std::unique_ptr<Backend> backend); 120void AddBackend(std::unique_ptr<Backend> backend);
107 121
108void RemoveBackend(std::string_view backend_name); 122void RemoveBackend(std::string_view backend_name);
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 731d1db34..14f7037d8 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -4,11 +4,10 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cctype> 6#include <cctype>
7#include <cerrno>
8#include <codecvt> 7#include <codecvt>
9#include <cstdio>
10#include <cstdlib> 8#include <cstdlib>
11#include <cstring> 9#include <locale>
10#include <sstream>
12#include "common/common_paths.h" 11#include "common/common_paths.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
14#include "common/string_util.h" 13#include "common/string_util.h"
@@ -33,24 +32,6 @@ std::string ToUpper(std::string str) {
33 return str; 32 return str;
34} 33}
35 34
36// For Debugging. Read out an u8 array.
37std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
38 std::ostringstream oss;
39 oss << std::setfill('0') << std::hex;
40
41 for (int line = 0; size; ++data, --size) {
42 oss << std::setw(2) << (int)*data;
43
44 if (line_len == ++line) {
45 oss << '\n';
46 line = 0;
47 } else if (spaces)
48 oss << ' ';
49 }
50
51 return oss.str();
52}
53
54std::string StringFromBuffer(const std::vector<u8>& data) { 35std::string StringFromBuffer(const std::vector<u8>& data) {
55 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); 36 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
56} 37}
@@ -75,40 +56,6 @@ std::string StripQuotes(const std::string& s) {
75 return s; 56 return s;
76} 57}
77 58
78bool TryParse(const std::string& str, u32* const output) {
79 char* endptr = nullptr;
80
81 // Reset errno to a value other than ERANGE
82 errno = 0;
83
84 unsigned long value = strtoul(str.c_str(), &endptr, 0);
85
86 if (!endptr || *endptr)
87 return false;
88
89 if (errno == ERANGE)
90 return false;
91
92#if ULONG_MAX > UINT_MAX
93 if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
94 return false;
95#endif
96
97 *output = static_cast<u32>(value);
98 return true;
99}
100
101bool TryParse(const std::string& str, bool* const output) {
102 if ("1" == str || "true" == ToLower(str))
103 *output = true;
104 else if ("0" == str || "false" == ToLower(str))
105 *output = false;
106 else
107 return false;
108
109 return true;
110}
111
112std::string StringFromBool(bool value) { 59std::string StringFromBool(bool value) {
113 return value ? "True" : "False"; 60 return value ? "True" : "False";
114} 61}
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 32bf6a19c..08f96533b 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -5,8 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <iomanip>
9#include <sstream>
10#include <string> 8#include <string>
11#include <vector> 9#include <vector>
12#include "common/common_types.h" 10#include "common/common_types.h"
@@ -19,44 +17,13 @@ std::string ToLower(std::string str);
19/// Make a string uppercase 17/// Make a string uppercase
20std::string ToUpper(std::string str); 18std::string ToUpper(std::string str);
21 19
22std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
23
24std::string StringFromBuffer(const std::vector<u8>& data); 20std::string StringFromBuffer(const std::vector<u8>& data);
25 21
26std::string StripSpaces(const std::string& s); 22std::string StripSpaces(const std::string& s);
27std::string StripQuotes(const std::string& s); 23std::string StripQuotes(const std::string& s);
28 24
29// Thousand separator. Turns 12345678 into 12,345,678
30template <typename I>
31std::string ThousandSeparate(I value, int spaces = 0) {
32 std::ostringstream oss;
33
34// std::locale("") seems to be broken on many platforms
35#if defined _WIN32 || (defined __linux__ && !defined __clang__)
36 oss.imbue(std::locale(""));
37#endif
38 oss << std::setw(spaces) << value;
39
40 return oss.str();
41}
42
43std::string StringFromBool(bool value); 25std::string StringFromBool(bool value);
44 26
45bool TryParse(const std::string& str, bool* output);
46bool TryParse(const std::string& str, u32* output);
47
48template <typename N>
49static bool TryParse(const std::string& str, N* const output) {
50 std::istringstream iss(str);
51
52 N tmp = 0;
53 if (iss >> tmp) {
54 *output = tmp;
55 return true;
56 } else
57 return false;
58}
59
60std::string TabsToSpaces(int tab_size, std::string in); 27std::string TabsToSpaces(int tab_size, std::string in);
61 28
62void SplitString(const std::string& str, char delim, std::vector<std::string>& output); 29void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 76a2b7e86..e29f70b3a 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -8,8 +8,9 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 dump_root(std::move(dump_root_)),
13 sysnand_cache(std::make_unique<RegisteredCache>( 14 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 15 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_unique<RegisteredCache>( 16 usrnand_cache(std::make_unique<RegisteredCache>(
@@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
32 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); 33 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
33} 34}
34 35
36VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
37 if (title_id == 0)
38 return nullptr;
39 return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
40}
41
35} // namespace FileSys 42} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 364d309bd..453c11ad2 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -17,17 +17,19 @@ class RegisteredCache;
17/// registered caches. 17/// registered caches.
18class BISFactory { 18class BISFactory {
19public: 19public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 RegisteredCache* GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 RegisteredCache* GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 VirtualDir GetModificationDumpRoot(u64 title_id) const;
27 28
28private: 29private:
29 VirtualDir nand_root; 30 VirtualDir nand_root;
30 VirtualDir load_root; 31 VirtualDir load_root;
32 VirtualDir dump_root;
31 33
32 std::unique_ptr<RegisteredCache> sysnand_cache; 34 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::unique_ptr<RegisteredCache> usrnand_cache; 35 std::unique_ptr<RegisteredCache> usrnand_cache;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 1ece55731..2c145bd09 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -176,7 +176,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
176 for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) { 176 for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
177 if (file->GetExtension() != "nca") 177 if (file->GetExtension() != "nca")
178 continue; 178 continue;
179 auto nca = std::make_shared<NCA>(file); 179 auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
180 // TODO(DarkLordZach): Add proper Rev1+ Support 180 // TODO(DarkLordZach): Add proper Rev1+ Support
181 if (nca->IsUpdate()) 181 if (nca->IsUpdate())
182 continue; 182 continue;
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 8f62571cf..25f5914b6 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/crypto/key_manager.h"
12#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs.h"
13 14
14namespace Loader { 15namespace Loader {
@@ -107,5 +108,7 @@ private:
107 std::shared_ptr<NSP> secure_partition; 108 std::shared_ptr<NSP> secure_partition;
108 std::shared_ptr<NCA> program; 109 std::shared_ptr<NCA> program;
109 std::vector<std::shared_ptr<NCA>> ncas; 110 std::vector<std::shared_ptr<NCA>> ncas;
111
112 Core::Crypto::KeyManager keys;
110}; 113};
111} // namespace FileSys 114} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index b46fe893c..19b6f8600 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -101,8 +101,9 @@ static bool IsValidNCA(const NCAHeader& header) {
101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
102} 102}
103 103
104NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) 104NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
105 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { 105 Core::Crypto::KeyManager keys_)
106 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
106 if (file == nullptr) { 107 if (file == nullptr) {
107 status = Loader::ResultStatus::ErrorNullFile; 108 status = Loader::ResultStatus::ErrorNullFile;
108 return; 109 return;
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 4bba55607..99294cbb4 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -79,7 +79,8 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
79class NCA : public ReadOnlyVfsDirectory { 79class NCA : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, 81 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
82 u64 bktr_base_ivfc_offset = 0); 82 u64 bktr_base_ivfc_offset = 0,
83 Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
83 ~NCA() override; 84 ~NCA() override;
84 85
85 Loader::ResultStatus GetStatus() const; 86 Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index a012c2be9..c8fa912bf 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const {
66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size()); 67 raw->version_string.size());
68} 68}
69
70std::vector<u8> NACP::GetRawBytes() const {
71 std::vector<u8> out(sizeof(RawNACP));
72 std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
73 return out;
74}
69} // namespace FileSys 75} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 141f7e056..bfaad46b4 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -81,6 +81,7 @@ public:
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const; 82 u64 GetDLCBaseTitleId() const;
83 std::string GetVersionString() const; 83 std::string GetVersionString() const;
84 std::vector<u8> GetRawBytes() const;
84 85
85private: 86private:
86 std::unique_ptr<RawNACP> raw; 87 std::unique_ptr<RawNACP> raw;
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index fea0593c7..e4a4ee4ab 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -8,25 +8,10 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11namespace ErrCodes { 11constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
12enum { 12constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
13 NotFound = 1, 13constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
14 TitleNotFound = 1002, 14constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
15 SdCardNotFound = 2001, 15constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
16 RomFSNotFound = 2520,
17};
18}
19
20constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
21
22// TODO(bunnei): Replace these with correct errors for Switch OS
23constexpr ResultCode ERROR_INVALID_PATH(-1);
24constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
25constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
26constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
27constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
28constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
29constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
30constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
31 16
32} // namespace FileSys 17} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0c1156989..8d062eb3e 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -19,6 +19,7 @@
19#include "core/file_sys/vfs_vector.h" 19#include "core/file_sys/vfs_vector.h"
20#include "core/hle/service/filesystem/filesystem.h" 20#include "core/hle/service/filesystem/filesystem.h"
21#include "core/loader/loader.h" 21#include "core/loader/loader.h"
22#include "core/settings.h"
22 23
23namespace FileSys { 24namespace FileSys {
24 25
@@ -119,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
119 const auto build_id_raw = Common::HexArrayToString(header.build_id); 120 const auto build_id_raw = Common::HexArrayToString(header.build_id);
120 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 121 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
121 122
123 if (Settings::values.dump_nso) {
124 LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
125 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
126 if (dump_dir != nullptr) {
127 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
128 const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
129
130 file->Resize(nso.size());
131 file->WriteBytes(nso);
132 }
133 }
134
122 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); 135 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
123 136
124 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 137 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 96302a241..a3f8f2f73 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -106,9 +106,12 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
106 106
107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
108 std::string_view path) const { 108 std::string_view path) const {
109 if (dir->GetFileRelative(path) != nullptr) 109 const auto file = dir->GetFileRelative(path);
110 return dir->GetFileRelative(path); 110 if (file != nullptr)
111 if (dir->GetDirectoryRelative(path) != nullptr) { 111 return file;
112
113 const auto nca_dir = dir->GetDirectoryRelative(path);
114 if (nca_dir != nullptr) {
112 const auto nca_dir = dir->GetDirectoryRelative(path); 115 const auto nca_dir = dir->GetDirectoryRelative(path);
113 VirtualFile file = nullptr; 116 VirtualFile file = nullptr;
114 117
@@ -225,7 +228,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
225 228
226 if (file == nullptr) 229 if (file == nullptr)
227 continue; 230 continue;
228 const auto nca = std::make_shared<NCA>(parser(file, id)); 231 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
229 if (nca->GetStatus() != Loader::ResultStatus::Success || 232 if (nca->GetStatus() != Loader::ResultStatus::Success ||
230 nca->GetType() != NCAContentType::Meta) { 233 nca->GetType() != NCAContentType::Meta) {
231 continue; 234 continue;
@@ -315,7 +318,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
315 const auto raw = GetEntryRaw(title_id, type); 318 const auto raw = GetEntryRaw(title_id, type);
316 if (raw == nullptr) 319 if (raw == nullptr)
317 return nullptr; 320 return nullptr;
318 return std::make_unique<NCA>(raw); 321 return std::make_unique<NCA>(raw, nullptr, 0, keys);
319} 322}
320 323
321std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 324std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 6cfb16017..6b89db8de 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -12,6 +12,7 @@
12#include <vector> 12#include <vector>
13#include <boost/container/flat_map.hpp> 13#include <boost/container/flat_map.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/crypto/key_manager.h"
15#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
16 17
17namespace FileSys { 18namespace FileSys {
@@ -133,6 +134,8 @@ private:
133 134
134 VirtualDir dir; 135 VirtualDir dir;
135 RegisteredCacheParsingFunction parser; 136 RegisteredCacheParsingFunction parser;
137 Core::Crypto::KeyManager keys;
138
136 // maps tid -> NcaID of meta 139 // maps tid -> NcaID of meta
137 boost::container::flat_map<u64, NcaID> meta_id; 140 boost::container::flat_map<u64, NcaID> meta_id;
138 // maps tid -> meta 141 // maps tid -> meta
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index ef1aaebbb..5434f2149 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -83,28 +83,32 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
83 return MakeResult<VirtualDir>(std::move(out)); 83 return MakeResult<VirtualDir>(std::move(out));
84} 84}
85 85
86std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, 86VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
87 u128 user_id, u64 save_id) { 87 return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
88 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 88}
89 // be interpreted as the title id of the current process.
90 if (type == SaveDataType::SaveData && title_id == 0)
91 title_id = Core::CurrentProcess()->GetTitleID();
92
93 std::string out;
94 89
90std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
95 switch (space) { 91 switch (space) {
96 case SaveDataSpaceId::NandSystem: 92 case SaveDataSpaceId::NandSystem:
97 out = "/system/"; 93 return "/system/";
98 break;
99 case SaveDataSpaceId::NandUser: 94 case SaveDataSpaceId::NandUser:
100 out = "/user/"; 95 return "/user/";
101 break;
102 case SaveDataSpaceId::TemporaryStorage: 96 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/"; 97 return "/temp/";
104 break;
105 default: 98 default:
106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 99 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
100 return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
107 } 101 }
102}
103
104std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
105 u128 user_id, u64 save_id) {
106 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
107 // be interpreted as the title id of the current process.
108 if (type == SaveDataType::SaveData && title_id == 0)
109 title_id = Core::CurrentProcess()->GetTitleID();
110
111 std::string out = GetSaveDataSpaceIdPath(space);
108 112
109 switch (type) { 113 switch (type) {
110 case SaveDataType::SystemSaveData: 114 case SaveDataType::SystemSaveData:
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index d69ef6741..2a0088040 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -52,6 +52,9 @@ public:
52 52
53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); 53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
54 54
55 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
56
57 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
55 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, 58 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
56 u128 user_id, u64 save_id); 59 u128 user_id, u64 save_id);
57 60
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 2aaba4179..e1a4210db 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -252,7 +252,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
252 continue; 252 continue;
253 } 253 }
254 254
255 auto next_nca = std::make_shared<NCA>(next_file); 255 auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
256 if (next_nca->GetType() == NCAContentType::Program) 256 if (next_nca->GetType() == NCAContentType::Program)
257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
258 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 258 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 338080b7e..9a28ed5bb 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -70,6 +70,8 @@ private:
70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; 70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
71 std::vector<VirtualFile> ticket_files; 71 std::vector<VirtualFile> ticket_files;
72 72
73 Core::Crypto::KeyManager keys;
74
73 VirtualFile romfs; 75 VirtualFile romfs;
74 VirtualDir exefs; 76 VirtualDir exefs;
75}; 77};
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index ee698c8a7..8b58d701d 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -8,58 +8,28 @@
8 8
9namespace Kernel { 9namespace Kernel {
10 10
11namespace ErrCodes { 11// Confirmed Switch kernel error codes
12enum {
13 // Confirmed Switch OS error codes
14 MaxConnectionsReached = 7,
15 InvalidSize = 101,
16 InvalidAddress = 102,
17 HandleTableFull = 105,
18 InvalidMemoryState = 106,
19 InvalidMemoryPermissions = 108,
20 InvalidMemoryRange = 110,
21 InvalidThreadPriority = 112,
22 InvalidProcessorId = 113,
23 InvalidHandle = 114,
24 InvalidPointer = 115,
25 InvalidCombination = 116,
26 Timeout = 117,
27 SynchronizationCanceled = 118,
28 TooLarge = 119,
29 InvalidEnumValue = 120,
30 NoSuchEntry = 121,
31 AlreadyRegistered = 122,
32 SessionClosed = 123,
33 InvalidState = 125,
34 ResourceLimitExceeded = 132,
35};
36}
37 12
38// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
39// double check that the code matches before re-using the constant. 14constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
40 15constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
41constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); 16constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
42constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); 17constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
43constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); 18constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
44constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, 19constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
45 ErrCodes::MaxConnectionsReached); 20constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
46constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); 21constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
47constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, 22constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
48 ErrCodes::InvalidCombination); 23constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
49constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); 24constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
50constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 25constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
51constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 26constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
52 ErrCodes::InvalidMemoryPermissions); 27constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
53constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange); 28constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
54constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 29constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
55constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 30constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
56constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
57constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered); 32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
58constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
59constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
60 ErrCodes::InvalidThreadPriority);
61constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
62constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
63constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
64 34
65} // namespace Kernel 35} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 420218d59..a257c3726 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -5,11 +5,9 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_funcs.h"
9#include "common/logging/log.h" 8#include "common/logging/log.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/file_sys/program_metadata.h" 10#include "core/file_sys/program_metadata.h"
12#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/resource_limit.h" 13#include "core/hle/kernel/resource_limit.h"
@@ -17,6 +15,7 @@
17#include "core/hle/kernel/thread.h" 15#include "core/hle/kernel/thread.h"
18#include "core/hle/kernel/vm_manager.h" 16#include "core/hle/kernel/vm_manager.h"
19#include "core/memory.h" 17#include "core/memory.h"
18#include "core/settings.h"
20 19
21namespace Kernel { 20namespace Kernel {
22 21
@@ -35,6 +34,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
35 process->process_id = kernel.CreateNewProcessID(); 34 process->process_id = kernel.CreateNewProcessID();
36 process->svc_access_mask.set(); 35 process->svc_access_mask.set();
37 36
37 std::mt19937 rng(Settings::values.rng_seed.value_or(0));
38 std::uniform_int_distribution<u64> distribution;
39 std::generate(process->random_entropy.begin(), process->random_entropy.end(),
40 [&] { return distribution(rng); });
41
38 kernel.AppendNewProcess(process); 42 kernel.AppendNewProcess(process);
39 return process; 43 return process;
40} 44}
@@ -241,83 +245,15 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
241} 245}
242 246
243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 247ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
244 if (target < vm_manager.GetHeapRegionBaseAddress() || 248 return vm_manager.HeapAllocate(target, size, perms);
245 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
246 return ERR_INVALID_ADDRESS;
247 }
248
249 if (heap_memory == nullptr) {
250 // Initialize heap
251 heap_memory = std::make_shared<std::vector<u8>>();
252 heap_start = heap_end = target;
253 } else {
254 vm_manager.UnmapRange(heap_start, heap_end - heap_start);
255 }
256
257 // If necessary, expand backing vector to cover new heap extents.
258 if (target < heap_start) {
259 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
260 heap_start = target;
261 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
262 }
263 if (target + size > heap_end) {
264 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
265 heap_end = target + size;
266 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
267 }
268 ASSERT(heap_end - heap_start == heap_memory->size());
269
270 CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
271 size, MemoryState::Heap));
272 vm_manager.Reprotect(vma, perms);
273
274 heap_used = size;
275
276 return MakeResult<VAddr>(heap_end - size);
277} 249}
278 250
279ResultCode Process::HeapFree(VAddr target, u32 size) { 251ResultCode Process::HeapFree(VAddr target, u32 size) {
280 if (target < vm_manager.GetHeapRegionBaseAddress() || 252 return vm_manager.HeapFree(target, size);
281 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
282 return ERR_INVALID_ADDRESS;
283 }
284
285 if (size == 0) {
286 return RESULT_SUCCESS;
287 }
288
289 ResultCode result = vm_manager.UnmapRange(target, size);
290 if (result.IsError())
291 return result;
292
293 heap_used -= size;
294
295 return RESULT_SUCCESS;
296} 253}
297 254
298ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 255ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
299 auto vma = vm_manager.FindVMA(src_addr); 256 return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
300
301 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
302 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
303
304 // The returned VMA might be a bigger one encompassing the desired address.
305 auto vma_offset = src_addr - vma->first;
306 ASSERT_MSG(vma_offset + size <= vma->second.size,
307 "Shared memory exceeds bounds of mapped block");
308
309 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
310 std::size_t backing_block_offset = vma->second.offset + vma_offset;
311
312 CASCADE_RESULT(auto new_vma,
313 vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
314 MemoryState::Mapped));
315 // Protect mirror with permissions from old region
316 vm_manager.Reprotect(new_vma, vma->second.permissions);
317 // Remove permissions from old region
318 vm_manager.Reprotect(vma, VMAPermission::None);
319
320 return RESULT_SUCCESS;
321} 257}
322 258
323ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { 259ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 8d2616c79..230e395ff 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -8,6 +8,7 @@
8#include <bitset> 8#include <bitset>
9#include <cstddef> 9#include <cstddef>
10#include <memory> 10#include <memory>
11#include <random>
11#include <string> 12#include <string>
12#include <vector> 13#include <vector>
13#include <boost/container/static_vector.hpp> 14#include <boost/container/static_vector.hpp>
@@ -119,6 +120,8 @@ struct CodeSet final {
119 120
120class Process final : public Object { 121class Process final : public Object {
121public: 122public:
123 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
124
122 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); 125 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
123 126
124 std::string GetTypeName() const override { 127 std::string GetTypeName() const override {
@@ -212,6 +215,11 @@ public:
212 total_process_running_time_ticks += ticks; 215 total_process_running_time_ticks += ticks;
213 } 216 }
214 217
218 /// Gets 8 bytes of random data for svcGetInfo RandomEntropy
219 u64 GetRandomEntropy(std::size_t index) const {
220 return random_entropy.at(index);
221 }
222
215 /** 223 /**
216 * Loads process-specifics configuration info with metadata provided 224 * Loads process-specifics configuration info with metadata provided
217 * by an executable. 225 * by an executable.
@@ -251,7 +259,8 @@ public:
251 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 259 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
252 ResultCode HeapFree(VAddr target, u32 size); 260 ResultCode HeapFree(VAddr target, u32 size);
253 261
254 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); 262 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
263 MemoryState state = MemoryState::Mapped);
255 264
256 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); 265 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
257 266
@@ -292,17 +301,6 @@ private:
292 u32 allowed_thread_priority_mask = 0xFFFFFFFF; 301 u32 allowed_thread_priority_mask = 0xFFFFFFFF;
293 u32 is_virtual_address_memory_enabled = 0; 302 u32 is_virtual_address_memory_enabled = 0;
294 303
295 // Memory used to back the allocations in the regular heap. A single vector is used to cover
296 // the entire virtual address space extents that bound the allocations, including any holes.
297 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
298 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
299 std::shared_ptr<std::vector<u8>> heap_memory;
300
301 // The left/right bounds of the address space covered by heap_memory.
302 VAddr heap_start = 0;
303 VAddr heap_end = 0;
304 u64 heap_used = 0;
305
306 /// The Thread Local Storage area is allocated as processes create threads, 304 /// The Thread Local Storage area is allocated as processes create threads,
307 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part 305 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
308 /// holds the TLS for a specific thread. This vector contains which parts are in use for each 306 /// holds the TLS for a specific thread. This vector contains which parts are in use for each
@@ -321,6 +319,9 @@ private:
321 /// Per-process handle table for storing created object handles in. 319 /// Per-process handle table for storing created object handles in.
322 HandleTable handle_table; 320 HandleTable handle_table;
323 321
322 /// Random values for svcGetInfo RandomEntropy
323 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
324
324 std::string name; 325 std::string name;
325}; 326};
326 327
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index c7c579aaf..75dbfc31d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -34,6 +34,7 @@
34#include "core/hle/lock.h" 34#include "core/hle/lock.h"
35#include "core/hle/result.h" 35#include "core/hle/result.h"
36#include "core/hle/service/service.h" 36#include "core/hle/service/service.h"
37#include "core/settings.h"
37 38
38namespace Kernel { 39namespace Kernel {
39namespace { 40namespace {
@@ -122,6 +123,48 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
122 return RESULT_SUCCESS; 123 return RESULT_SUCCESS;
123} 124}
124 125
126static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
127 LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
128
129 if (!Common::Is4KBAligned(addr)) {
130 return ERR_INVALID_ADDRESS;
131 }
132
133 if (size == 0 || !Common::Is4KBAligned(size)) {
134 return ERR_INVALID_SIZE;
135 }
136
137 if (!IsValidAddressRange(addr, size)) {
138 return ERR_INVALID_ADDRESS_STATE;
139 }
140
141 const auto permission = static_cast<MemoryPermission>(prot);
142 if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
143 permission != MemoryPermission::ReadWrite) {
144 return ERR_INVALID_MEMORY_PERMISSIONS;
145 }
146
147 auto* const current_process = Core::CurrentProcess();
148 auto& vm_manager = current_process->VMManager();
149
150 if (!IsInsideAddressSpace(vm_manager, addr, size)) {
151 return ERR_INVALID_ADDRESS_STATE;
152 }
153
154 const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
155 if (iter == vm_manager.vma_map.end()) {
156 return ERR_INVALID_ADDRESS_STATE;
157 }
158
159 LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
160 // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
161 // make sense to allow changing permissions on kernel memory itself, etc).
162
163 const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
164
165 return vm_manager.ReprotectRange(addr, size, converted_permissions);
166}
167
125static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { 168static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
126 LOG_WARNING(Kernel_SVC, 169 LOG_WARNING(Kernel_SVC,
127 "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr, 170 "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
@@ -171,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
171 // Read 1 char beyond the max allowed port name to detect names that are too long. 214 // Read 1 char beyond the max allowed port name to detect names that are too long.
172 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); 215 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
173 if (port_name.size() > PortNameMaxLength) { 216 if (port_name.size() > PortNameMaxLength) {
174 return ERR_PORT_NAME_TOO_LONG; 217 return ERR_OUT_OF_RANGE;
175 } 218 }
176 219
177 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 220 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -267,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
267 310
268 static constexpr u64 MaxHandles = 0x40; 311 static constexpr u64 MaxHandles = 0x40;
269 312
270 if (handle_count > MaxHandles) 313 if (handle_count > MaxHandles) {
271 return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); 314 return ERR_OUT_OF_RANGE;
315 }
272 316
273 auto* const thread = GetCurrentThread(); 317 auto* const thread = GetCurrentThread();
274 318
@@ -333,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
333 } 377 }
334 378
335 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); 379 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
336 thread->SetWaitSynchronizationResult( 380 thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
337 ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
338 thread->ResumeFromWait(); 381 thread->ResumeFromWait();
339 return RESULT_SUCCESS; 382 return RESULT_SUCCESS;
340} 383}
@@ -395,16 +438,42 @@ struct BreakReason {
395/// Break program execution 438/// Break program execution
396static void Break(u32 reason, u64 info1, u64 info2) { 439static void Break(u32 reason, u64 info1, u64 info2) {
397 BreakReason break_reason{reason}; 440 BreakReason break_reason{reason};
441 bool has_dumped_buffer{};
442
443 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
444 if (sz == 0 || addr == 0 || has_dumped_buffer) {
445 return;
446 }
398 447
448 // This typically is an error code so we're going to assume this is the case
449 if (sz == sizeof(u32)) {
450 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
451 } else {
452 // We don't know what's in here so we'll hexdump it
453 std::vector<u8> debug_buffer(sz);
454 Memory::ReadBlock(addr, debug_buffer.data(), sz);
455 std::string hexdump;
456 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
457 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
458 if (i != 0 && i % 16 == 0) {
459 hexdump += '\n';
460 }
461 }
462 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
463 }
464 has_dumped_buffer = true;
465 };
399 switch (break_reason.break_type) { 466 switch (break_reason.break_type) {
400 case BreakType::Panic: 467 case BreakType::Panic:
401 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", 468 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
402 info1, info2); 469 info1, info2);
470 handle_debug_buffer(info1, info2);
403 break; 471 break;
404 case BreakType::AssertionFailed: 472 case BreakType::AssertionFailed:
405 LOG_CRITICAL(Debug_Emulated, 473 LOG_CRITICAL(Debug_Emulated,
406 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", 474 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
407 info1, info2); 475 info1, info2);
476 handle_debug_buffer(info1, info2);
408 break; 477 break;
409 case BreakType::PreNROLoad: 478 case BreakType::PreNROLoad:
410 LOG_WARNING( 479 LOG_WARNING(
@@ -433,6 +502,7 @@ static void Break(u32 reason, u64 info1, u64 info2) {
433 Debug_Emulated, 502 Debug_Emulated,
434 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", 503 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
435 static_cast<u32>(break_reason.break_type.Value()), info1, info2); 504 static_cast<u32>(break_reason.break_type.Value()), info1, info2);
505 handle_debug_buffer(info1, info2);
436 break; 506 break;
437 } 507 }
438 508
@@ -441,6 +511,7 @@ static void Break(u32 reason, u64 info1, u64 info2) {
441 Debug_Emulated, 511 Debug_Emulated,
442 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 512 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
443 reason, info1, info2); 513 reason, info1, info2);
514 handle_debug_buffer(info1, info2);
444 ASSERT(false); 515 ASSERT(false);
445 516
446 Core::CurrentProcess()->PrepareForTermination(); 517 Core::CurrentProcess()->PrepareForTermination();
@@ -530,7 +601,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
530 *result = 0; 601 *result = 0;
531 break; 602 break;
532 case GetInfoType::RandomEntropy: 603 case GetInfoType::RandomEntropy:
533 *result = 0; 604 if (handle != 0) {
605 return ERR_INVALID_HANDLE;
606 }
607
608 if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
609 return ERR_INVALID_COMBINATION;
610 }
611
612 *result = current_process->GetRandomEntropy(info_sub_id);
613 return RESULT_SUCCESS;
534 break; 614 break;
535 case GetInfoType::ASLRRegionBaseAddr: 615 case GetInfoType::ASLRRegionBaseAddr:
536 *result = vm_manager.GetASLRRegionBaseAddress(); 616 *result = vm_manager.GetASLRRegionBaseAddress();
@@ -563,7 +643,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
563 case GetInfoType::ThreadTickCount: { 643 case GetInfoType::ThreadTickCount: {
564 constexpr u64 num_cpus = 4; 644 constexpr u64 num_cpus = 4;
565 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { 645 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
566 return ERR_INVALID_COMBINATION_KERNEL; 646 return ERR_INVALID_COMBINATION;
567 } 647 }
568 648
569 const auto thread = 649 const auto thread =
@@ -1101,7 +1181,7 @@ static ResultCode CloseHandle(Handle handle) {
1101 1181
1102/// Reset an event 1182/// Reset an event
1103static ResultCode ResetSignal(Handle handle) { 1183static ResultCode ResetSignal(Handle handle) {
1104 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1184 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1105 1185
1106 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1186 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1107 auto event = handle_table.Get<Event>(handle); 1187 auto event = handle_table.Get<Event>(handle);
@@ -1156,7 +1236,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1156 } 1236 }
1157 1237
1158 if (mask == 0) { 1238 if (mask == 0) {
1159 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1239 return ERR_INVALID_COMBINATION;
1160 } 1240 }
1161 1241
1162 /// This value is used to only change the affinity mask without changing the current ideal core. 1242 /// This value is used to only change the affinity mask without changing the current ideal core.
@@ -1165,12 +1245,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1165 if (core == OnlyChangeMask) { 1245 if (core == OnlyChangeMask) {
1166 core = thread->GetIdealCore(); 1246 core = thread->GetIdealCore();
1167 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { 1247 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
1168 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 1248 return ERR_INVALID_PROCESSOR_ID;
1169 } 1249 }
1170 1250
1171 // Error out if the input core isn't enabled in the input mask. 1251 // Error out if the input core isn't enabled in the input mask.
1172 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { 1252 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
1173 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1253 return ERR_INVALID_COMBINATION;
1174 } 1254 }
1175 1255
1176 thread->ChangeCore(core, mask); 1256 thread->ChangeCore(core, mask);
@@ -1259,7 +1339,7 @@ struct FunctionDef {
1259static const FunctionDef SVC_Table[] = { 1339static const FunctionDef SVC_Table[] = {
1260 {0x00, nullptr, "Unknown"}, 1340 {0x00, nullptr, "Unknown"},
1261 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"}, 1341 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
1262 {0x02, nullptr, "SetMemoryPermission"}, 1342 {0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
1263 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"}, 1343 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
1264 {0x04, SvcWrap<MapMemory>, "MapMemory"}, 1344 {0x04, SvcWrap<MapMemory>, "MapMemory"},
1265 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"}, 1345 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index b09753c80..233a99fb0 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -121,6 +121,11 @@ void SvcWrap() {
121 FuncReturn(func(Param(0), Param(1), Param(2)).raw); 121 FuncReturn(func(Param(0), Param(1), Param(2)).raw);
122} 122}
123 123
124template <ResultCode func(u64, u64, u32)>
125void SvcWrap() {
126 FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
127}
128
124template <ResultCode func(u32, u64, u64, u32)> 129template <ResultCode func(u32, u64, u64, u32)>
125void SvcWrap() { 130void SvcWrap() {
126 FuncReturn( 131 FuncReturn(
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index dd5cd9ced..4ffb76818 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
142 142
143 status = ThreadStatus::Ready; 143 status = ThreadStatus::Ready;
144 144
145 std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); 145 ChangeScheduler();
146 if (!new_processor_id) {
147 new_processor_id = processor_id;
148 }
149 if (ideal_core != -1 &&
150 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
151 new_processor_id = ideal_core;
152 }
153
154 ASSERT(*new_processor_id < 4);
155
156 // Add thread to new core's scheduler
157 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
158
159 if (*new_processor_id != processor_id) {
160 // Remove thread from previous core's scheduler
161 scheduler->RemoveThread(this);
162 next_scheduler->AddThread(this, current_priority);
163 }
164
165 processor_id = *new_processor_id;
166
167 // If the thread was ready, unschedule from the previous core and schedule on the new core
168 scheduler->UnscheduleThread(this, current_priority);
169 next_scheduler->ScheduleThread(this, current_priority);
170
171 // Change thread's scheduler
172 scheduler = next_scheduler;
173
174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
175} 146}
176 147
177/** 148/**
@@ -364,42 +335,45 @@ void Thread::UpdatePriority() {
364void Thread::ChangeCore(u32 core, u64 mask) { 335void Thread::ChangeCore(u32 core, u64 mask) {
365 ideal_core = core; 336 ideal_core = core;
366 affinity_mask = mask; 337 affinity_mask = mask;
338 ChangeScheduler();
339}
367 340
341void Thread::ChangeScheduler() {
368 if (status != ThreadStatus::Ready) { 342 if (status != ThreadStatus::Ready) {
369 return; 343 return;
370 } 344 }
371 345
346 auto& system = Core::System::GetInstance();
372 std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; 347 std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
373 348
374 if (!new_processor_id) { 349 if (!new_processor_id) {
375 new_processor_id = processor_id; 350 new_processor_id = processor_id;
376 } 351 }
377 if (ideal_core != -1 && 352 if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
379 new_processor_id = ideal_core; 353 new_processor_id = ideal_core;
380 } 354 }
381 355
382 ASSERT(*new_processor_id < 4); 356 ASSERT(*new_processor_id < 4);
383 357
384 // Add thread to new core's scheduler 358 // Add thread to new core's scheduler
385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id); 359 auto& next_scheduler = system.Scheduler(*new_processor_id);
386 360
387 if (*new_processor_id != processor_id) { 361 if (*new_processor_id != processor_id) {
388 // Remove thread from previous core's scheduler 362 // Remove thread from previous core's scheduler
389 scheduler->RemoveThread(this); 363 scheduler->RemoveThread(this);
390 next_scheduler->AddThread(this, current_priority); 364 next_scheduler.AddThread(this, current_priority);
391 } 365 }
392 366
393 processor_id = *new_processor_id; 367 processor_id = *new_processor_id;
394 368
395 // If the thread was ready, unschedule from the previous core and schedule on the new core 369 // If the thread was ready, unschedule from the previous core and schedule on the new core
396 scheduler->UnscheduleThread(this, current_priority); 370 scheduler->UnscheduleThread(this, current_priority);
397 next_scheduler->ScheduleThread(this, current_priority); 371 next_scheduler.ScheduleThread(this, current_priority);
398 372
399 // Change thread's scheduler 373 // Change thread's scheduler
400 scheduler = next_scheduler; 374 scheduler = &next_scheduler;
401 375
402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 376 system.CpuCore(processor_id).PrepareReschedule();
403} 377}
404 378
405bool Thread::AllWaitObjectsReady() { 379bool Thread::AllWaitObjectsReady() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 4a6e11239..d384d50db 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -374,6 +374,8 @@ private:
374 explicit Thread(KernelCore& kernel); 374 explicit Thread(KernelCore& kernel);
375 ~Thread() override; 375 ~Thread() override;
376 376
377 void ChangeScheduler();
378
377 Core::ARM_Interface::ThreadContext context{}; 379 Core::ARM_Interface::ThreadContext context{};
378 380
379 u32 thread_id = 0; 381 u32 thread_id = 0;
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 1a92c8f70..100f8f6bf 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -243,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
243 return RESULT_SUCCESS; 243 return RESULT_SUCCESS;
244} 244}
245 245
246ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
247 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
248 target + size < target) {
249 return ERR_INVALID_ADDRESS;
250 }
251
252 if (heap_memory == nullptr) {
253 // Initialize heap
254 heap_memory = std::make_shared<std::vector<u8>>();
255 heap_start = heap_end = target;
256 } else {
257 UnmapRange(heap_start, heap_end - heap_start);
258 }
259
260 // If necessary, expand backing vector to cover new heap extents.
261 if (target < heap_start) {
262 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
263 heap_start = target;
264 RefreshMemoryBlockMappings(heap_memory.get());
265 }
266 if (target + size > heap_end) {
267 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
268 heap_end = target + size;
269 RefreshMemoryBlockMappings(heap_memory.get());
270 }
271 ASSERT(heap_end - heap_start == heap_memory->size());
272
273 CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
274 MemoryState::Heap));
275 Reprotect(vma, perms);
276
277 heap_used = size;
278
279 return MakeResult<VAddr>(heap_end - size);
280}
281
282ResultCode VMManager::HeapFree(VAddr target, u64 size) {
283 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
284 target + size < target) {
285 return ERR_INVALID_ADDRESS;
286 }
287
288 if (size == 0) {
289 return RESULT_SUCCESS;
290 }
291
292 const ResultCode result = UnmapRange(target, size);
293 if (result.IsError()) {
294 return result;
295 }
296
297 heap_used -= size;
298 return RESULT_SUCCESS;
299}
300
301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
302 const auto vma = FindVMA(src_addr);
303
304 ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
305 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
306
307 // The returned VMA might be a bigger one encompassing the desired address.
308 const auto vma_offset = src_addr - vma->first;
309 ASSERT_MSG(vma_offset + size <= vma->second.size,
310 "Shared memory exceeds bounds of mapped block");
311
312 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
313 const std::size_t backing_block_offset = vma->second.offset + vma_offset;
314
315 CASCADE_RESULT(auto new_vma,
316 MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
317 // Protect mirror with permissions from old region
318 Reprotect(new_vma, vma->second.permissions);
319 // Remove permissions from old region
320 Reprotect(vma, VMAPermission::None);
321
322 return RESULT_SUCCESS;
323}
324
246void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { 325void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
247 // If this ever proves to have a noticeable performance impact, allow users of the function to 326 // If this ever proves to have a noticeable performance impact, allow users of the function to
248 // specify a specific range of addresses to limit the scan to. 327 // specify a specific range of addresses to limit the scan to.
@@ -495,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const {
495} 574}
496 575
497u64 VMManager::GetTotalHeapUsage() const { 576u64 VMManager::GetTotalHeapUsage() const {
498 LOG_WARNING(Kernel, "(STUBBED) called"); 577 return heap_used;
499 return 0x0;
500} 578}
501 579
502VAddr VMManager::GetAddressSpaceBaseAddress() const { 580VAddr VMManager::GetAddressSpaceBaseAddress() const {
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 2447cbb8f..d522404fe 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -186,6 +186,12 @@ public:
186 /// Changes the permissions of a range of addresses, splitting VMAs as necessary. 186 /// Changes the permissions of a range of addresses, splitting VMAs as necessary.
187 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); 187 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
188 188
189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
190 ResultCode HeapFree(VAddr target, u64 size);
191
192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
193 MemoryState state = MemoryState::Mapped);
194
189 /** 195 /**
190 * Scans all VMAs and updates the page table range of any that use the given vector as backing 196 * Scans all VMAs and updates the page table range of any that use the given vector as backing
191 * memory. This should be called after any operation that causes reallocation of the vector. 197 * memory. This should be called after any operation that causes reallocation of the vector.
@@ -343,5 +349,15 @@ private:
343 349
344 VAddr tls_io_region_base = 0; 350 VAddr tls_io_region_base = 0;
345 VAddr tls_io_region_end = 0; 351 VAddr tls_io_region_end = 0;
352
353 // Memory used to back the allocations in the regular heap. A single vector is used to cover
354 // the entire virtual address space extents that bound the allocations, including any holes.
355 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
356 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
357 std::shared_ptr<std::vector<u8>> heap_memory;
358 // The left/right bounds of the address space covered by heap_memory.
359 VAddr heap_start = 0;
360 VAddr heap_end = 0;
361 u64 heap_used = 0;
346}; 362};
347} // namespace Kernel 363} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index c6b18cfba..bfb77cc31 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -19,8 +19,6 @@
19enum class ErrorDescription : u32 { 19enum class ErrorDescription : u32 {
20 Success = 0, 20 Success = 0,
21 RemoteProcessDead = 301, 21 RemoteProcessDead = 301,
22 InvalidOffset = 6061,
23 InvalidLength = 6062,
24}; 22};
25 23
26/** 24/**
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index c6437a671..c629f9357 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -242,6 +242,30 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
242 LOG_DEBUG(Service_ACC, "called"); 242 LOG_DEBUG(Service_ACC, "called");
243} 243}
244 244
245void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
246 LOG_DEBUG(Service_ACC, "called");
247 // A u8 is passed into this function which we can safely ignore. It's to determine if we have
248 // access to use the network or not by the looks of it
249 IPC::ResponseBuilder rb{ctx, 6};
250 if (profile_manager->GetUserCount() != 1) {
251 rb.Push(RESULT_SUCCESS);
252 rb.PushRaw<u128>(INVALID_UUID);
253 return;
254 }
255
256 const auto user_list = profile_manager->GetAllUsers();
257 if (std::all_of(user_list.begin(), user_list.end(),
258 [](const auto& user) { return user.uuid == INVALID_UUID; })) {
259 rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
260 rb.PushRaw<u128>(INVALID_UUID);
261 return;
262 }
263
264 // Select the first user we have
265 rb.Push(RESULT_SUCCESS);
266 rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
267}
268
245Module::Interface::Interface(std::shared_ptr<Module> module, 269Module::Interface::Interface(std::shared_ptr<Module> module,
246 std::shared_ptr<ProfileManager> profile_manager, const char* name) 270 std::shared_ptr<ProfileManager> profile_manager, const char* name)
247 : ServiceFramework(name), module(std::move(module)), 271 : ServiceFramework(name), module(std::move(module)),
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index c7ed74351..89b2104fa 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -27,6 +27,7 @@ public:
27 void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); 27 void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
28 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); 28 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
29 void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); 29 void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
30 void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
30 31
31 protected: 32 protected:
32 std::shared_ptr<Module> module; 33 std::shared_ptr<Module> module;
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index ad455c3a7..5e2030355 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {5, &ACC_SU::GetProfile, "GetProfile"}, 17 {5, &ACC_SU::GetProfile, "GetProfile"},
18 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
19 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 22 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 23 {101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 72d4adf35..a4d705b45 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {5, &ACC_U0::GetProfile, "GetProfile"}, 17 {5, &ACC_U0::GetProfile, "GetProfile"},
18 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
19 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
22 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, 22 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
23 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, 23 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index d480f08e5..8fffc93b5 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {5, &ACC_U1::GetProfile, "GetProfile"}, 17 {5, &ACC_U1::GetProfile, "GetProfile"},
18 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
19 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 22 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 23 {101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index c08394e4c..968263846 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -2,8 +2,11 @@
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 <cstring>
5#include <random> 6#include <random>
6 7
8#include <fmt/format.h>
9
7#include "common/file_util.h" 10#include "common/file_util.h"
8#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
9#include "core/settings.h" 12#include "core/settings.h"
@@ -39,6 +42,19 @@ UUID UUID::Generate() {
39 return UUID{distribution(gen), distribution(gen)}; 42 return UUID{distribution(gen), distribution(gen)};
40} 43}
41 44
45std::string UUID::Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47}
48
49std::string UUID::FormatSwitch() const {
50 std::array<u8, 16> s{};
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56}
57
42ProfileManager::ProfileManager() { 58ProfileManager::ProfileManager() {
43 ParseUserSaveFile(); 59 ParseUserSaveFile();
44 60
@@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() {
325 return; 341 return;
326 } 342 }
327 343
328 for (std::size_t i = 0; i < MAX_USERS; ++i) { 344 for (const auto& user : data.users) {
329 const auto& user = data.users[i]; 345 if (user.uuid == UUID(INVALID_UUID)) {
346 continue;
347 }
330 348
331 if (user.uuid != UUID(INVALID_UUID)) 349 AddUser({user.uuid, user.username, user.timestamp, {}, false});
332 AddUser({user.uuid, user.username, user.timestamp, {}, false});
333 } 350 }
334 351
335 std::stable_partition(profiles.begin(), profiles.end(), 352 std::stable_partition(profiles.begin(), profiles.end(),
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 747c46c20..d2d8e6c6b 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -42,18 +42,9 @@ struct UUID {
42 void Invalidate() { 42 void Invalidate() {
43 uuid = INVALID_UUID; 43 uuid = INVALID_UUID;
44 } 44 }
45 std::string Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47 }
48 45
49 std::string FormatSwitch() const { 46 std::string Format() const;
50 std::array<u8, 16> s{}; 47 std::string FormatSwitch() const;
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56 }
57}; 48};
58static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 49static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
59 50
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index ac3ff9f20..3758ecae1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -203,8 +203,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
203ISelfController::~ISelfController() = default; 203ISelfController::~ISelfController() = default;
204 204
205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
206 // Takes 3 input u8s with each field located immediately after the previous u8, these are 206 // Takes 3 input u8s with each field located immediately after the previous
207 // bool flags. No output. 207 // u8, these are bool flags. No output.
208 208
209 IPC::RequestParser rp{ctx}; 209 IPC::RequestParser rp{ctx};
210 210
@@ -258,8 +258,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
258} 258}
259 259
260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
261 // Takes 3 input u8s with each field located immediately after the previous u8, these are 261 // Takes 3 input u8s with each field located immediately after the previous
262 // bool flags. No output. 262 // u8, these are bool flags. No output.
263 IPC::RequestParser rp{ctx}; 263 IPC::RequestParser rp{ctx};
264 264
265 bool enabled = rp.Pop<bool>(); 265 bool enabled = rp.Pop<bool>();
@@ -302,8 +302,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
302} 302}
303 303
304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
305 // TODO(Subv): Find out how AM determines the display to use, for now just create the layer 305 // TODO(Subv): Find out how AM determines the display to use, for now just
306 // in the Default display. 306 // create the layer in the Default display.
307 u64 display_id = nvflinger->OpenDisplay("Default"); 307 u64 display_id = nvflinger->OpenDisplay("Default");
308 u64 layer_id = nvflinger->CreateLayer(display_id); 308 u64 layer_id = nvflinger->CreateLayer(display_id);
309 309
@@ -338,7 +338,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
338 LOG_WARNING(Service_AM, "(STUBBED) called"); 338 LOG_WARNING(Service_AM, "(STUBBED) called");
339} 339}
340 340
341ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { 341AppletMessageQueue::AppletMessageQueue() {
342 auto& kernel = Core::System::GetInstance().Kernel();
343 on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
344 "AMMessageQueue:OnMessageRecieved");
345 on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
346 "AMMessageQueue:OperationModeChanged");
347}
348
349AppletMessageQueue::~AppletMessageQueue() = default;
350
351const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const {
352 return on_new_message;
353}
354
355const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const {
356 return on_operation_mode_changed;
357}
358
359void AppletMessageQueue::PushMessage(AppletMessage msg) {
360 messages.push(msg);
361 on_new_message->Signal();
362}
363
364AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
365 if (messages.empty()) {
366 on_new_message->Clear();
367 return AppletMessage::NoMessage;
368 }
369 auto msg = messages.front();
370 messages.pop();
371 if (messages.empty()) {
372 on_new_message->Clear();
373 }
374 return msg;
375}
376
377std::size_t AppletMessageQueue::GetMessageCount() const {
378 return messages.size();
379}
380
381void AppletMessageQueue::OperationModeChanged() {
382 PushMessage(AppletMessage::OperationModeChanged);
383 PushMessage(AppletMessage::PerformanceModeChanged);
384 on_operation_mode_changed->Signal();
385}
386
387ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
388 : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) {
342 // clang-format off 389 // clang-format off
343 static const FunctionInfo functions[] = { 390 static const FunctionInfo functions[] = {
344 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, 391 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
@@ -388,21 +435,19 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
388} 435}
389 436
390void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { 437void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
391 event->Signal();
392
393 IPC::ResponseBuilder rb{ctx, 2, 1}; 438 IPC::ResponseBuilder rb{ctx, 2, 1};
394 rb.Push(RESULT_SUCCESS); 439 rb.Push(RESULT_SUCCESS);
395 rb.PushCopyObjects(event); 440 rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
396 441
397 LOG_WARNING(Service_AM, "(STUBBED) called"); 442 LOG_DEBUG(Service_AM, "called");
398} 443}
399 444
400void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { 445void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
401 IPC::ResponseBuilder rb{ctx, 3}; 446 IPC::ResponseBuilder rb{ctx, 3};
402 rb.Push(RESULT_SUCCESS); 447 rb.Push(RESULT_SUCCESS);
403 rb.Push<u32>(15); 448 rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
404 449
405 LOG_WARNING(Service_AM, "(STUBBED) called"); 450 LOG_DEBUG(Service_AM, "called");
406} 451}
407 452
408void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { 453void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
@@ -414,13 +459,11 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
414} 459}
415 460
416void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { 461void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
417 event->Signal();
418
419 IPC::ResponseBuilder rb{ctx, 2, 1}; 462 IPC::ResponseBuilder rb{ctx, 2, 1};
420 rb.Push(RESULT_SUCCESS); 463 rb.Push(RESULT_SUCCESS);
421 rb.PushCopyObjects(event); 464 rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
422 465
423 LOG_WARNING(Service_AM, "(STUBBED) called"); 466 LOG_DEBUG(Service_AM, "called");
424} 467}
425 468
426void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { 469void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
@@ -444,7 +487,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
444 rb.Push(RESULT_SUCCESS); 487 rb.Push(RESULT_SUCCESS);
445 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); 488 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
446 489
447 LOG_WARNING(Service_AM, "(STUBBED) called"); 490 LOG_DEBUG(Service_AM, "called");
448} 491}
449 492
450void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { 493void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
@@ -454,7 +497,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
454 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked 497 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
455 : APM::PerformanceMode::Handheld)); 498 : APM::PerformanceMode::Handheld));
456 499
457 LOG_WARNING(Service_AM, "(STUBBED) called"); 500 LOG_DEBUG(Service_AM, "called");
458} 501}
459 502
460class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 503class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
@@ -690,7 +733,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
690 {70, nullptr, "RequestToShutdown"}, 733 {70, nullptr, "RequestToShutdown"},
691 {71, nullptr, "RequestToReboot"}, 734 {71, nullptr, "RequestToReboot"},
692 {80, nullptr, "ExitAndRequestToShowThanksMessage"}, 735 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
693 {90, nullptr, "EnableApplicationCrashReport"}, 736 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
694 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, 737 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
695 {101, nullptr, "SetApplicationCopyrightImage"}, 738 {101, nullptr, "SetApplicationCopyrightImage"},
696 {102, nullptr, "SetApplicationCopyrightVisibility"}, 739 {102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -709,6 +752,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
709 752
710IApplicationFunctions::~IApplicationFunctions() = default; 753IApplicationFunctions::~IApplicationFunctions() = default;
711 754
755void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
756 IPC::ResponseBuilder rb{ctx, 2};
757 rb.Push(RESULT_SUCCESS);
758 LOG_WARNING(Service_AM, "(STUBBED) called");
759}
760
712void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( 761void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
713 Kernel::HLERequestContext& ctx) { 762 Kernel::HLERequestContext& ctx) {
714 IPC::ResponseBuilder rb{ctx, 2}; 763 IPC::ResponseBuilder rb{ctx, 2};
@@ -778,7 +827,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
778 827
779void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 828void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
780 // Takes an input u32 Result, no output. 829 // Takes an input u32 Result, no output.
781 // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. 830 // For example, in some cases official apps use this with error 0x2A2 then
831 // uses svcBreak.
782 832
783 IPC::RequestParser rp{ctx}; 833 IPC::RequestParser rp{ctx};
784 u32 result = rp.Pop<u32>(); 834 u32 result = rp.Pop<u32>();
@@ -840,8 +890,12 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
840 890
841void InstallInterfaces(SM::ServiceManager& service_manager, 891void InstallInterfaces(SM::ServiceManager& service_manager,
842 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 892 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
843 std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); 893 auto message_queue = std::make_shared<AppletMessageQueue>();
844 std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); 894 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
895 // game boot
896
897 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
898 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
845 std::make_shared<IdleSys>()->InstallAsService(service_manager); 899 std::make_shared<IdleSys>()->InstallAsService(service_manager);
846 std::make_shared<OMM>()->InstallAsService(service_manager); 900 std::make_shared<OMM>()->InstallAsService(service_manager);
847 std::make_shared<SPSM>()->InstallAsService(service_manager); 901 std::make_shared<SPSM>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 095f94851..5a3fcba8f 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <queue>
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
10namespace Kernel { 11namespace Kernel {
@@ -39,6 +40,31 @@ enum SystemLanguage {
39 TraditionalChinese = 16, 40 TraditionalChinese = 16,
40}; 41};
41 42
43class AppletMessageQueue {
44public:
45 enum class AppletMessage : u32 {
46 NoMessage = 0,
47 FocusStateChanged = 15,
48 OperationModeChanged = 30,
49 PerformanceModeChanged = 31,
50 };
51
52 AppletMessageQueue();
53 ~AppletMessageQueue();
54
55 const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const;
56 const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const;
57 void PushMessage(AppletMessage msg);
58 AppletMessage PopMessage();
59 std::size_t GetMessageCount() const;
60 void OperationModeChanged();
61
62private:
63 std::queue<AppletMessage> messages;
64 Kernel::SharedPtr<Kernel::Event> on_new_message;
65 Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed;
66};
67
42class IWindowController final : public ServiceFramework<IWindowController> { 68class IWindowController final : public ServiceFramework<IWindowController> {
43public: 69public:
44 IWindowController(); 70 IWindowController();
@@ -102,7 +128,7 @@ private:
102 128
103class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 129class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
104public: 130public:
105 ICommonStateGetter(); 131 explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue);
106 ~ICommonStateGetter() override; 132 ~ICommonStateGetter() override;
107 133
108private: 134private:
@@ -126,6 +152,7 @@ private:
126 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); 152 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
127 153
128 Kernel::SharedPtr<Kernel::Event> event; 154 Kernel::SharedPtr<Kernel::Event> event;
155 std::shared_ptr<AppletMessageQueue> msg_queue;
129}; 156};
130 157
131class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { 158class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
@@ -158,6 +185,7 @@ private:
158 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); 185 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
159 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); 186 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
160 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); 187 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
188 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
161}; 189};
162 190
163class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 191class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 68ea778e8..ec93e3529 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -12,8 +12,10 @@ namespace Service::AM {
12 12
13class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 13class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
14public: 14public:
15 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 15 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
16 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) { 16 std::shared_ptr<AppletMessageQueue> msg_queue)
17 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) {
17 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
18 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 20 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
19 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, 21 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
@@ -32,7 +34,7 @@ private:
32 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { 34 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
33 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 35 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
34 rb.Push(RESULT_SUCCESS); 36 rb.Push(RESULT_SUCCESS);
35 rb.PushIpcInterface<ICommonStateGetter>(); 37 rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
36 LOG_DEBUG(Service_AM, "called"); 38 LOG_DEBUG(Service_AM, "called");
37 } 39 }
38 40
@@ -93,12 +95,15 @@ private:
93 } 95 }
94 96
95 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 97 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
98 std::shared_ptr<AppletMessageQueue> msg_queue;
96}; 99};
97 100
98class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { 101class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
99public: 102public:
100 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 103 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
101 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) { 104 std::shared_ptr<AppletMessageQueue> msg_queue)
105 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
106 msg_queue(std::move(msg_queue)) {
102 static const FunctionInfo functions[] = { 107 static const FunctionInfo functions[] = {
103 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 108 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
104 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, 109 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
@@ -119,7 +124,7 @@ private:
119 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { 124 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
120 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 125 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
121 rb.Push(RESULT_SUCCESS); 126 rb.Push(RESULT_SUCCESS);
122 rb.PushIpcInterface<ICommonStateGetter>(); 127 rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
123 LOG_DEBUG(Service_AM, "called"); 128 LOG_DEBUG(Service_AM, "called");
124 } 129 }
125 130
@@ -186,31 +191,34 @@ private:
186 LOG_DEBUG(Service_AM, "called"); 191 LOG_DEBUG(Service_AM, "called");
187 } 192 }
188 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 193 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
194 std::shared_ptr<AppletMessageQueue> msg_queue;
189}; 195};
190 196
191void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { 197void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
192 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 198 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
193 rb.Push(RESULT_SUCCESS); 199 rb.Push(RESULT_SUCCESS);
194 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger); 200 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
195 LOG_DEBUG(Service_AM, "called"); 201 LOG_DEBUG(Service_AM, "called");
196} 202}
197 203
198void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { 204void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
199 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 205 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
200 rb.Push(RESULT_SUCCESS); 206 rb.Push(RESULT_SUCCESS);
201 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); 207 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
202 LOG_DEBUG(Service_AM, "called"); 208 LOG_DEBUG(Service_AM, "called");
203} 209}
204 210
205void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { 211void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
206 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 212 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
207 rb.Push(RESULT_SUCCESS); 213 rb.Push(RESULT_SUCCESS);
208 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); 214 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
209 LOG_DEBUG(Service_AM, "called"); 215 LOG_DEBUG(Service_AM, "called");
210} 216}
211 217
212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 218AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
213 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { 219 std::shared_ptr<AppletMessageQueue> msg_queue)
220 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
221 msg_queue(std::move(msg_queue)) {
214 // clang-format off 222 // clang-format off
215 static const FunctionInfo functions[] = { 223 static const FunctionInfo functions[] = {
216 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 224 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
@@ -228,4 +236,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
228 236
229AppletAE::~AppletAE() = default; 237AppletAE::~AppletAE() = default;
230 238
239const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
240 return msg_queue;
241}
242
231} // namespace Service::AM 243} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 1ed77baa4..902db2665 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -17,15 +17,19 @@ namespace AM {
17 17
18class AppletAE final : public ServiceFramework<AppletAE> { 18class AppletAE final : public ServiceFramework<AppletAE> {
19public: 19public:
20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
21 std::shared_ptr<AppletMessageQueue> msg_queue);
21 ~AppletAE() override; 22 ~AppletAE() override;
22 23
24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
25
23private: 26private:
24 void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx); 27 void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
25 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); 28 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
26 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); 29 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
27 30
28 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 31 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
32 std::shared_ptr<AppletMessageQueue> msg_queue;
29}; 33};
30 34
31} // namespace AM 35} // namespace AM
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index 60717afd9..20c8d5fff 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -12,8 +12,10 @@ namespace Service::AM {
12 12
13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 14public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
16 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { 16 std::shared_ptr<AppletMessageQueue> msg_queue)
17 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) {
17 // clang-format off 19 // clang-format off
18 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
19 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 21 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -70,7 +72,7 @@ private:
70 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { 72 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
71 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 73 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
72 rb.Push(RESULT_SUCCESS); 74 rb.Push(RESULT_SUCCESS);
73 rb.PushIpcInterface<ICommonStateGetter>(); 75 rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
74 LOG_DEBUG(Service_AM, "called"); 76 LOG_DEBUG(Service_AM, "called");
75 } 77 }
76 78
@@ -89,17 +91,20 @@ private:
89 } 91 }
90 92
91 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 93 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
94 std::shared_ptr<AppletMessageQueue> msg_queue;
92}; 95};
93 96
94void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { 97void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
95 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 98 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
96 rb.Push(RESULT_SUCCESS); 99 rb.Push(RESULT_SUCCESS);
97 rb.PushIpcInterface<IApplicationProxy>(nvflinger); 100 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
98 LOG_DEBUG(Service_AM, "called"); 101 LOG_DEBUG(Service_AM, "called");
99} 102}
100 103
101AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 104AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
102 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) { 105 std::shared_ptr<AppletMessageQueue> msg_queue)
106 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
107 msg_queue(std::move(msg_queue)) {
103 static const FunctionInfo functions[] = { 108 static const FunctionInfo functions[] = {
104 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, 109 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
105 }; 110 };
@@ -108,4 +113,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
108 113
109AppletOE::~AppletOE() = default; 114AppletOE::~AppletOE() = default;
110 115
116const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
117 return msg_queue;
118}
119
111} // namespace Service::AM 120} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 60cfdfd9d..bbd0108ef 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -17,13 +17,17 @@ namespace AM {
17 17
18class AppletOE final : public ServiceFramework<AppletOE> { 18class AppletOE final : public ServiceFramework<AppletOE> {
19public: 19public:
20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
21 std::shared_ptr<AppletMessageQueue> msg_queue);
21 ~AppletOE() override; 22 ~AppletOE() override;
22 23
24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
25
23private: 26private:
24 void OpenApplicationProxy(Kernel::HLERequestContext& ctx); 27 void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
25 28
26 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 29 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
30 std::shared_ptr<AppletMessageQueue> msg_queue;
27}; 31};
28 32
29} // namespace AM 33} // namespace AM
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index fac6785a5..d3ea57ea7 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -28,13 +28,13 @@ public:
28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, 28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, 29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
30 {3, &IAudioRenderer::GetState, "GetState"}, 30 {3, &IAudioRenderer::GetState, "GetState"},
31 {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, 31 {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
32 {5, &IAudioRenderer::Start, "Start"}, 32 {5, &IAudioRenderer::Start, "Start"},
33 {6, &IAudioRenderer::Stop, "Stop"}, 33 {6, &IAudioRenderer::Stop, "Stop"},
34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
35 {8, nullptr, "SetRenderingTimeLimit"}, 35 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
36 {9, nullptr, "GetRenderingTimeLimit"}, 36 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
37 {10, nullptr, "RequestUpdateAuto"}, 37 {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
38 {11, nullptr, "ExecuteAudioRendererRendering"}, 38 {11, nullptr, "ExecuteAudioRendererRendering"},
39 }; 39 };
40 // clang-format on 40 // clang-format on
@@ -79,7 +79,7 @@ private:
79 LOG_DEBUG(Service_Audio, "called"); 79 LOG_DEBUG(Service_Audio, "called");
80 } 80 }
81 81
82 void RequestUpdate(Kernel::HLERequestContext& ctx) { 82 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); 83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
84 IPC::ResponseBuilder rb{ctx, 2}; 84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS); 85 rb.Push(RESULT_SUCCESS);
@@ -110,8 +110,29 @@ private:
110 LOG_WARNING(Service_Audio, "(STUBBED) called"); 110 LOG_WARNING(Service_Audio, "(STUBBED) called");
111 } 111 }
112 112
113 void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
114 IPC::RequestParser rp{ctx};
115 rendering_time_limit_percent = rp.Pop<u32>();
116 ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
117
118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(RESULT_SUCCESS);
120
121 LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
122 rendering_time_limit_percent);
123 }
124
125 void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
126 LOG_DEBUG(Service_Audio, "called");
127
128 IPC::ResponseBuilder rb{ctx, 3};
129 rb.Push(RESULT_SUCCESS);
130 rb.Push(rendering_time_limit_percent);
131 }
132
113 Kernel::SharedPtr<Kernel::Event> system_event; 133 Kernel::SharedPtr<Kernel::Event> system_event;
114 std::unique_ptr<AudioCore::AudioRenderer> renderer; 134 std::unique_ptr<AudioCore::AudioRenderer> renderer;
135 u32 rendering_time_limit_percent = 100;
115}; 136};
116 137
117class IAudioDevice final : public ServiceFramework<IAudioDevice> { 138class IAudioDevice final : public ServiceFramework<IAudioDevice> {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 783c39503..763e619a4 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -77,8 +77,8 @@ private:
77 IPC::ResponseBuilder rb{ctx, 6}; 77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS); 78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed); 79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count); 80 rb.Push<u32>(sample_count);
81 rb.Push<u64>(performance);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 } 83 }
84 84
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index d0a15cc4c..f3bde6d0d 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -2,12 +2,49 @@
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 "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/hle_ipc.h"
5#include "core/hle/service/btdrv/btdrv.h" 9#include "core/hle/service/btdrv/btdrv.h"
6#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
7#include "core/hle/service/sm/sm.h" 11#include "core/hle/service/sm/sm.h"
8 12
9namespace Service::BtDrv { 13namespace Service::BtDrv {
10 14
15class Bt final : public ServiceFramework<Bt> {
16public:
17 explicit Bt() : ServiceFramework{"bt"} {
18 // clang-format off
19 static const FunctionInfo functions[] = {
20 {0, nullptr, "Unknown0"},
21 {1, nullptr, "Unknown1"},
22 {2, nullptr, "Unknown2"},
23 {3, nullptr, "Unknown3"},
24 {4, nullptr, "Unknown4"},
25 {5, nullptr, "Unknown5"},
26 {6, nullptr, "Unknown6"},
27 {7, nullptr, "Unknown7"},
28 {8, nullptr, "Unknown8"},
29 {9, &Bt::RegisterEvent, "RegisterEvent"},
30 };
31 // clang-format on
32 RegisterHandlers(functions);
33 }
34
35private:
36 void RegisterEvent(Kernel::HLERequestContext& ctx) {
37 auto& kernel = Core::System::GetInstance().Kernel();
38 register_event =
39 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
40 IPC::ResponseBuilder rb{ctx, 2, 1};
41 rb.Push(RESULT_SUCCESS);
42 rb.PushCopyObjects(register_event);
43 LOG_WARNING(Service_BTM, "(STUBBED) called");
44 }
45 Kernel::SharedPtr<Kernel::Event> register_event;
46};
47
11class BtDrv final : public ServiceFramework<BtDrv> { 48class BtDrv final : public ServiceFramework<BtDrv> {
12public: 49public:
13 explicit BtDrv() : ServiceFramework{"btdrv"} { 50 explicit BtDrv() : ServiceFramework{"btdrv"} {
@@ -67,6 +104,7 @@ public:
67 104
68void InstallInterfaces(SM::ServiceManager& sm) { 105void InstallInterfaces(SM::ServiceManager& sm) {
69 std::make_shared<BtDrv>()->InstallAsService(sm); 106 std::make_shared<BtDrv>()->InstallAsService(sm);
107 std::make_shared<Bt>()->InstallAsService(sm);
70} 108}
71 109
72} // namespace Service::BtDrv 110} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index b949bfabd..a02f6b53a 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -6,13 +6,118 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/hle_ipc.h" 10#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/btm/btm.h" 11#include "core/hle/service/btm/btm.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13 13
14namespace Service::BTM { 14namespace Service::BTM {
15 15
16class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
17public:
18 explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
22 {1, nullptr, "Unknown1"},
23 {2, nullptr, "Unknown2"},
24 {3, nullptr, "Unknown3"},
25 {4, nullptr, "Unknown4"},
26 {5, nullptr, "Unknown5"},
27 {6, nullptr, "Unknown6"},
28 {7, nullptr, "Unknown7"},
29 {8, nullptr, "Unknown8"},
30 {9, nullptr, "Unknown9"},
31 {10, nullptr, "Unknown10"},
32 {17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
33 {18, nullptr, "Unknown18"},
34 {19, nullptr, "Unknown19"},
35 {20, nullptr, "Unknown20"},
36 {21, nullptr, "Unknown21"},
37 {22, nullptr, "Unknown22"},
38 {23, nullptr, "Unknown23"},
39 {24, nullptr, "Unknown24"},
40 {25, nullptr, "Unknown25"},
41 {26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
42 {27, nullptr, "Unknown27"},
43 {28, nullptr, "Unknown28"},
44 {29, nullptr, "Unknown29"},
45 {30, nullptr, "Unknown30"},
46 {31, nullptr, "Unknown31"},
47 {32, nullptr, "Unknown32"},
48 {33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
49 {34, nullptr, "Unknown34"},
50 {35, nullptr, "Unknown35"},
51 {36, nullptr, "Unknown36"},
52 {37, nullptr, "Unknown37"},
53 };
54 // clang-format on
55 RegisterHandlers(functions);
56 }
57
58private:
59 void GetScanEvent(Kernel::HLERequestContext& ctx) {
60 auto& kernel = Core::System::GetInstance().Kernel();
61 scan_event =
62 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
63 IPC::ResponseBuilder rb{ctx, 2, 1};
64 rb.Push(RESULT_SUCCESS);
65 rb.PushCopyObjects(scan_event);
66 LOG_WARNING(Service_BTM, "(STUBBED) called");
67 }
68 void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
69 auto& kernel = Core::System::GetInstance().Kernel();
70 connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
71 "IBtmUserCore:ConnectionEvent");
72 IPC::ResponseBuilder rb{ctx, 2, 1};
73 rb.Push(RESULT_SUCCESS);
74 rb.PushCopyObjects(connection_event);
75 LOG_WARNING(Service_BTM, "(STUBBED) called");
76 }
77 void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
78 auto& kernel = Core::System::GetInstance().Kernel();
79 service_discovery =
80 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
81 IPC::ResponseBuilder rb{ctx, 2, 1};
82 rb.Push(RESULT_SUCCESS);
83 rb.PushCopyObjects(service_discovery);
84 LOG_WARNING(Service_BTM, "(STUBBED) called");
85 }
86 void GetConfigEvent(Kernel::HLERequestContext& ctx) {
87 auto& kernel = Core::System::GetInstance().Kernel();
88 config_event =
89 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
90 IPC::ResponseBuilder rb{ctx, 2, 1};
91 rb.Push(RESULT_SUCCESS);
92 rb.PushCopyObjects(config_event);
93 LOG_WARNING(Service_BTM, "(STUBBED) called");
94 }
95 Kernel::SharedPtr<Kernel::Event> scan_event;
96 Kernel::SharedPtr<Kernel::Event> connection_event;
97 Kernel::SharedPtr<Kernel::Event> service_discovery;
98 Kernel::SharedPtr<Kernel::Event> config_event;
99};
100
101class BTM_USR final : public ServiceFramework<BTM_USR> {
102public:
103 explicit BTM_USR() : ServiceFramework{"btm:u"} {
104 // clang-format off
105 static const FunctionInfo functions[] = {
106 {0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
107 };
108 // clang-format on
109 RegisterHandlers(functions);
110 }
111
112private:
113 void GetCoreImpl(Kernel::HLERequestContext& ctx) {
114 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
115 rb.Push(RESULT_SUCCESS);
116 rb.PushIpcInterface<IBtmUserCore>();
117 LOG_DEBUG(Service_BTM, "called");
118 }
119};
120
16class BTM final : public ServiceFramework<BTM> { 121class BTM final : public ServiceFramework<BTM> {
17public: 122public:
18 explicit BTM() : ServiceFramework{"btm"} { 123 explicit BTM() : ServiceFramework{"btm"} {
@@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
116 std::make_shared<BTM>()->InstallAsService(sm); 221 std::make_shared<BTM>()->InstallAsService(sm);
117 std::make_shared<BTM_DBG>()->InstallAsService(sm); 222 std::make_shared<BTM_DBG>()->InstallAsService(sm);
118 std::make_shared<BTM_SYS>()->InstallAsService(sm); 223 std::make_shared<BTM_SYS>()->InstallAsService(sm);
224 std::make_shared<BTM_USR>()->InstallAsService(sm);
119} 225}
120 226
121} // namespace Service::BTM 227} // namespace Service::BTM
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index e32a7c48e..5d6294016 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -303,25 +303,42 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
303 static_cast<u8>(space), save_struct.DebugInfo()); 303 static_cast<u8>(space), save_struct.DebugInfo());
304 304
305 if (save_data_factory == nullptr) { 305 if (save_data_factory == nullptr) {
306 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); 306 return FileSys::ERROR_ENTITY_NOT_FOUND;
307 } 307 }
308 308
309 return save_data_factory->Open(space, save_struct); 309 return save_data_factory->Open(space, save_struct);
310} 310}
311 311
312ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
313 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
314
315 if (save_data_factory == nullptr) {
316 return FileSys::ERROR_ENTITY_NOT_FOUND;
317 }
318
319 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
320}
321
312ResultVal<FileSys::VirtualDir> OpenSDMC() { 322ResultVal<FileSys::VirtualDir> OpenSDMC() {
313 LOG_TRACE(Service_FS, "Opening SDMC"); 323 LOG_TRACE(Service_FS, "Opening SDMC");
314 324
315 if (sdmc_factory == nullptr) { 325 if (sdmc_factory == nullptr) {
316 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound); 326 return FileSys::ERROR_SD_CARD_NOT_FOUND;
317 } 327 }
318 328
319 return sdmc_factory->Open(); 329 return sdmc_factory->Open();
320} 330}
321 331
322std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { 332std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union;
323 return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ 333
324 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); 334std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
335 if (registered_cache_union == nullptr) {
336 registered_cache_union =
337 std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
338 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
339 }
340
341 return registered_cache_union;
325} 342}
326 343
327FileSys::RegisteredCache* GetSystemNANDContents() { 344FileSys::RegisteredCache* GetSystemNANDContents() {
@@ -360,6 +377,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
360 return bis_factory->GetModificationLoadRoot(title_id); 377 return bis_factory->GetModificationLoadRoot(title_id);
361} 378}
362 379
380FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
381 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
382
383 if (bis_factory == nullptr)
384 return nullptr;
385
386 return bis_factory->GetModificationDumpRoot(title_id);
387}
388
363void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 389void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
364 if (overwrite) { 390 if (overwrite) {
365 bis_factory = nullptr; 391 bis_factory = nullptr;
@@ -373,13 +399,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
373 FileSys::Mode::ReadWrite); 399 FileSys::Mode::ReadWrite);
374 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 400 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
375 FileSys::Mode::ReadWrite); 401 FileSys::Mode::ReadWrite);
402 auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
403 FileSys::Mode::ReadWrite);
376 404
377 if (bis_factory == nullptr) 405 if (bis_factory == nullptr) {
378 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); 406 bis_factory =
379 if (save_data_factory == nullptr) 407 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
408 }
409
410 if (save_data_factory == nullptr) {
380 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); 411 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
381 if (sdmc_factory == nullptr) 412 }
413
414 if (sdmc_factory == nullptr) {
382 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 415 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
416 }
383} 417}
384 418
385void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { 419void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 6ca5c5636..ff9182e84 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -45,15 +45,17 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
45 FileSys::ContentRecordType type); 45 FileSys::ContentRecordType type);
46ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, 46ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
47 FileSys::SaveDataDescriptor save_struct); 47 FileSys::SaveDataDescriptor save_struct);
48ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
48ResultVal<FileSys::VirtualDir> OpenSDMC(); 49ResultVal<FileSys::VirtualDir> OpenSDMC();
49 50
50std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 51std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
51 52
52FileSys::RegisteredCache* GetSystemNANDContents(); 53FileSys::RegisteredCache* GetSystemNANDContents();
53FileSys::RegisteredCache* GetUserNANDContents(); 54FileSys::RegisteredCache* GetUserNANDContents();
54FileSys::RegisteredCache* GetSDMCContents(); 55FileSys::RegisteredCache* GetSDMCContents();
55 56
56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 57FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
58FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
57 59
58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 60// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
59// above is called. 61// above is called.
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index c1c83a11d..038dc80b1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -11,6 +11,7 @@
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/hex_util.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "common/string_util.h" 16#include "common/string_util.h"
16#include "core/file_sys/directory.h" 17#include "core/file_sys/directory.h"
@@ -62,12 +63,12 @@ private:
62 // Error checking 63 // Error checking
63 if (length < 0) { 64 if (length < 0) {
64 IPC::ResponseBuilder rb{ctx, 2}; 65 IPC::ResponseBuilder rb{ctx, 2};
65 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 66 rb.Push(FileSys::ERROR_INVALID_SIZE);
66 return; 67 return;
67 } 68 }
68 if (offset < 0) { 69 if (offset < 0) {
69 IPC::ResponseBuilder rb{ctx, 2}; 70 IPC::ResponseBuilder rb{ctx, 2};
70 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 71 rb.Push(FileSys::ERROR_INVALID_OFFSET);
71 return; 72 return;
72 } 73 }
73 74
@@ -107,12 +108,12 @@ private:
107 // Error checking 108 // Error checking
108 if (length < 0) { 109 if (length < 0) {
109 IPC::ResponseBuilder rb{ctx, 2}; 110 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 111 rb.Push(FileSys::ERROR_INVALID_SIZE);
111 return; 112 return;
112 } 113 }
113 if (offset < 0) { 114 if (offset < 0) {
114 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 116 rb.Push(FileSys::ERROR_INVALID_OFFSET);
116 return; 117 return;
117 } 118 }
118 119
@@ -138,12 +139,12 @@ private:
138 // Error checking 139 // Error checking
139 if (length < 0) { 140 if (length < 0) {
140 IPC::ResponseBuilder rb{ctx, 2}; 141 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 142 rb.Push(FileSys::ERROR_INVALID_SIZE);
142 return; 143 return;
143 } 144 }
144 if (offset < 0) { 145 if (offset < 0) {
145 IPC::ResponseBuilder rb{ctx, 2}; 146 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 147 rb.Push(FileSys::ERROR_INVALID_OFFSET);
147 return; 148 return;
148 } 149 }
149 150
@@ -451,7 +452,147 @@ private:
451 VfsDirectoryServiceWrapper backend; 452 VfsDirectoryServiceWrapper backend;
452}; 453};
453 454
455class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
456public:
457 explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
458 : ServiceFramework("ISaveDataInfoReader") {
459 static const FunctionInfo functions[] = {
460 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
461 };
462 RegisterHandlers(functions);
463
464 FindAllSaves(space);
465 }
466
467 void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
468 // Calculate how many entries we can fit in the output buffer
469 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
470
471 // Cap at total number of entries.
472 const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
473
474 // Determine data start and end
475 const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
476 const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
477 const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
478
479 next_entry_index += actual_entries;
480
481 // Write the data to memory
482 ctx.WriteBuffer(begin, range_size);
483
484 IPC::ResponseBuilder rb{ctx, 3};
485 rb.Push(RESULT_SUCCESS);
486 rb.Push<u32>(static_cast<u32>(actual_entries));
487 }
488
489private:
490 static u64 stoull_be(std::string_view str) {
491 if (str.size() != 16)
492 return 0;
493
494 const auto bytes = Common::HexStringToArray<0x8>(str);
495 u64 out{};
496 std::memcpy(&out, bytes.data(), sizeof(u64));
497
498 return Common::swap64(out);
499 }
500
501 void FindAllSaves(FileSys::SaveDataSpaceId space) {
502 const auto save_root = OpenSaveDataSpace(space);
503 ASSERT(save_root.Succeeded());
504
505 for (const auto& type : (*save_root)->GetSubdirectories()) {
506 if (type->GetName() == "save") {
507 for (const auto& save_id : type->GetSubdirectories()) {
508 for (const auto& user_id : save_id->GetSubdirectories()) {
509 const auto save_id_numeric = stoull_be(save_id->GetName());
510 auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
511 std::reverse(user_id_numeric.begin(), user_id_numeric.end());
512
513 if (save_id_numeric != 0) {
514 // System Save Data
515 info.emplace_back(SaveDataInfo{
516 0,
517 space,
518 FileSys::SaveDataType::SystemSaveData,
519 {},
520 user_id_numeric,
521 save_id_numeric,
522 0,
523 user_id->GetSize(),
524 {},
525 });
526
527 continue;
528 }
529
530 for (const auto& title_id : user_id->GetSubdirectories()) {
531 const auto device =
532 std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
533 [](u8 val) { return val == 0; });
534 info.emplace_back(SaveDataInfo{
535 0,
536 space,
537 device ? FileSys::SaveDataType::DeviceSaveData
538 : FileSys::SaveDataType::SaveData,
539 {},
540 user_id_numeric,
541 save_id_numeric,
542 stoull_be(title_id->GetName()),
543 title_id->GetSize(),
544 {},
545 });
546 }
547 }
548 }
549 } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
550 // Temporary Storage
551 for (const auto& user_id : type->GetSubdirectories()) {
552 for (const auto& title_id : user_id->GetSubdirectories()) {
553 if (!title_id->GetFiles().empty() ||
554 !title_id->GetSubdirectories().empty()) {
555 auto user_id_numeric =
556 Common::HexStringToArray<0x10>(user_id->GetName());
557 std::reverse(user_id_numeric.begin(), user_id_numeric.end());
558
559 info.emplace_back(SaveDataInfo{
560 0,
561 space,
562 FileSys::SaveDataType::TemporaryStorage,
563 {},
564 user_id_numeric,
565 stoull_be(type->GetName()),
566 stoull_be(title_id->GetName()),
567 title_id->GetSize(),
568 {},
569 });
570 }
571 }
572 }
573 }
574 }
575 }
576
577 struct SaveDataInfo {
578 u64_le save_id_unknown;
579 FileSys::SaveDataSpaceId space;
580 FileSys::SaveDataType type;
581 INSERT_PADDING_BYTES(0x6);
582 std::array<u8, 0x10> user_id;
583 u64_le save_id;
584 u64_le title_id;
585 u64_le save_image_size;
586 INSERT_PADDING_BYTES(0x28);
587 };
588 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
589
590 std::vector<SaveDataInfo> info;
591 u64 next_entry_index = 0;
592};
593
454FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { 594FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
595 // clang-format off
455 static const FunctionInfo functions[] = { 596 static const FunctionInfo functions[] = {
456 {0, nullptr, "MountContent"}, 597 {0, nullptr, "MountContent"},
457 {1, &FSP_SRV::Initialize, "Initialize"}, 598 {1, &FSP_SRV::Initialize, "Initialize"},
@@ -485,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
485 {58, nullptr, "ReadSaveDataFileSystemExtraData"}, 626 {58, nullptr, "ReadSaveDataFileSystemExtraData"},
486 {59, nullptr, "WriteSaveDataFileSystemExtraData"}, 627 {59, nullptr, "WriteSaveDataFileSystemExtraData"},
487 {60, nullptr, "OpenSaveDataInfoReader"}, 628 {60, nullptr, "OpenSaveDataInfoReader"},
488 {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, 629 {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
489 {62, nullptr, "OpenCacheStorageList"}, 630 {62, nullptr, "OpenCacheStorageList"},
490 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, 631 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
491 {65, nullptr, "UpdateSaveDataMacForDebug"}, 632 {65, nullptr, "UpdateSaveDataMacForDebug"},
@@ -544,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
544 {1009, nullptr, "GetAndClearMemoryReportInfo"}, 685 {1009, nullptr, "GetAndClearMemoryReportInfo"},
545 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 686 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
546 }; 687 };
688 // clang-format on
547 RegisterHandlers(functions); 689 RegisterHandlers(functions);
548} 690}
549 691
@@ -602,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
602 744
603 if (dir.Failed()) { 745 if (dir.Failed()) {
604 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 746 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
605 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 747 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
606 return; 748 return;
607 } 749 }
608 750
@@ -618,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
618 MountSaveData(ctx); 760 MountSaveData(ctx);
619} 761}
620 762
763void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
764 IPC::RequestParser rp{ctx};
765 const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
766
767 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
768 rb.Push(RESULT_SUCCESS);
769 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
770}
771
621void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 772void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
622 LOG_WARNING(Service_FS, "(STUBBED) called"); 773 LOG_WARNING(Service_FS, "(STUBBED) called");
623 774
@@ -685,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
685 static_cast<u8>(storage_id), title_id); 836 static_cast<u8>(storage_id), title_id);
686 837
687 IPC::ResponseBuilder rb{ctx, 2}; 838 IPC::ResponseBuilder rb{ctx, 2};
688 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 839 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
689} 840}
690 841
691} // namespace Service::FileSystem 842} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 4aa0358cb..e7abec0a3 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -25,6 +25,7 @@ private:
25 void CreateSaveData(Kernel::HLERequestContext& ctx); 25 void CreateSaveData(Kernel::HLERequestContext& ctx);
26 void MountSaveData(Kernel::HLERequestContext& ctx); 26 void MountSaveData(Kernel::HLERequestContext& ctx);
27 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); 27 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
28 void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
28 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); 29 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
29 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 30 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
30 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); 31 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 4b4d1324f..205e4fd14 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -40,6 +40,29 @@ enum class JoystickId : std::size_t {
40 Joystick_Right, 40 Joystick_Right,
41}; 41};
42 42
43static std::size_t NPadIdToIndex(u32 npad_id) {
44 switch (npad_id) {
45 case 0:
46 case 1:
47 case 2:
48 case 3:
49 case 4:
50 case 5:
51 case 6:
52 case 7:
53 return npad_id;
54 case 8:
55 case NPAD_HANDHELD:
56 return 8;
57 case 9:
58 case NPAD_UNKNOWN:
59 return 9;
60 default:
61 UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
62 return 0;
63 }
64}
65
43Controller_NPad::Controller_NPad() = default; 66Controller_NPad::Controller_NPad() = default;
44Controller_NPad::~Controller_NPad() = default; 67Controller_NPad::~Controller_NPad() = default;
45 68
@@ -288,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
288 switch (controller_type) { 311 switch (controller_type) {
289 case NPadControllerType::Handheld: 312 case NPadControllerType::Handheld:
290 handheld_entry.connection_status.raw = 0; 313 handheld_entry.connection_status.raw = 0;
291 handheld_entry.connection_status.IsConnected.Assign(1); 314 handheld_entry.connection_status.IsWired.Assign(1);
292 if (!Settings::values.use_docked_mode) { 315 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
293 handheld_entry.connection_status.IsWired.Assign(1); 316 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
294 } 317 handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
318 handheld_entry.connection_status.IsRightJoyWired.Assign(1);
295 handheld_entry.pad_states.raw = pad_state.raw; 319 handheld_entry.pad_states.raw = pad_state.raw;
296 handheld_entry.l_stick = lstick_entry; 320 handheld_entry.l_stick = lstick_entry;
297 handheld_entry.r_stick = rstick_entry; 321 handheld_entry.r_stick = rstick_entry;
@@ -310,6 +334,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
310 dual_entry.pad_states.raw = pad_state.raw; 334 dual_entry.pad_states.raw = pad_state.raw;
311 dual_entry.l_stick = lstick_entry; 335 dual_entry.l_stick = lstick_entry;
312 dual_entry.r_stick = rstick_entry; 336 dual_entry.r_stick = rstick_entry;
337 break;
313 case NPadControllerType::JoyLeft: 338 case NPadControllerType::JoyLeft:
314 left_entry.connection_status.raw = 0; 339 left_entry.connection_status.raw = 0;
315 340
@@ -370,16 +395,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
370 supported_npad_id_types.clear(); 395 supported_npad_id_types.clear();
371 supported_npad_id_types.resize(length / sizeof(u32)); 396 supported_npad_id_types.resize(length / sizeof(u32));
372 std::memcpy(supported_npad_id_types.data(), data, length); 397 std::memcpy(supported_npad_id_types.data(), data, length);
398 bool had_controller_update = false;
373 for (std::size_t i = 0; i < connected_controllers.size(); i++) { 399 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
374 auto& controller = connected_controllers[i]; 400 auto& controller = connected_controllers[i];
375 if (!controller.is_connected) { 401 if (!controller.is_connected) {
376 continue; 402 continue;
377 } 403 }
378 if (!IsControllerSupported(PREFERRED_CONTROLLER)) { 404 if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
379 controller.type = DecideBestController(PREFERRED_CONTROLLER); 405 const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
380 InitNewlyAddedControler(i); 406 const bool is_handheld = (best_type == NPadControllerType::Handheld ||
407 PREFERRED_CONTROLLER == NPadControllerType::Handheld);
408 if (is_handheld) {
409 controller.type = NPadControllerType::None;
410 controller.is_connected = false;
411 AddNewController(best_type);
412 } else {
413 controller.type = best_type;
414 InitNewlyAddedControler(i);
415 }
416 had_controller_update = true;
381 } 417 }
382 } 418 }
419 if (had_controller_update) {
420 styleset_changed_event->Signal();
421 }
383} 422}
384 423
385void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 424void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -392,8 +431,10 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
392} 431}
393 432
394void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 433void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
434 styleset_changed_event->Signal();
395 hold_type = joy_hold_type; 435 hold_type = joy_hold_type;
396} 436}
437
397Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { 438Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
398 return hold_type; 439 return hold_type;
399} 440}
@@ -427,6 +468,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
427} 468}
428 469
429Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { 470Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
471 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
472 // be signalled at least once, and signaled after a new controller is connected?
473 styleset_changed_event->Signal();
430 return styleset_changed_event; 474 return styleset_changed_event;
431} 475}
432 476
@@ -452,15 +496,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
452} 496}
453 497
454void Controller_NPad::ConnectNPad(u32 npad_id) { 498void Controller_NPad::ConnectNPad(u32 npad_id) {
455 if (npad_id >= connected_controllers.size()) 499 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
456 return;
457 connected_controllers[npad_id].is_connected = true;
458} 500}
459 501
460void Controller_NPad::DisconnectNPad(u32 npad_id) { 502void Controller_NPad::DisconnectNPad(u32 npad_id) {
461 if (npad_id >= connected_controllers.size()) 503 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
462 return;
463 connected_controllers[npad_id].is_connected = false;
464} 504}
465 505
466Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 506Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index a9aa9ec78..39631b14f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -96,6 +96,8 @@ public:
96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
97 97
98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); 98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
99
100 ReloadInputDevices();
99 } 101 }
100 102
101 void ActivateController(HidController controller) { 103 void ActivateController(HidController controller) {
@@ -284,10 +286,10 @@ public:
284 {519, nullptr, "GetPalmaOperationResult"}, 286 {519, nullptr, "GetPalmaOperationResult"},
285 {520, nullptr, "ReadPalmaPlayLog"}, 287 {520, nullptr, "ReadPalmaPlayLog"},
286 {521, nullptr, "ResetPalmaPlayLog"}, 288 {521, nullptr, "ResetPalmaPlayLog"},
287 {522, nullptr, "SetIsPalmaAllConnectable"}, 289 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
288 {523, nullptr, "SetIsPalmaPairedConnectable"}, 290 {523, nullptr, "SetIsPalmaPairedConnectable"},
289 {524, nullptr, "PairPalma"}, 291 {524, nullptr, "PairPalma"},
290 {525, nullptr, "SetPalmaBoostMode"}, 292 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
291 {1000, nullptr, "SetNpadCommunicationMode"}, 293 {1000, nullptr, "SetNpadCommunicationMode"},
292 {1001, nullptr, "GetNpadCommunicationMode"}, 294 {1001, nullptr, "GetNpadCommunicationMode"},
293 }; 295 };
@@ -594,6 +596,18 @@ private:
594 rb.Push(RESULT_SUCCESS); 596 rb.Push(RESULT_SUCCESS);
595 LOG_WARNING(Service_HID, "(STUBBED) called"); 597 LOG_WARNING(Service_HID, "(STUBBED) called");
596 } 598 }
599
600 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
601 IPC::ResponseBuilder rb{ctx, 2};
602 rb.Push(RESULT_SUCCESS);
603 LOG_WARNING(Service_HID, "(STUBBED) called");
604 }
605
606 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
607 IPC::ResponseBuilder rb{ctx, 2};
608 rb.Push(RESULT_SUCCESS);
609 LOG_WARNING(Service_HID, "(STUBBED) called");
610 }
597}; 611};
598 612
599class HidDbg final : public ServiceFramework<HidDbg> { 613class HidDbg final : public ServiceFramework<HidDbg> {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index d607d985e..b43f1f054 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -4,7 +4,10 @@
4 4
5#include <memory> 5#include <memory>
6#include <fmt/format.h> 6#include <fmt/format.h>
7#include <mbedtls/sha256.h>
7 8
9#include "common/alignment.h"
10#include "common/hex_util.h"
8#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
10#include "core/hle/service/ldr/ldr.h" 13#include "core/hle/service/ldr/ldr.h"
@@ -13,6 +16,38 @@
13 16
14namespace Service::LDR { 17namespace Service::LDR {
15 18
19namespace ErrCodes {
20enum {
21 InvalidMemoryState = 51,
22 InvalidNRO = 52,
23 InvalidNRR = 53,
24 MissingNRRHash = 54,
25 MaximumNRO = 55,
26 MaximumNRR = 56,
27 AlreadyLoaded = 57,
28 InvalidAlignment = 81,
29 InvalidSize = 82,
30 InvalidNROAddress = 84,
31 InvalidNRRAddress = 85,
32 NotInitialized = 87,
33};
34}
35
36constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
37constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
38constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
39constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
40constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
41constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
42constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
43constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
44constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
45constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
46constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
47constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
48
49constexpr u64 MAXIMUM_LOADED_RO = 0x40;
50
16class DebugMonitor final : public ServiceFramework<DebugMonitor> { 51class DebugMonitor final : public ServiceFramework<DebugMonitor> {
17public: 52public:
18 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { 53 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -64,9 +99,9 @@ public:
64 // clang-format off 99 // clang-format off
65 static const FunctionInfo functions[] = { 100 static const FunctionInfo functions[] = {
66 {0, &RelocatableObject::LoadNro, "LoadNro"}, 101 {0, &RelocatableObject::LoadNro, "LoadNro"},
67 {1, nullptr, "UnloadNro"}, 102 {1, &RelocatableObject::UnloadNro, "UnloadNro"},
68 {2, &RelocatableObject::LoadNrr, "LoadNrr"}, 103 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
69 {3, nullptr, "UnloadNrr"}, 104 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
70 {4, &RelocatableObject::Initialize, "Initialize"}, 105 {4, &RelocatableObject::Initialize, "Initialize"},
71 }; 106 };
72 // clang-format on 107 // clang-format on
@@ -75,9 +110,123 @@ public:
75 } 110 }
76 111
77 void LoadNrr(Kernel::HLERequestContext& ctx) { 112 void LoadNrr(Kernel::HLERequestContext& ctx) {
113 IPC::RequestParser rp{ctx};
114 rp.Skip(2, false);
115 const VAddr nrr_addr{rp.Pop<VAddr>()};
116 const u64 nrr_size{rp.Pop<u64>()};
117
118 if (!initialized) {
119 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(ERROR_NOT_INITIALIZED);
122 return;
123 }
124
125 if (nrr.size() >= MAXIMUM_LOADED_RO) {
126 LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
127 "(0x40)! Failing...");
128 IPC::ResponseBuilder rb{ctx, 2};
129 rb.Push(ERROR_MAXIMUM_NRR);
130 return;
131 }
132
133 // NRR Address does not fall on 0x1000 byte boundary
134 if (!Common::Is4KBAligned(nrr_addr)) {
135 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
136 IPC::ResponseBuilder rb{ctx, 2};
137 rb.Push(ERROR_INVALID_ALIGNMENT);
138 return;
139 }
140
141 // NRR Size is zero or causes overflow
142 if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
143 LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
144 nrr_addr, nrr_size);
145 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ERROR_INVALID_SIZE);
147 return;
148 }
149 // Read NRR data from memory
150 std::vector<u8> nrr_data(nrr_size);
151 Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
152 NRRHeader header;
153 std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
154
155 if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
156 LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
157 IPC::ResponseBuilder rb{ctx, 2};
158 rb.Push(ERROR_INVALID_NRR);
159 return;
160 }
161
162 if (header.size != nrr_size) {
163 LOG_ERROR(Service_LDR,
164 "NRR header reported size did not match LoadNrr parameter size! "
165 "(header_size={:016X}, loadnrr_size={:016X})",
166 header.size, nrr_size);
167 IPC::ResponseBuilder rb{ctx, 2};
168 rb.Push(ERROR_INVALID_SIZE);
169 return;
170 }
171
172 if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
173 LOG_ERROR(Service_LDR,
174 "Attempting to load NRR with title ID other than current process. (actual "
175 "{:016X})!",
176 header.title_id);
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(ERROR_INVALID_NRR);
179 return;
180 }
181
182 std::vector<SHA256Hash> hashes;
183
184 // Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
185 for (std::size_t i = header.hash_offset;
186 i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
187 SHA256Hash hash;
188 std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
189 hashes.emplace_back(hash);
190 }
191
192 nrr.insert_or_assign(nrr_addr, std::move(hashes));
193
194 IPC::ResponseBuilder rb{ctx, 2};
195 rb.Push(RESULT_SUCCESS);
196 }
197
198 void UnloadNrr(Kernel::HLERequestContext& ctx) {
199 if (!initialized) {
200 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
201 IPC::ResponseBuilder rb{ctx, 2};
202 rb.Push(ERROR_NOT_INITIALIZED);
203 return;
204 }
205
206 IPC::RequestParser rp{ctx};
207 rp.Skip(2, false);
208 const auto nrr_addr{rp.Pop<VAddr>()};
209
210 if (!Common::Is4KBAligned(nrr_addr)) {
211 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
212 IPC::ResponseBuilder rb{ctx, 2};
213 rb.Push(ERROR_INVALID_ALIGNMENT);
214 return;
215 }
216
217 const auto iter = nrr.find(nrr_addr);
218 if (iter == nrr.end()) {
219 LOG_ERROR(Service_LDR,
220 "Attempting to unload NRR which has not been loaded! (addr={:016X})",
221 nrr_addr);
222 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ERROR_INVALID_NRR_ADDRESS);
224 return;
225 }
226
227 nrr.erase(iter);
78 IPC::ResponseBuilder rb{ctx, 2}; 228 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS); 229 rb.Push(RESULT_SUCCESS);
80 LOG_WARNING(Service_LDR, "(STUBBED) called");
81 } 230 }
82 231
83 void LoadNro(Kernel::HLERequestContext& ctx) { 232 void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -88,33 +237,253 @@ public:
88 const VAddr bss_addr{rp.Pop<VAddr>()}; 237 const VAddr bss_addr{rp.Pop<VAddr>()};
89 const u64 bss_size{rp.Pop<u64>()}; 238 const u64 bss_size{rp.Pop<u64>()};
90 239
240 if (!initialized) {
241 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
242 IPC::ResponseBuilder rb{ctx, 2};
243 rb.Push(ERROR_NOT_INITIALIZED);
244 return;
245 }
246
247 if (nro.size() >= MAXIMUM_LOADED_RO) {
248 LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
249 "(0x40)! Failing...");
250 IPC::ResponseBuilder rb{ctx, 2};
251 rb.Push(ERROR_MAXIMUM_NRO);
252 return;
253 }
254
255 // NRO Address does not fall on 0x1000 byte boundary
256 if (!Common::Is4KBAligned(nro_addr)) {
257 LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
258 IPC::ResponseBuilder rb{ctx, 2};
259 rb.Push(ERROR_INVALID_ALIGNMENT);
260 return;
261 }
262
263 // NRO Size or BSS Size is zero or causes overflow
264 const auto nro_size_valid =
265 nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
266 const auto bss_size_valid =
267 nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
268
269 if (!nro_size_valid || !bss_size_valid) {
270 LOG_ERROR(Service_LDR,
271 "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
272 "bss_address={:016X}, bss_size={:016X})",
273 nro_addr, nro_size, bss_addr, bss_size);
274 IPC::ResponseBuilder rb{ctx, 2};
275 rb.Push(ERROR_INVALID_SIZE);
276 return;
277 }
278
91 // Read NRO data from memory 279 // Read NRO data from memory
92 std::vector<u8> nro_data(nro_size); 280 std::vector<u8> nro_data(nro_size);
93 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); 281 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
94 282
283 SHA256Hash hash{};
284 mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
285
286 // NRO Hash is already loaded
287 if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
288 return info.second.hash == hash;
289 })) {
290 LOG_ERROR(Service_LDR, "NRO is already loaded!");
291 IPC::ResponseBuilder rb{ctx, 2};
292 rb.Push(ERROR_ALREADY_LOADED);
293 return;
294 }
295
296 // NRO Hash is not in any loaded NRR
297 if (!IsValidNROHash(hash)) {
298 LOG_ERROR(Service_LDR,
299 "NRO hash is not present in any currently loaded NRRs (hash={})!",
300 Common::HexArrayToString(hash));
301 IPC::ResponseBuilder rb{ctx, 2};
302 rb.Push(ERROR_MISSING_NRR_HASH);
303 return;
304 }
305
306 NROHeader header;
307 std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
308
309 if (!IsValidNRO(header, nro_size, bss_size)) {
310 LOG_ERROR(Service_LDR, "NRO was invalid!");
311 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(ERROR_INVALID_NRO);
313 return;
314 }
315
95 // Load NRO as new executable module 316 // Load NRO as new executable module
96 const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; 317 auto* process = Core::CurrentProcess();
97 Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); 318 auto& vm_manager = process->VMManager();
319 auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
320
321 if (!map_address.Succeeded() ||
322 *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
323
324 LOG_ERROR(Service_LDR,
325 "General error while allocation memory or no available memory to allocate!");
326 IPC::ResponseBuilder rb{ctx, 2};
327 rb.Push(ERROR_INVALID_MEMORY_STATE);
328 return;
329 }
330
331 ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
332 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
333 ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
334
335 if (bss_size > 0) {
336 ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
337 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
338 ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
339 }
340
341 vm_manager.ReprotectRange(*map_address, header.text_size,
342 Kernel::VMAPermission::ReadExecute);
343 vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
344 Kernel::VMAPermission::Read);
345 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
346 Kernel::VMAPermission::ReadWrite);
98 347
99 // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. 348 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
100 // It is currently missing: 349 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
101 // - Signature checks with LoadNRR 350 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
102 // - Checking if a module has already been loaded 351 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
103 // - Using/validating BSS, etc. params (these are used from NRO header instead) 352
104 // - Error checking 353 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
105 // - ...Probably other things
106 354
107 IPC::ResponseBuilder rb{ctx, 4}; 355 IPC::ResponseBuilder rb{ctx, 4};
108 rb.Push(RESULT_SUCCESS); 356 rb.Push(RESULT_SUCCESS);
109 rb.Push(addr); 357 rb.Push(*map_address);
110 LOG_WARNING(Service_LDR, "(STUBBED) called"); 358 }
359
360 void UnloadNro(Kernel::HLERequestContext& ctx) {
361 IPC::RequestParser rp{ctx};
362 rp.Skip(2, false);
363 const VAddr mapped_addr{rp.PopRaw<VAddr>()};
364 const VAddr heap_addr{rp.PopRaw<VAddr>()};
365
366 if (!initialized) {
367 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
368 IPC::ResponseBuilder rb{ctx, 2};
369 rb.Push(ERROR_NOT_INITIALIZED);
370 return;
371 }
372
373 if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
374 LOG_ERROR(Service_LDR,
375 "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
376 "bss_addr={:016X})!",
377 mapped_addr, heap_addr);
378 IPC::ResponseBuilder rb{ctx, 2};
379 rb.Push(ERROR_INVALID_ALIGNMENT);
380 return;
381 }
382
383 const auto iter = nro.find(mapped_addr);
384 if (iter == nro.end()) {
385 LOG_ERROR(Service_LDR,
386 "The NRO attempting to unmap was not mapped or has an invalid address "
387 "(actual {:016X})!",
388 mapped_addr);
389 IPC::ResponseBuilder rb{ctx, 2};
390 rb.Push(ERROR_INVALID_NRO_ADDRESS);
391 return;
392 }
393
394 auto* process = Core::CurrentProcess();
395 auto& vm_manager = process->VMManager();
396 const auto& nro_size = iter->second.size;
397
398 ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
399 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
400 ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
401
402 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
403 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
404 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
405 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
406
407 nro.erase(iter);
408 IPC::ResponseBuilder rb{ctx, 2};
409 rb.Push(RESULT_SUCCESS);
111 } 410 }
112 411
113 void Initialize(Kernel::HLERequestContext& ctx) { 412 void Initialize(Kernel::HLERequestContext& ctx) {
413 initialized = true;
414
114 IPC::ResponseBuilder rb{ctx, 2}; 415 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(RESULT_SUCCESS); 416 rb.Push(RESULT_SUCCESS);
116 LOG_WARNING(Service_LDR, "(STUBBED) called"); 417 LOG_WARNING(Service_LDR, "(STUBBED) called");
117 } 418 }
419
420private:
421 using SHA256Hash = std::array<u8, 0x20>;
422
423 struct NROHeader {
424 u32_le entrypoint_insn;
425 u32_le mod_offset;
426 INSERT_PADDING_WORDS(2);
427 u32_le magic;
428 INSERT_PADDING_WORDS(1);
429 u32_le nro_size;
430 INSERT_PADDING_WORDS(1);
431 u32_le text_offset;
432 u32_le text_size;
433 u32_le ro_offset;
434 u32_le ro_size;
435 u32_le rw_offset;
436 u32_le rw_size;
437 u32_le bss_size;
438 INSERT_PADDING_WORDS(1);
439 std::array<u8, 0x20> build_id;
440 INSERT_PADDING_BYTES(0x20);
441 };
442 static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
443
444 struct NRRHeader {
445 u32_le magic;
446 INSERT_PADDING_BYTES(0x1C);
447 u64_le title_id_mask;
448 u64_le title_id_pattern;
449 std::array<u8, 0x100> modulus;
450 std::array<u8, 0x100> signature_1;
451 std::array<u8, 0x100> signature_2;
452 u64_le title_id;
453 u32_le size;
454 INSERT_PADDING_BYTES(4);
455 u32_le hash_offset;
456 u32_le hash_count;
457 INSERT_PADDING_BYTES(8);
458 };
459 static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
460
461 struct NROInfo {
462 SHA256Hash hash;
463 u64 size;
464 };
465
466 bool initialized = false;
467
468 std::map<VAddr, NROInfo> nro;
469 std::map<VAddr, std::vector<SHA256Hash>> nrr;
470
471 bool IsValidNROHash(const SHA256Hash& hash) {
472 return std::any_of(
473 nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
474 return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
475 });
476 }
477
478 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
479 return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
480 header.nro_size == nro_size && header.bss_size == bss_size &&
481 header.ro_offset == header.text_offset + header.text_size &&
482 header.rw_offset == header.ro_offset + header.ro_size &&
483 nro_size == header.rw_offset + header.rw_size &&
484 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
485 Common::Is4KBAligned(header.rw_size);
486 }
118}; 487};
119 488
120void InstallInterfaces(SM::ServiceManager& sm) { 489void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index c1af878fe..1d6e7756f 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -212,7 +212,7 @@ private:
212 IPC::ResponseBuilder rb{ctx, 2}; 212 IPC::ResponseBuilder rb{ctx, 2};
213 auto amiibo = nfp_interface.GetAmiiboBuffer(); 213 auto amiibo = nfp_interface.GetAmiiboBuffer();
214 TagInfo tag_info{}; 214 TagInfo tag_info{};
215 std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); 215 tag_info.uuid = amiibo.uuid;
216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); 216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
217 217
218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values 218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 07c1381fe..1d2978f24 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -2,6 +2,9 @@
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 "common/logging/log.h"
6#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h"
5#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/hle_ipc.h" 9#include "core/hle/kernel/hle_ipc.h"
7#include "core/hle/service/ns/ns.h" 10#include "core/hle/service/ns/ns.h"
@@ -118,7 +121,7 @@ public:
118 {305, nullptr, "TerminateSystemApplet"}, 121 {305, nullptr, "TerminateSystemApplet"},
119 {306, nullptr, "LaunchOverlayApplet"}, 122 {306, nullptr, "LaunchOverlayApplet"},
120 {307, nullptr, "TerminateOverlayApplet"}, 123 {307, nullptr, "TerminateOverlayApplet"},
121 {400, nullptr, "GetApplicationControlData"}, 124 {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
122 {401, nullptr, "InvalidateAllApplicationControlCache"}, 125 {401, nullptr, "InvalidateAllApplicationControlCache"},
123 {402, nullptr, "RequestDownloadApplicationControlData"}, 126 {402, nullptr, "RequestDownloadApplicationControlData"},
124 {403, nullptr, "GetMaxApplicationControlCacheCount"}, 127 {403, nullptr, "GetMaxApplicationControlCacheCount"},
@@ -243,6 +246,65 @@ public:
243 246
244 RegisterHandlers(functions); 247 RegisterHandlers(functions);
245 } 248 }
249
250 void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
251 IPC::RequestParser rp{ctx};
252 const auto flag = rp.PopRaw<u64>();
253 LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
254
255 const auto title_id = rp.PopRaw<u64>();
256
257 const auto size = ctx.GetWriteBufferSize();
258
259 const FileSys::PatchManager pm{title_id};
260 const auto control = pm.GetControlMetadata();
261
262 std::vector<u8> out;
263
264 if (control.first != nullptr) {
265 if (size < 0x4000) {
266 LOG_ERROR(Service_NS,
267 "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
268 size);
269 IPC::ResponseBuilder rb{ctx, 2};
270 // TODO(DarkLordZach): Find a better error code for this.
271 rb.Push(ResultCode(-1));
272 return;
273 }
274
275 out.resize(0x4000);
276 const auto bytes = control.first->GetRawBytes();
277 std::memcpy(out.data(), bytes.data(), bytes.size());
278 } else {
279 LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
280 title_id);
281 out.resize(std::min<u64>(0x4000, size));
282 }
283
284 if (control.second != nullptr) {
285 if (size < 0x4000 + control.second->GetSize()) {
286 LOG_ERROR(Service_NS,
287 "output buffer is too small! (actual={:016X}, expected_min={:016X})",
288 size, 0x4000 + control.second->GetSize());
289 IPC::ResponseBuilder rb{ctx, 2};
290 // TODO(DarkLordZach): Find a better error code for this.
291 rb.Push(ResultCode(-1));
292 return;
293 }
294
295 out.resize(0x4000 + control.second->GetSize());
296 control.second->Read(out.data() + 0x4000, control.second->GetSize());
297 } else {
298 LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
299 title_id);
300 }
301
302 ctx.WriteBuffer(out);
303
304 IPC::ResponseBuilder rb{ctx, 3};
305 rb.Push(RESULT_SUCCESS);
306 rb.Push<u32>(static_cast<u32>(out.size()));
307 }
246}; 308};
247 309
248class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { 310class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 44accecb7..1066bf505 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -351,6 +351,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
351 font_sizes.push_back(region.size); 351 font_sizes.push_back(region.size);
352 } 352 }
353 353
354 // Resize buffers if game requests smaller size output.
355 font_codes.resize(
356 std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
357 font_offsets.resize(
358 std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
359 font_sizes.resize(
360 std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
361
354 ctx.WriteBuffer(font_codes, 0); 362 ctx.WriteBuffer(font_codes, 0);
355 ctx.WriteBuffer(font_offsets, 1); 363 ctx.WriteBuffer(font_offsets, 1);
356 ctx.WriteBuffer(font_sizes, 2); 364 ctx.WriteBuffer(font_sizes, 2);
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 2fe81a560..8cff5eb71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -58,9 +58,9 @@ public:
58 /// Rotate source image 90 degrees clockwise 58 /// Rotate source image 90 degrees clockwise
59 Rotate90 = 0x04, 59 Rotate90 = 0x04,
60 /// Rotate source image 180 degrees 60 /// Rotate source image 180 degrees
61 Roate180 = 0x03, 61 Rotate180 = 0x03,
62 /// Rotate source image 270 degrees clockwise 62 /// Rotate source image 270 degrees clockwise
63 Roate270 = 0x07, 63 Rotate270 = 0x07,
64 }; 64 };
65 65
66 struct Buffer { 66 struct Buffer {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index a4cf45267..1ec340466 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -80,8 +80,8 @@ namespace Service {
80 * Creates a function string for logging, complete with the name (or header code, depending 80 * Creates a function string for logging, complete with the name (or header code, depending
81 * on what's passed in) the port name, and all the cmd_buff arguments. 81 * on what's passed in) the port name, and all the cmd_buff arguments.
82 */ 82 */
83static std::string MakeFunctionString(const char* name, const char* port_name, 83[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
84 const u32* cmd_buff) { 84 const u32* cmd_buff) {
85 // Number of params == bits 0-5 + bits 6-11 85 // Number of params == bits 0-5 + bits 6-11
86 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); 86 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
87 87
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 44a6717d0..b2de2a818 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -3,18 +3,23 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <chrono>
6#include <cstdlib> 7#include <cstdlib>
8#include <ctime>
9#include <functional>
7#include <vector> 10#include <vector>
8#include "common/logging/log.h" 11#include "common/logging/log.h"
9#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/spl/csrng.h" 13#include "core/hle/service/spl/csrng.h"
11#include "core/hle/service/spl/module.h" 14#include "core/hle/service/spl/module.h"
12#include "core/hle/service/spl/spl.h" 15#include "core/hle/service/spl/spl.h"
16#include "core/settings.h"
13 17
14namespace Service::SPL { 18namespace Service::SPL {
15 19
16Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 20Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
17 : ServiceFramework(name), module(std::move(module)) {} 21 : ServiceFramework(name), module(std::move(module)),
22 rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
18 23
19Module::Interface::~Interface() = default; 24Module::Interface::~Interface() = default;
20 25
@@ -23,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
23 28
24 std::size_t size = ctx.GetWriteBufferSize(); 29 std::size_t size = ctx.GetWriteBufferSize();
25 30
31 std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
26 std::vector<u8> data(size); 32 std::vector<u8> data(size);
27 std::generate(data.begin(), data.end(), std::rand); 33 std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
28 34
29 ctx.WriteBuffer(data); 35 ctx.WriteBuffer(data);
30 36
diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h
index 48fda6099..afa1f0295 100644
--- a/src/core/hle/service/spl/module.h
+++ b/src/core/hle/service/spl/module.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <random>
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service::SPL { 10namespace Service::SPL {
@@ -19,6 +20,9 @@ public:
19 20
20 protected: 21 protected:
21 std::shared_ptr<Module> module; 22 std::shared_ptr<Module> module;
23
24 private:
25 std::mt19937 rng;
22 }; 26 };
23}; 27};
24 28
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 18a5d71d5..b3a196f65 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -21,9 +21,10 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
21 {102, nullptr, "GetStandardUserSystemClockInitialYear"}, 21 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, 22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, nullptr, "GetClockSnapshot"}, 24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, 26 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
27 "CalculateStandardUserSystemClockDifferenceByUser"},
27 {501, nullptr, "CalculateSpanBetween"}, 28 {501, nullptr, "CalculateSpanBetween"},
28 }; 29 };
29 RegisterHandlers(functions); 30 RegisterHandlers(functions);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 28fd8debc..e561a0c52 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -15,6 +15,44 @@
15 15
16namespace Service::Time { 16namespace Service::Time {
17 17
18static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
19 CalendarAdditionalInfo& additional_info,
20 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
21 const std::time_t time(posix_time);
22 const std::tm* tm = std::localtime(&time);
23 if (tm == nullptr) {
24 calendar_time = {};
25 additional_info = {};
26 return;
27 }
28 calendar_time.year = tm->tm_year + 1900;
29 calendar_time.month = tm->tm_mon + 1;
30 calendar_time.day = tm->tm_mday;
31 calendar_time.hour = tm->tm_hour;
32 calendar_time.minute = tm->tm_min;
33 calendar_time.second = tm->tm_sec;
34
35 additional_info.day_of_week = tm->tm_wday;
36 additional_info.day_of_year = tm->tm_yday;
37 std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
38 additional_info.utc_offset = 0;
39}
40
41static u64 CalendarToPosix(const CalendarTime& calendar_time,
42 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
43 std::tm time{};
44 time.tm_year = calendar_time.year - 1900;
45 time.tm_mon = calendar_time.month - 1;
46 time.tm_mday = calendar_time.day;
47
48 time.tm_hour = calendar_time.hour;
49 time.tm_min = calendar_time.minute;
50 time.tm_sec = calendar_time.second;
51
52 std::time_t epoch_time = std::mktime(&time);
53 return static_cast<u64>(epoch_time);
54}
55
18class ISystemClock final : public ServiceFramework<ISystemClock> { 56class ISystemClock final : public ServiceFramework<ISystemClock> {
19public: 57public:
20 ISystemClock() : ServiceFramework("ISystemClock") { 58 ISystemClock() : ServiceFramework("ISystemClock") {
@@ -80,8 +118,8 @@ public:
80 {5, nullptr, "GetTimeZoneRuleVersion"}, 118 {5, nullptr, "GetTimeZoneRuleVersion"},
81 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, 119 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
82 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, 120 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
83 {201, nullptr, "ToPosixTime"}, 121 {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
84 {202, nullptr, "ToPosixTimeWithMyRule"}, 122 {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
85 }; 123 };
86 RegisterHandlers(functions); 124 RegisterHandlers(functions);
87 } 125 }
@@ -151,24 +189,29 @@ private:
151 rb.PushRaw(additional_info); 189 rb.PushRaw(additional_info);
152 } 190 }
153 191
154 void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, 192 void ToPosixTime(Kernel::HLERequestContext& ctx) {
155 CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) { 193 // TODO(ogniK): Figure out how to handle multiple times
156 std::time_t t(posix_time); 194 LOG_WARNING(Service_Time, "(STUBBED) called");
157 std::tm* tm = std::localtime(&t); 195 IPC::RequestParser rp{ctx};
158 if (!tm) { 196 auto calendar_time = rp.PopRaw<CalendarTime>();
159 return; 197 auto posix_time = CalendarToPosix(calendar_time, {});
160 } 198
161 calendar_time.year = tm->tm_year + 1900; 199 IPC::ResponseBuilder rb{ctx, 3};
162 calendar_time.month = tm->tm_mon + 1; 200 rb.Push(RESULT_SUCCESS);
163 calendar_time.day = tm->tm_mday; 201 rb.PushRaw<u32>(1); // Amount of times we're returning
164 calendar_time.hour = tm->tm_hour; 202 ctx.WriteBuffer(&posix_time, sizeof(u64));
165 calendar_time.minute = tm->tm_min; 203 }
166 calendar_time.second = tm->tm_sec; 204
167 205 void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
168 additional_info.day_of_week = tm->tm_wday; 206 LOG_WARNING(Service_Time, "(STUBBED) called");
169 additional_info.day_of_year = tm->tm_yday; 207 IPC::RequestParser rp{ctx};
170 std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); 208 auto calendar_time = rp.PopRaw<CalendarTime>();
171 additional_info.utc_offset = 0; 209 auto posix_time = CalendarToPosix(calendar_time, {});
210
211 IPC::ResponseBuilder rb{ctx, 3};
212 rb.Push(RESULT_SUCCESS);
213 rb.PushRaw<u32>(1); // Amount of times we're returning
214 ctx.WriteBuffer(&posix_time, sizeof(u64));
172 } 215 }
173}; 216};
174 217
@@ -207,6 +250,70 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
207 LOG_DEBUG(Service_Time, "called"); 250 LOG_DEBUG(Service_Time, "called");
208} 251}
209 252
253void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
254 LOG_DEBUG(Service_Time, "called");
255
256 IPC::RequestParser rp{ctx};
257 auto unknown_u8 = rp.PopRaw<u8>();
258
259 ClockSnapshot clock_snapshot{};
260
261 const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
262 std::chrono::system_clock::now().time_since_epoch())
263 .count()};
264 CalendarTime calendar_time{};
265 const std::time_t time(time_since_epoch);
266 const std::tm* tm = std::localtime(&time);
267 if (tm == nullptr) {
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
270 return;
271 }
272 SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
273 1000};
274
275 LocationName location_name{"UTC"};
276 calendar_time.year = tm->tm_year + 1900;
277 calendar_time.month = tm->tm_mon + 1;
278 calendar_time.day = tm->tm_mday;
279 calendar_time.hour = tm->tm_hour;
280 calendar_time.minute = tm->tm_min;
281 calendar_time.second = tm->tm_sec;
282 clock_snapshot.system_posix_time = time_since_epoch;
283 clock_snapshot.network_posix_time = time_since_epoch;
284 clock_snapshot.system_calendar_time = calendar_time;
285 clock_snapshot.network_calendar_time = calendar_time;
286
287 CalendarAdditionalInfo additional_info{};
288 PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
289
290 clock_snapshot.system_calendar_info = additional_info;
291 clock_snapshot.network_calendar_info = additional_info;
292
293 clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
294 clock_snapshot.location_name = location_name;
295 clock_snapshot.clock_auto_adjustment_enabled = 1;
296 clock_snapshot.ipc_u8 = unknown_u8;
297 IPC::ResponseBuilder rb{ctx, 2};
298 rb.Push(RESULT_SUCCESS);
299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
300}
301
302void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
303 Kernel::HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called");
305
306 IPC::RequestParser rp{ctx};
307 const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
308 const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
309 const u64 difference =
310 snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
311
312 IPC::ResponseBuilder rb{ctx, 4};
313 rb.Push(RESULT_SUCCESS);
314 rb.PushRaw<u64>(difference);
315}
316
210Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 317Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
211 : ServiceFramework(name), time(std::move(time)) {} 318 : ServiceFramework(name), time(std::move(time)) {}
212 319
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 5659ecad3..ea43fbea7 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/common_funcs.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
10namespace Service::Time { 11namespace Service::Time {
@@ -53,6 +54,23 @@ struct SystemClockContext {
53static_assert(sizeof(SystemClockContext) == 0x20, 54static_assert(sizeof(SystemClockContext) == 0x20,
54 "SystemClockContext structure has incorrect size"); 55 "SystemClockContext structure has incorrect size");
55 56
57struct ClockSnapshot {
58 SystemClockContext user_clock_context;
59 SystemClockContext network_clock_context;
60 s64_le system_posix_time;
61 s64_le network_posix_time;
62 CalendarTime system_calendar_time;
63 CalendarTime network_calendar_time;
64 CalendarAdditionalInfo system_calendar_info;
65 CalendarAdditionalInfo network_calendar_info;
66 SteadyClockTimePoint steady_clock_timepoint;
67 LocationName location_name;
68 u8 clock_auto_adjustment_enabled;
69 u8 ipc_u8;
70 INSERT_PADDING_BYTES(2);
71};
72static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
73
56class Module final { 74class Module final {
57public: 75public:
58 class Interface : public ServiceFramework<Interface> { 76 class Interface : public ServiceFramework<Interface> {
@@ -65,6 +83,8 @@ public:
65 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); 83 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
66 void GetTimeZoneService(Kernel::HLERequestContext& ctx); 84 void GetTimeZoneService(Kernel::HLERequestContext& ctx);
67 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
86 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
87 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
68 88
69 protected: 89 protected:
70 std::shared_ptr<Module> time; 90 std::shared_ptr<Module> time;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index d764b2406..d25fdb1fe 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -237,6 +237,22 @@ private:
237 Data data{}; 237 Data data{};
238}; 238};
239 239
240/// Represents a parcel containing one int '0' as its data
241/// Used by DetachBuffer and Disconnect
242class IGBPEmptyResponseParcel : public Parcel {
243protected:
244 void SerializeData() override {
245 Write(data);
246 }
247
248private:
249 struct Data {
250 u32_le unk_0;
251 };
252
253 Data data{};
254};
255
240class IGBPSetPreallocatedBufferRequestParcel : public Parcel { 256class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
241public: 257public:
242 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) 258 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -554,6 +570,12 @@ private:
554 ctx.WriteBuffer(response.Serialize()); 570 ctx.WriteBuffer(response.Serialize());
555 } else if (transaction == TransactionId::CancelBuffer) { 571 } else if (transaction == TransactionId::CancelBuffer) {
556 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); 572 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
573 } else if (transaction == TransactionId::Disconnect ||
574 transaction == TransactionId::DetachBuffer) {
575 const auto buffer = ctx.ReadBuffer();
576
577 IGBPEmptyResponseParcel response{};
578 ctx.WriteBuffer(response.Serialize());
557 } else { 579 } else {
558 ASSERT_MSG(false, "Unimplemented"); 580 ASSERT_MSG(false, "Unimplemented");
559 } 581 }
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index bc8e402a8..fbbd6b0de 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -12,10 +12,12 @@
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 17#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
20#include "core/hle/service/filesystem/filesystem.h"
19#include "core/loader/nro.h" 21#include "core/loader/nro.h"
20#include "core/loader/nso.h" 22#include "core/loader/nso.h"
21#include "core/memory.h" 23#include "core/memory.h"
@@ -168,17 +170,20 @@ static constexpr u32 PageAlignSize(u32 size) {
168 arg_data.size()); 170 arg_data.size());
169 } 171 }
170 172
171 // Read MOD header
172 ModHeader mod_header{};
173 // Default .bss to NRO header bss size if MOD0 section doesn't exist 173 // Default .bss to NRO header bss size if MOD0 section doesn't exist
174 u32 bss_size{PageAlignSize(nro_header.bss_size)}; 174 u32 bss_size{PageAlignSize(nro_header.bss_size)};
175
176 // Read MOD header
177 ModHeader mod_header{};
175 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, 178 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
176 sizeof(ModHeader)); 179 sizeof(ModHeader));
180
177 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; 181 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
178 if (has_mod_header) { 182 if (has_mod_header) {
179 // Resize program image to include .bss section and page align each section 183 // Resize program image to include .bss section and page align each section
180 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 184 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
181 } 185 }
186
182 codeset.DataSegment().size += bss_size; 187 codeset.DataSegment().size += bss_size;
183 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 188 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
184 189
@@ -208,6 +213,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
208 return ResultStatus::ErrorLoadingNRO; 213 return ResultStatus::ErrorLoadingNRO;
209 } 214 }
210 215
216 if (romfs != nullptr)
217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
218
211 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 219 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
212 220
213 is_loaded = true; 221 is_loaded = true;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 68efca5c0..aaf006309 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd
154 program_image.resize(image_size); 154 program_image.resize(image_size);
155 155
156 // Apply patches if necessary 156 // Apply patches if necessary
157 if (pm && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
158 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
diff --git a/src/core/settings.h b/src/core/settings.h
index b5aeff29b..e424479f2 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <atomic> 8#include <atomic>
9#include <optional>
9#include <string> 10#include <string>
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
@@ -114,8 +115,9 @@ struct Values {
114 // System 115 // System
115 bool use_docked_mode; 116 bool use_docked_mode;
116 bool enable_nfc; 117 bool enable_nfc;
117 int current_user; 118 std::optional<u32> rng_seed;
118 int language_index; 119 s32 current_user;
120 s32 language_index;
119 121
120 // Controls 122 // Controls
121 std::array<std::string, NativeButton::NumButtons> buttons; 123 std::array<std::string, NativeButton::NumButtons> buttons;
@@ -157,6 +159,7 @@ struct Values {
157 bool use_gdbstub; 159 bool use_gdbstub;
158 u16 gdbstub_port; 160 u16 gdbstub_port;
159 std::string program_args; 161 std::string program_args;
162 bool dump_nso;
160 163
161 // WebService 164 // WebService
162 bool enable_telemetry; 165 bool enable_telemetry;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index ddb1a1d69..a780215c1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -21,6 +21,7 @@ add_library(video_core STATIC
21 macro_interpreter.h 21 macro_interpreter.h
22 memory_manager.cpp 22 memory_manager.cpp
23 memory_manager.h 23 memory_manager.h
24 rasterizer_cache.cpp
24 rasterizer_cache.h 25 rasterizer_cache.h
25 rasterizer_interface.h 26 rasterizer_interface.h
26 renderer_base.cpp 27 renderer_base.cpp
@@ -33,6 +34,7 @@ add_library(video_core STATIC
33 renderer_opengl/gl_rasterizer.h 34 renderer_opengl/gl_rasterizer.h
34 renderer_opengl/gl_rasterizer_cache.cpp 35 renderer_opengl/gl_rasterizer_cache.cpp
35 renderer_opengl/gl_rasterizer_cache.h 36 renderer_opengl/gl_rasterizer_cache.h
37 renderer_opengl/gl_resource_manager.cpp
36 renderer_opengl/gl_resource_manager.h 38 renderer_opengl/gl_resource_manager.h
37 renderer_opengl/gl_shader_cache.cpp 39 renderer_opengl/gl_shader_cache.cpp
38 renderer_opengl/gl_shader_cache.h 40 renderer_opengl/gl_shader_cache.h
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index d79c50919..6de07ea56 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -37,6 +37,35 @@ void Maxwell3D::InitializeRegisterDefaults() {
37 regs.viewport[viewport].depth_range_near = 0.0f; 37 regs.viewport[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f; 38 regs.viewport[viewport].depth_range_far = 1.0f;
39 } 39 }
40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend
41 // so initialize blend registers with sane values
42 regs.blend.equation_rgb = Regs::Blend::Equation::Add;
43 regs.blend.factor_source_rgb = Regs::Blend::Factor::One;
44 regs.blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
45 regs.blend.equation_a = Regs::Blend::Equation::Add;
46 regs.blend.factor_source_a = Regs::Blend::Factor::One;
47 regs.blend.factor_dest_a = Regs::Blend::Factor::Zero;
48 for (std::size_t blend_index = 0; blend_index < Regs::NumRenderTargets; blend_index++) {
49 regs.independent_blend[blend_index].equation_rgb = Regs::Blend::Equation::Add;
50 regs.independent_blend[blend_index].factor_source_rgb = Regs::Blend::Factor::One;
51 regs.independent_blend[blend_index].factor_dest_rgb = Regs::Blend::Factor::Zero;
52 regs.independent_blend[blend_index].equation_a = Regs::Blend::Equation::Add;
53 regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
54 regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
55 }
56 regs.stencil_front_op_fail = Regs::StencilOp::Keep;
57 regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
58 regs.stencil_front_op_zpass = Regs::StencilOp::Keep;
59 regs.stencil_front_func_func = Regs::ComparisonOp::Always;
60 regs.stencil_front_func_mask = 0xFFFFFFFF;
61 regs.stencil_front_mask = 0xFFFFFFFF;
62 regs.stencil_two_side_enable = 1;
63 regs.stencil_back_op_fail = Regs::StencilOp::Keep;
64 regs.stencil_back_op_zfail = Regs::StencilOp::Keep;
65 regs.stencil_back_op_zpass = Regs::StencilOp::Keep;
66 regs.stencil_back_func_func = Regs::ComparisonOp::Always;
67 regs.stencil_back_func_mask = 0xFFFFFFFF;
68 regs.stencil_back_mask = 0xFFFFFFFF;
40} 69}
41 70
42void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 71void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
@@ -92,7 +121,13 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
92 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); 121 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
93 } 122 }
94 123
95 regs.reg_array[method] = value; 124 if (regs.reg_array[method] != value) {
125 regs.reg_array[method] = value;
126 if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
127 method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
128 dirty_flags.vertex_attrib_format = true;
129 }
130 }
96 131
97 switch (method) { 132 switch (method) {
98 case MAXWELL3D_REG_INDEX(macros.data): { 133 case MAXWELL3D_REG_INDEX(macros.data): {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 50873813e..91ca57883 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -345,6 +345,14 @@ public:
345 Invert = 6, 345 Invert = 6,
346 IncrWrap = 7, 346 IncrWrap = 7,
347 DecrWrap = 8, 347 DecrWrap = 8,
348 KeepOGL = 0x1E00,
349 ZeroOGL = 0,
350 ReplaceOGL = 0x1E01,
351 IncrOGL = 0x1E02,
352 DecrOGL = 0x1E03,
353 InvertOGL = 0x150A,
354 IncrWrapOGL = 0x8507,
355 DecrWrapOGL = 0x8508,
348 }; 356 };
349 357
350 enum class MemoryLayout : u32 { 358 enum class MemoryLayout : u32 {
@@ -462,6 +470,16 @@ public:
462 } 470 }
463 }; 471 };
464 472
473 struct ColorMask {
474 union {
475 u32 raw;
476 BitField<0, 4, u32> R;
477 BitField<4, 4, u32> G;
478 BitField<8, 4, u32> B;
479 BitField<12, 4, u32> A;
480 };
481 };
482
465 bool IsShaderConfigEnabled(std::size_t index) const { 483 bool IsShaderConfigEnabled(std::size_t index) const {
466 // The VertexB is always enabled. 484 // The VertexB is always enabled.
467 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { 485 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -571,7 +589,11 @@ public:
571 u32 stencil_back_mask; 589 u32 stencil_back_mask;
572 u32 stencil_back_func_mask; 590 u32 stencil_back_func_mask;
573 591
574 INSERT_PADDING_WORDS(0x13); 592 INSERT_PADDING_WORDS(0xC);
593
594 u32 color_mask_common;
595
596 INSERT_PADDING_WORDS(0x6);
575 597
576 u32 rt_separate_frag_data; 598 u32 rt_separate_frag_data;
577 599
@@ -646,8 +668,14 @@ public:
646 ComparisonOp depth_test_func; 668 ComparisonOp depth_test_func;
647 float alpha_test_ref; 669 float alpha_test_ref;
648 ComparisonOp alpha_test_func; 670 ComparisonOp alpha_test_func;
649 671 u32 draw_tfb_stride;
650 INSERT_PADDING_WORDS(0x9); 672 struct {
673 float r;
674 float g;
675 float b;
676 float a;
677 } blend_color;
678 INSERT_PADDING_WORDS(0x4);
651 679
652 struct { 680 struct {
653 u32 separate_alpha; 681 u32 separate_alpha;
@@ -841,8 +869,9 @@ public:
841 BitField<6, 4, u32> RT; 869 BitField<6, 4, u32> RT;
842 BitField<10, 11, u32> layer; 870 BitField<10, 11, u32> layer;
843 } clear_buffers; 871 } clear_buffers;
844 872 INSERT_PADDING_WORDS(0xB);
845 INSERT_PADDING_WORDS(0x4B); 873 std::array<ColorMask, NumRenderTargets> color_mask;
874 INSERT_PADDING_WORDS(0x38);
846 875
847 struct { 876 struct {
848 u32 query_address_high; 877 u32 query_address_high;
@@ -983,6 +1012,12 @@ public:
983 State state{}; 1012 State state{};
984 MemoryManager& memory_manager; 1013 MemoryManager& memory_manager;
985 1014
1015 struct DirtyFlags {
1016 bool vertex_attrib_format = true;
1017 };
1018
1019 DirtyFlags dirty_flags;
1020
986 /// Reads a register value located at the input method address 1021 /// Reads a register value located at the input method address
987 u32 GetRegisterValue(u32 method) const; 1022 u32 GetRegisterValue(u32 method) const;
988 1023
@@ -1075,6 +1110,7 @@ ASSERT_REG_POSITION(scissor_test, 0x380);
1075ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 1110ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
1076ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 1111ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
1077ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 1112ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
1113ASSERT_REG_POSITION(color_mask_common, 0x3E4);
1078ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); 1114ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
1079ASSERT_REG_POSITION(zeta, 0x3F8); 1115ASSERT_REG_POSITION(zeta, 0x3F8);
1080ASSERT_REG_POSITION(vertex_attrib_format, 0x458); 1116ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
@@ -1087,6 +1123,10 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
1087ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); 1123ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
1088ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); 1124ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
1089ASSERT_REG_POSITION(depth_test_func, 0x4C3); 1125ASSERT_REG_POSITION(depth_test_func, 0x4C3);
1126ASSERT_REG_POSITION(alpha_test_ref, 0x4C4);
1127ASSERT_REG_POSITION(alpha_test_func, 0x4C5);
1128ASSERT_REG_POSITION(draw_tfb_stride, 0x4C6);
1129ASSERT_REG_POSITION(blend_color, 0x4C7);
1090ASSERT_REG_POSITION(blend, 0x4CF); 1130ASSERT_REG_POSITION(blend, 0x4CF);
1091ASSERT_REG_POSITION(stencil_enable, 0x4E0); 1131ASSERT_REG_POSITION(stencil_enable, 0x4E0);
1092ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1); 1132ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1);
@@ -1117,6 +1157,7 @@ ASSERT_REG_POSITION(instanced_arrays, 0x620);
1117ASSERT_REG_POSITION(cull, 0x646); 1157ASSERT_REG_POSITION(cull, 0x646);
1118ASSERT_REG_POSITION(logic_op, 0x671); 1158ASSERT_REG_POSITION(logic_op, 0x671);
1119ASSERT_REG_POSITION(clear_buffers, 0x674); 1159ASSERT_REG_POSITION(clear_buffers, 0x674);
1160ASSERT_REG_POSITION(color_mask, 0x680);
1120ASSERT_REG_POSITION(query, 0x6C0); 1161ASSERT_REG_POSITION(query, 0x6C0);
1121ASSERT_REG_POSITION(vertex_array[0], 0x700); 1162ASSERT_REG_POSITION(vertex_array[0], 0x700);
1122ASSERT_REG_POSITION(independent_blend, 0x780); 1163ASSERT_REG_POSITION(independent_blend, 0x780);
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 90a8e825d..77a20bb84 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -4,18 +4,21 @@
4 4
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h"
7#include "video_core/memory_manager.h" 8#include "video_core/memory_manager.h"
8 9
9namespace Tegra { 10namespace Tegra {
10 11
11GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { 12GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
12 std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, align); 13 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)};
13 ASSERT(gpu_addr);
14 14
15 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 15 ASSERT_MSG(gpu_addr, "unable to find available GPU memory");
16 VAddr& slot = PageSlot(*gpu_addr + offset); 16
17 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
18 VAddr& slot{PageSlot(*gpu_addr + offset)};
17 19
18 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 20 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped));
21
19 slot = static_cast<u64>(PageStatus::Allocated); 22 slot = static_cast<u64>(PageStatus::Allocated);
20 } 23 }
21 24
@@ -23,10 +26,11 @@ GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
23} 26}
24 27
25GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { 28GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
26 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 29 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
27 VAddr& slot = PageSlot(gpu_addr + offset); 30 VAddr& slot{PageSlot(gpu_addr + offset)};
28 31
29 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 32 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped));
33
30 slot = static_cast<u64>(PageStatus::Allocated); 34 slot = static_cast<u64>(PageStatus::Allocated);
31 } 35 }
32 36
@@ -34,17 +38,19 @@ GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
34} 38}
35 39
36GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { 40GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
37 std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, PAGE_SIZE); 41 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, PAGE_SIZE, PageStatus::Unmapped)};
38 ASSERT(gpu_addr); 42
43 ASSERT_MSG(gpu_addr, "unable to find available GPU memory");
39 44
40 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 45 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
41 VAddr& slot = PageSlot(*gpu_addr + offset); 46 VAddr& slot{PageSlot(*gpu_addr + offset)};
42 47
43 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 48 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped));
49
44 slot = cpu_addr + offset; 50 slot = cpu_addr + offset;
45 } 51 }
46 52
47 MappedRegion region{cpu_addr, *gpu_addr, size}; 53 const MappedRegion region{cpu_addr, *gpu_addr, size};
48 mapped_regions.push_back(region); 54 mapped_regions.push_back(region);
49 55
50 return *gpu_addr; 56 return *gpu_addr;
@@ -53,14 +59,31 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
53GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) { 59GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) {
54 ASSERT((gpu_addr & PAGE_MASK) == 0); 60 ASSERT((gpu_addr & PAGE_MASK) == 0);
55 61
56 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 62 if (PageSlot(gpu_addr) != static_cast<u64>(PageStatus::Allocated)) {
57 VAddr& slot = PageSlot(gpu_addr + offset); 63 // Page has been already mapped. In this case, we must find a new area of memory to use that
64 // is different than the specified one. Super Mario Odyssey hits this scenario when changing
65 // areas, but we do not want to overwrite the old pages.
66 // TODO(bunnei): We need to write a hardware test to confirm this behavior.
67
68 LOG_ERROR(HW_GPU, "attempting to map addr 0x{:016X}, which is not available!", gpu_addr);
69
70 const std::optional<GPUVAddr> new_gpu_addr{
71 FindFreeBlock(gpu_addr, size, PAGE_SIZE, PageStatus::Allocated)};
72
73 ASSERT_MSG(new_gpu_addr, "unable to find available GPU memory");
74
75 gpu_addr = *new_gpu_addr;
76 }
77
78 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
79 VAddr& slot{PageSlot(gpu_addr + offset)};
58 80
59 ASSERT(slot == static_cast<u64>(PageStatus::Allocated)); 81 ASSERT(slot == static_cast<u64>(PageStatus::Allocated));
82
60 slot = cpu_addr + offset; 83 slot = cpu_addr + offset;
61 } 84 }
62 85
63 MappedRegion region{cpu_addr, gpu_addr, size}; 86 const MappedRegion region{cpu_addr, gpu_addr, size};
64 mapped_regions.push_back(region); 87 mapped_regions.push_back(region);
65 88
66 return gpu_addr; 89 return gpu_addr;
@@ -69,11 +92,12 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
69GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { 92GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
70 ASSERT((gpu_addr & PAGE_MASK) == 0); 93 ASSERT((gpu_addr & PAGE_MASK) == 0);
71 94
72 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 95 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
73 VAddr& slot = PageSlot(gpu_addr + offset); 96 VAddr& slot{PageSlot(gpu_addr + offset)};
74 97
75 ASSERT(slot != static_cast<u64>(PageStatus::Allocated) && 98 ASSERT(slot != static_cast<u64>(PageStatus::Allocated) &&
76 slot != static_cast<u64>(PageStatus::Unmapped)); 99 slot != static_cast<u64>(PageStatus::Unmapped));
100
77 slot = static_cast<u64>(PageStatus::Unmapped); 101 slot = static_cast<u64>(PageStatus::Unmapped);
78 } 102 }
79 103
@@ -97,13 +121,14 @@ GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const {
97 return {}; 121 return {};
98} 122}
99 123
100std::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { 124std::optional<GPUVAddr> MemoryManager::FindFreeBlock(GPUVAddr region_start, u64 size, u64 align,
101 GPUVAddr gpu_addr = 0; 125 PageStatus status) {
102 u64 free_space = 0; 126 GPUVAddr gpu_addr{region_start};
127 u64 free_space{};
103 align = (align + PAGE_MASK) & ~PAGE_MASK; 128 align = (align + PAGE_MASK) & ~PAGE_MASK;
104 129
105 while (gpu_addr + free_space < MAX_ADDRESS) { 130 while (gpu_addr + free_space < MAX_ADDRESS) {
106 if (!IsPageMapped(gpu_addr + free_space)) { 131 if (PageSlot(gpu_addr + free_space) == static_cast<u64>(status)) {
107 free_space += PAGE_SIZE; 132 free_space += PAGE_SIZE;
108 if (free_space >= size) { 133 if (free_space >= size) {
109 return gpu_addr; 134 return gpu_addr;
@@ -119,7 +144,7 @@ std::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
119} 144}
120 145
121std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { 146std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
122 VAddr base_addr = PageSlot(gpu_addr); 147 const VAddr base_addr{PageSlot(gpu_addr)};
123 148
124 if (base_addr == static_cast<u64>(PageStatus::Allocated) || 149 if (base_addr == static_cast<u64>(PageStatus::Allocated) ||
125 base_addr == static_cast<u64>(PageStatus::Unmapped)) { 150 base_addr == static_cast<u64>(PageStatus::Unmapped)) {
@@ -133,19 +158,15 @@ std::vector<GPUVAddr> MemoryManager::CpuToGpuAddress(VAddr cpu_addr) const {
133 std::vector<GPUVAddr> results; 158 std::vector<GPUVAddr> results;
134 for (const auto& region : mapped_regions) { 159 for (const auto& region : mapped_regions) {
135 if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) { 160 if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) {
136 u64 offset = cpu_addr - region.cpu_addr; 161 const u64 offset{cpu_addr - region.cpu_addr};
137 results.push_back(region.gpu_addr + offset); 162 results.push_back(region.gpu_addr + offset);
138 } 163 }
139 } 164 }
140 return results; 165 return results;
141} 166}
142 167
143bool MemoryManager::IsPageMapped(GPUVAddr gpu_addr) {
144 return PageSlot(gpu_addr) != static_cast<u64>(PageStatus::Unmapped);
145}
146
147VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) { 168VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) {
148 auto& block = page_table[(gpu_addr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]; 169 auto& block{page_table[(gpu_addr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]};
149 if (!block) { 170 if (!block) {
150 block = std::make_unique<PageBlock>(); 171 block = std::make_unique<PageBlock>();
151 block->fill(static_cast<VAddr>(PageStatus::Unmapped)); 172 block->fill(static_cast<VAddr>(PageStatus::Unmapped));
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index b1255fd56..4eb338aa2 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -34,15 +34,15 @@ public:
34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
35 35
36private: 36private:
37 std::optional<GPUVAddr> FindFreeBlock(u64 size, u64 align = 1);
38 bool IsPageMapped(GPUVAddr gpu_addr);
39 VAddr& PageSlot(GPUVAddr gpu_addr);
40
41 enum class PageStatus : u64 { 37 enum class PageStatus : u64 {
42 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 38 Unmapped = 0xFFFFFFFFFFFFFFFFULL,
43 Allocated = 0xFFFFFFFFFFFFFFFEULL, 39 Allocated = 0xFFFFFFFFFFFFFFFEULL,
44 }; 40 };
45 41
42 std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align,
43 PageStatus status);
44 VAddr& PageSlot(GPUVAddr gpu_addr);
45
46 static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; 46 static constexpr u64 MAX_ADDRESS{0x10000000000ULL};
47 static constexpr u64 PAGE_TABLE_BITS{10}; 47 static constexpr u64 PAGE_TABLE_BITS{10};
48 static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS}; 48 static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS};
diff --git a/src/video_core/rasterizer_cache.cpp b/src/video_core/rasterizer_cache.cpp
new file mode 100644
index 000000000..093b2cdf4
--- /dev/null
+++ b/src/video_core/rasterizer_cache.cpp
@@ -0,0 +1,7 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/rasterizer_cache.h"
6
7RasterizerCacheObject::~RasterizerCacheObject() = default;
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 0a3b3951e..bcf0c15a4 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -5,18 +5,19 @@
5#pragma once 5#pragma once
6 6
7#include <set> 7#include <set>
8#include <unordered_map>
8 9
9#include <boost/icl/interval_map.hpp> 10#include <boost/icl/interval_map.hpp>
10#include <boost/range/iterator_range_core.hpp> 11#include <boost/range/iterator_range_core.hpp>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "core/core.h"
14#include "core/settings.h" 14#include "core/settings.h"
15#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
16#include "video_core/renderer_base.h"
17 16
18class RasterizerCacheObject { 17class RasterizerCacheObject {
19public: 18public:
19 virtual ~RasterizerCacheObject();
20
20 /// Gets the address of the shader in guest memory, required for cache management 21 /// Gets the address of the shader in guest memory, required for cache management
21 virtual VAddr GetAddr() const = 0; 22 virtual VAddr GetAddr() const = 0;
22 23
@@ -64,6 +65,8 @@ class RasterizerCache : NonCopyable {
64 friend class RasterizerCacheObject; 65 friend class RasterizerCacheObject;
65 66
66public: 67public:
68 explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {}
69
67 /// Write any cached resources overlapping the specified region back to memory 70 /// Write any cached resources overlapping the specified region back to memory
68 void FlushRegion(Tegra::GPUVAddr addr, size_t size) { 71 void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
69 const auto& objects{GetSortedObjectsFromRegion(addr, size)}; 72 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
@@ -86,45 +89,39 @@ public:
86 89
87 /// Invalidates everything in the cache 90 /// Invalidates everything in the cache
88 void InvalidateAll() { 91 void InvalidateAll() {
89 while (object_cache.begin() != object_cache.end()) { 92 while (interval_cache.begin() != interval_cache.end()) {
90 Unregister(*object_cache.begin()->second.begin()); 93 Unregister(*interval_cache.begin()->second.begin());
91 } 94 }
92 } 95 }
93 96
94protected: 97protected:
95 /// Tries to get an object from the cache with the specified address 98 /// Tries to get an object from the cache with the specified address
96 T TryGet(VAddr addr) const { 99 T TryGet(VAddr addr) const {
97 const ObjectInterval interval{addr}; 100 const auto iter = map_cache.find(addr);
98 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 101 if (iter != map_cache.end())
99 for (auto& cached_object : pair.second) { 102 return iter->second;
100 if (cached_object->GetAddr() == addr) {
101 return cached_object;
102 }
103 }
104 }
105 return nullptr; 103 return nullptr;
106 } 104 }
107 105
108 /// Register an object into the cache 106 /// Register an object into the cache
109 void Register(const T& object) { 107 void Register(const T& object) {
110 object->SetIsRegistered(true); 108 object->SetIsRegistered(true);
111 object_cache.add({GetInterval(object), ObjectSet{object}}); 109 interval_cache.add({GetInterval(object), ObjectSet{object}});
112 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 110 map_cache.insert({object->GetAddr(), object});
113 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 111 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
114 } 112 }
115 113
116 /// Unregisters an object from the cache 114 /// Unregisters an object from the cache
117 void Unregister(const T& object) { 115 void Unregister(const T& object) {
118 object->SetIsRegistered(false); 116 object->SetIsRegistered(false);
119 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
120 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); 117 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
121
122 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit 118 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
123 if (Settings::values.use_accurate_gpu_emulation) { 119 if (Settings::values.use_accurate_gpu_emulation) {
124 FlushObject(object); 120 FlushObject(object);
125 } 121 }
126 122
127 object_cache.subtract({GetInterval(object), ObjectSet{object}}); 123 interval_cache.subtract({GetInterval(object), ObjectSet{object}});
124 map_cache.erase(object->GetAddr());
128 } 125 }
129 126
130 /// Returns a ticks counter used for tracking when cached objects were last modified 127 /// Returns a ticks counter used for tracking when cached objects were last modified
@@ -141,7 +138,7 @@ private:
141 138
142 std::vector<T> objects; 139 std::vector<T> objects;
143 const ObjectInterval interval{addr, addr + size}; 140 const ObjectInterval interval{addr, addr + size};
144 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 141 for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) {
145 for (auto& cached_object : pair.second) { 142 for (auto& cached_object : pair.second) {
146 if (!cached_object) { 143 if (!cached_object) {
147 continue; 144 continue;
@@ -167,14 +164,17 @@ private:
167 } 164 }
168 165
169 using ObjectSet = std::set<T>; 166 using ObjectSet = std::set<T>;
170 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; 167 using ObjectCache = std::unordered_map<VAddr, T>;
171 using ObjectInterval = typename ObjectCache::interval_type; 168 using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>;
169 using ObjectInterval = typename IntervalCache::interval_type;
172 170
173 static auto GetInterval(const T& object) { 171 static auto GetInterval(const T& object) {
174 return ObjectInterval::right_open(object->GetAddr(), 172 return ObjectInterval::right_open(object->GetAddr(),
175 object->GetAddr() + object->GetSizeInBytes()); 173 object->GetAddr() + object->GetSizeInBytes());
176 } 174 }
177 175
178 ObjectCache object_cache; ///< Cache of objects 176 ObjectCache map_cache;
179 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing 177 IntervalCache interval_cache; ///< Cache of objects
178 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
179 VideoCore::RasterizerInterface& rasterizer;
180}; 180};
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 0df3725c2..1482cdb40 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -5,7 +5,6 @@
5#include "core/frontend/emu_window.h" 5#include "core/frontend/emu_window.h"
6#include "core/settings.h" 6#include "core/settings.h"
7#include "video_core/renderer_base.h" 7#include "video_core/renderer_base.h"
8#include "video_core/renderer_opengl/gl_rasterizer.h"
9 8
10namespace VideoCore { 9namespace VideoCore {
11 10
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 41a54b3e7..075192c3f 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -9,10 +9,12 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/memory.h" 10#include "core/memory.h"
11#include "video_core/renderer_opengl/gl_buffer_cache.h" 11#include "video_core/renderer_opengl/gl_buffer_cache.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h"
12 13
13namespace OpenGL { 14namespace OpenGL {
14 15
15OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {} 16OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
17 : RasterizerCache{rasterizer}, stream_buffer(GL_ARRAY_BUFFER, size) {}
16 18
17GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, 19GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
18 std::size_t alignment, bool cache) { 20 std::size_t alignment, bool cache) {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index be29dc8be..91fca3f6c 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -15,6 +15,8 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18class RasterizerOpenGL;
19
18struct CachedBufferEntry final : public RasterizerCacheObject { 20struct CachedBufferEntry final : public RasterizerCacheObject {
19 VAddr GetAddr() const override { 21 VAddr GetAddr() const override {
20 return addr; 22 return addr;
@@ -35,7 +37,7 @@ struct CachedBufferEntry final : public RasterizerCacheObject {
35 37
36class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { 38class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
37public: 39public:
38 explicit OGLBufferCache(std::size_t size); 40 explicit OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size);
39 41
40 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been 42 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
41 /// allocated. 43 /// allocated.
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
index 741f14bc3..d9ed08437 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/core.h"
9#include "core/memory.h" 10#include "core/memory.h"
10#include "video_core/renderer_opengl/gl_buffer_cache.h" 11#include "video_core/renderer_opengl/gl_buffer_cache.h"
11#include "video_core/renderer_opengl/gl_primitive_assembler.h" 12#include "video_core/renderer_opengl/gl_primitive_assembler.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a0527fe57..84bd91eed 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -33,7 +33,8 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
33using PixelFormat = VideoCore::Surface::PixelFormat; 33using PixelFormat = VideoCore::Surface::PixelFormat;
34using SurfaceType = VideoCore::Surface::SurfaceType; 34using SurfaceType = VideoCore::Surface::SurfaceType;
35 35
36MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); 36MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Format Setup", MP_RGB(128, 128, 192));
37MICROPROFILE_DEFINE(OpenGL_VB, "OpenGL", "Vertex Buffer Setup", MP_RGB(128, 128, 192));
37MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192)); 38MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
38MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192)); 39MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
39MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192)); 40MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
@@ -79,7 +80,8 @@ struct DrawParameters {
79}; 80};
80 81
81RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) 82RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
82 : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { 83 : res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info},
84 buffer_cache(*this, STREAM_BUFFER_SIZE) {
83 // Create sampler objects 85 // Create sampler objects
84 for (std::size_t i = 0; i < texture_samplers.size(); ++i) { 86 for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
85 texture_samplers[i].Create(); 87 texture_samplers[i].Create();
@@ -105,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
105 107
106 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); 108 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
107 OpenGLState::ApplyDefaultState(); 109 OpenGLState::ApplyDefaultState();
108 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
109 state.clip_distance[0] = true;
110 110
111 // Create render framebuffer 111 // Create render framebuffer
112 framebuffer.Create(); 112 framebuffer.Create();
@@ -122,18 +122,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
122 122
123RasterizerOpenGL::~RasterizerOpenGL() {} 123RasterizerOpenGL::~RasterizerOpenGL() {}
124 124
125void RasterizerOpenGL::SetupVertexArrays() { 125void RasterizerOpenGL::SetupVertexFormat() {
126 MICROPROFILE_SCOPE(OpenGL_VAO); 126 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
127 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
128 const auto& regs = gpu.regs; 127 const auto& regs = gpu.regs;
129 128
129 if (!gpu.dirty_flags.vertex_attrib_format)
130 return;
131 gpu.dirty_flags.vertex_attrib_format = false;
132
133 MICROPROFILE_SCOPE(OpenGL_VAO);
134
130 auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format); 135 auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
131 auto& VAO = iter->second; 136 auto& VAO = iter->second;
132 137
133 if (is_cache_miss) { 138 if (is_cache_miss) {
134 VAO.Create(); 139 VAO.Create();
135 state.draw.vertex_array = VAO.handle; 140 state.draw.vertex_array = VAO.handle;
136 state.Apply(); 141 state.ApplyVertexBufferState();
137 142
138 // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work 143 // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
139 // around. 144 // around.
@@ -175,8 +180,13 @@ void RasterizerOpenGL::SetupVertexArrays() {
175 } 180 }
176 } 181 }
177 state.draw.vertex_array = VAO.handle; 182 state.draw.vertex_array = VAO.handle;
178 state.draw.vertex_buffer = buffer_cache.GetHandle(); 183 state.ApplyVertexBufferState();
179 state.Apply(); 184}
185
186void RasterizerOpenGL::SetupVertexBuffer() {
187 MICROPROFILE_SCOPE(OpenGL_VB);
188 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
189 const auto& regs = gpu.regs;
180 190
181 // Upload all guest vertex arrays sequentially to our buffer 191 // Upload all guest vertex arrays sequentially to our buffer
182 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 192 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -203,6 +213,9 @@ void RasterizerOpenGL::SetupVertexArrays() {
203 glVertexBindingDivisor(index, 0); 213 glVertexBindingDivisor(index, 0);
204 } 214 }
205 } 215 }
216
217 // Implicit set by glBindVertexBuffer. Stupid glstate handling...
218 state.draw.vertex_buffer = buffer_cache.GetHandle();
206} 219}
207 220
208DrawParameters RasterizerOpenGL::SetupDraw() { 221DrawParameters RasterizerOpenGL::SetupDraw() {
@@ -327,8 +340,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
327 index++; 340 index++;
328 } 341 }
329 } 342 }
330
331 state.Apply();
332} 343}
333 344
334std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 345std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -397,8 +408,8 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
397 cached_pages.add({pages_interval, delta}); 408 cached_pages.add({pages_interval, delta});
398} 409}
399 410
400void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, 411void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb,
401 bool preserve_contents, 412 bool using_depth_fb, bool preserve_contents,
402 std::optional<std::size_t> single_color_target) { 413 std::optional<std::size_t> single_color_target) {
403 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 414 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
404 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 415 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
@@ -414,9 +425,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
414 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented"); 425 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
415 426
416 // Bind the framebuffer surfaces 427 // Bind the framebuffer surfaces
417 state.draw.draw_framebuffer = framebuffer.handle; 428 current_state.draw.draw_framebuffer = framebuffer.handle;
418 state.Apply(); 429 current_state.ApplyFramebufferState();
419 state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; 430 current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
420 431
421 if (using_color_fb) { 432 if (using_color_fb) {
422 if (single_color_target) { 433 if (single_color_target) {
@@ -494,10 +505,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
494 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 505 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
495 0); 506 0);
496 } 507 }
497 508 SyncViewport(current_state);
498 SyncViewport();
499
500 state.Apply();
501} 509}
502 510
503void RasterizerOpenGL::Clear() { 511void RasterizerOpenGL::Clear() {
@@ -510,22 +518,23 @@ void RasterizerOpenGL::Clear() {
510 bool use_stencil{}; 518 bool use_stencil{};
511 519
512 OpenGLState clear_state; 520 OpenGLState clear_state;
513 clear_state.draw.draw_framebuffer = framebuffer.handle;
514 clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
515 clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
516 clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
517 clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
518
519 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 521 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
520 regs.clear_buffers.A) { 522 regs.clear_buffers.A) {
521 use_color = true; 523 use_color = true;
522 } 524 }
525 if (use_color) {
526 clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
527 clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
528 clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
529 clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
530 }
523 if (regs.clear_buffers.Z) { 531 if (regs.clear_buffers.Z) {
524 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); 532 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
525 use_depth = true; 533 use_depth = true;
526 534
527 // Always enable the depth write when clearing the depth buffer. The depth write mask is 535 // Always enable the depth write when clearing the depth buffer. The depth write mask is
528 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. 536 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to
537 // true.
529 clear_state.depth.test_enabled = true; 538 clear_state.depth.test_enabled = true;
530 clear_state.depth.test_func = GL_ALWAYS; 539 clear_state.depth.test_func = GL_ALWAYS;
531 } 540 }
@@ -542,11 +551,8 @@ void RasterizerOpenGL::Clear() {
542 551
543 ScopeAcquireGLContext acquire_context{emu_window}; 552 ScopeAcquireGLContext acquire_context{emu_window};
544 553
545 ConfigureFramebuffers(use_color, use_depth || use_stencil, false, 554 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
546 regs.clear_buffers.RT.Value()); 555 regs.clear_buffers.RT.Value());
547 // Copy the sRGB setting to the clear state to avoid problem with
548 // specific driver implementations
549 clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled;
550 clear_state.Apply(); 556 clear_state.Apply();
551 557
552 if (use_color) { 558 if (use_color) {
@@ -572,15 +578,14 @@ void RasterizerOpenGL::DrawArrays() {
572 578
573 ScopeAcquireGLContext acquire_context{emu_window}; 579 ScopeAcquireGLContext acquire_context{emu_window};
574 580
575 ConfigureFramebuffers(); 581 ConfigureFramebuffers(state);
576 582 SyncColorMask();
577 SyncDepthTestState(); 583 SyncDepthTestState();
578 SyncStencilTestState(); 584 SyncStencilTestState();
579 SyncBlendState(); 585 SyncBlendState();
580 SyncLogicOpState(); 586 SyncLogicOpState();
581 SyncCullMode(); 587 SyncCullMode();
582 SyncPrimitiveRestart(); 588 SyncPrimitiveRestart();
583 SyncDepthRange();
584 SyncScissorTest(); 589 SyncScissorTest();
585 // Alpha Testing is synced on shaders. 590 // Alpha Testing is synced on shaders.
586 SyncTransformFeedback(); 591 SyncTransformFeedback();
@@ -594,7 +599,7 @@ void RasterizerOpenGL::DrawArrays() {
594 const bool is_indexed = accelerate_draw == AccelDraw::Indexed; 599 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
595 600
596 state.draw.vertex_buffer = buffer_cache.GetHandle(); 601 state.draw.vertex_buffer = buffer_cache.GetHandle();
597 state.Apply(); 602 state.ApplyVertexBufferState();
598 603
599 std::size_t buffer_size = CalculateVertexArraysSize(); 604 std::size_t buffer_size = CalculateVertexArraysSize();
600 605
@@ -621,7 +626,8 @@ void RasterizerOpenGL::DrawArrays() {
621 626
622 buffer_cache.Map(buffer_size); 627 buffer_cache.Map(buffer_size);
623 628
624 SetupVertexArrays(); 629 SetupVertexFormat();
630 SetupVertexBuffer();
625 DrawParameters params = SetupDraw(); 631 DrawParameters params = SetupDraw();
626 SetupShaders(params.primitive_mode); 632 SetupShaders(params.primitive_mode);
627 633
@@ -725,9 +731,9 @@ void RasterizerOpenGL::SamplerInfo::Create() {
725 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); 731 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
726} 732}
727 733
728void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { 734void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) {
729 const GLuint s = sampler.handle; 735 const GLuint s = sampler.handle;
730 736 const Tegra::Texture::TSCEntry& config = info.tsc;
731 if (mag_filter != config.mag_filter) { 737 if (mag_filter != config.mag_filter) {
732 mag_filter = config.mag_filter; 738 mag_filter = config.mag_filter;
733 glSamplerParameteri( 739 glSamplerParameteri(
@@ -778,6 +784,22 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
778 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); 784 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
779 } 785 }
780 } 786 }
787 if (info.tic.use_header_opt_control == 0) {
788 if (GLAD_GL_ARB_texture_filter_anisotropic) {
789 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY,
790 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
791 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
792 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
793 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
794 }
795 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD,
796 static_cast<float>(info.tic.res_min_mip_level.Value()));
797 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD,
798 static_cast<float>(info.tic.res_max_mip_level.Value() == 0
799 ? 16
800 : info.tic.res_max_mip_level.Value()));
801 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f);
802 }
781} 803}
782 804
783u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, 805u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
@@ -875,7 +897,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
875 continue; 897 continue;
876 } 898 }
877 899
878 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); 900 texture_samplers[current_bindpoint].SyncWithConfig(texture);
879 Surface surface = res_cache.GetTextureSurface(texture, entry); 901 Surface surface = res_cache.GetTextureSurface(texture, entry);
880 if (surface != nullptr) { 902 if (surface != nullptr) {
881 state.texture_units[current_bindpoint].texture = surface->Texture().handle; 903 state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -897,14 +919,18 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
897 return current_unit + static_cast<u32>(entries.size()); 919 return current_unit + static_cast<u32>(entries.size());
898} 920}
899 921
900void RasterizerOpenGL::SyncViewport() { 922void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
901 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 923 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
902 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; 924 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
903 925 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
904 state.viewport.x = viewport_rect.left; 926 auto& viewport = current_state.viewports[i];
905 state.viewport.y = viewport_rect.bottom; 927 viewport.x = viewport_rect.left;
906 state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); 928 viewport.y = viewport_rect.bottom;
907 state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); 929 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
930 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
931 viewport.depth_range_far = regs.viewport[i].depth_range_far;
932 viewport.depth_range_near = regs.viewport[i].depth_range_near;
933 }
908} 934}
909 935
910void RasterizerOpenGL::SyncClipEnabled() { 936void RasterizerOpenGL::SyncClipEnabled() {
@@ -946,13 +972,6 @@ void RasterizerOpenGL::SyncPrimitiveRestart() {
946 state.primitive_restart.index = regs.primitive_restart.index; 972 state.primitive_restart.index = regs.primitive_restart.index;
947} 973}
948 974
949void RasterizerOpenGL::SyncDepthRange() {
950 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
951
952 state.depth.depth_range_near = regs.viewport->depth_range_near;
953 state.depth.depth_range_far = regs.viewport->depth_range_far;
954}
955
956void RasterizerOpenGL::SyncDepthTestState() { 975void RasterizerOpenGL::SyncDepthTestState() {
957 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 976 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
958 977
@@ -973,9 +992,6 @@ void RasterizerOpenGL::SyncStencilTestState() {
973 return; 992 return;
974 } 993 }
975 994
976 // TODO(bunnei): Verify behavior when this is not set
977 ASSERT(regs.stencil_two_side_enable);
978
979 state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func); 995 state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func);
980 state.stencil.front.test_ref = regs.stencil_front_func_ref; 996 state.stencil.front.test_ref = regs.stencil_front_func_ref;
981 state.stencil.front.test_mask = regs.stencil_front_func_mask; 997 state.stencil.front.test_mask = regs.stencil_front_func_mask;
@@ -983,36 +999,79 @@ void RasterizerOpenGL::SyncStencilTestState() {
983 state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail); 999 state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail);
984 state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass); 1000 state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass);
985 state.stencil.front.write_mask = regs.stencil_front_mask; 1001 state.stencil.front.write_mask = regs.stencil_front_mask;
1002 if (regs.stencil_two_side_enable) {
1003 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
1004 state.stencil.back.test_ref = regs.stencil_back_func_ref;
1005 state.stencil.back.test_mask = regs.stencil_back_func_mask;
1006 state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
1007 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
1008 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
1009 state.stencil.back.write_mask = regs.stencil_back_mask;
1010 } else {
1011 state.stencil.back.test_func = GL_ALWAYS;
1012 state.stencil.back.test_ref = 0;
1013 state.stencil.back.test_mask = 0xFFFFFFFF;
1014 state.stencil.back.write_mask = 0xFFFFFFFF;
1015 state.stencil.back.action_stencil_fail = GL_KEEP;
1016 state.stencil.back.action_depth_fail = GL_KEEP;
1017 state.stencil.back.action_depth_pass = GL_KEEP;
1018 }
1019}
986 1020
987 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); 1021void RasterizerOpenGL::SyncColorMask() {
988 state.stencil.back.test_ref = regs.stencil_back_func_ref; 1022 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
989 state.stencil.back.test_mask = regs.stencil_back_func_mask; 1023 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
990 state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail); 1024 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
991 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); 1025 auto& dest = state.color_mask[i];
992 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); 1026 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
993 state.stencil.back.write_mask = regs.stencil_back_mask; 1027 dest.green_enabled = (source.G == 0) ? GL_FALSE : GL_TRUE;
1028 dest.blue_enabled = (source.B == 0) ? GL_FALSE : GL_TRUE;
1029 dest.alpha_enabled = (source.A == 0) ? GL_FALSE : GL_TRUE;
1030 }
994} 1031}
995 1032
996void RasterizerOpenGL::SyncBlendState() { 1033void RasterizerOpenGL::SyncBlendState() {
997 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1034 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
998 1035
999 // TODO(Subv): Support more than just render target 0. 1036 state.blend_color.red = regs.blend_color.r;
1000 state.blend.enabled = regs.blend.enable[0] != 0; 1037 state.blend_color.green = regs.blend_color.g;
1001 1038 state.blend_color.blue = regs.blend_color.b;
1002 if (!state.blend.enabled) 1039 state.blend_color.alpha = regs.blend_color.a;
1040
1041 state.independant_blend.enabled = regs.independent_blend_enable;
1042 if (!state.independant_blend.enabled) {
1043 auto& blend = state.blend[0];
1044 blend.enabled = regs.blend.enable[0] != 0;
1045 blend.separate_alpha = regs.blend.separate_alpha;
1046 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
1047 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
1048 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
1049 if (blend.separate_alpha) {
1050 blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
1051 blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
1052 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
1053 }
1054 for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1055 state.blend[i].enabled = false;
1056 }
1003 return; 1057 return;
1058 }
1004 1059
1005 ASSERT_MSG(regs.logic_op.enable == 0, 1060 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1006 "Blending and logic op can't be enabled at the same time."); 1061 auto& blend = state.blend[i];
1007 1062 blend.enabled = regs.blend.enable[i] != 0;
1008 ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented"); 1063 if (!blend.enabled)
1009 ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented"); 1064 continue;
1010 state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb); 1065 blend.separate_alpha = regs.independent_blend[i].separate_alpha;
1011 state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb); 1066 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb);
1012 state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_rgb); 1067 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb);
1013 state.blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_a); 1068 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb);
1014 state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_a); 1069 if (blend.separate_alpha) {
1015 state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_a); 1070 blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a);
1071 blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
1072 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
1073 }
1074 }
1016} 1075}
1017 1076
1018void RasterizerOpenGL::SyncLogicOpState() { 1077void RasterizerOpenGL::SyncLogicOpState() {
@@ -1031,19 +1090,19 @@ void RasterizerOpenGL::SyncLogicOpState() {
1031} 1090}
1032 1091
1033void RasterizerOpenGL::SyncScissorTest() { 1092void RasterizerOpenGL::SyncScissorTest() {
1093 // TODO: what is the correct behavior here, a single scissor for all targets
1094 // or scissor disabled for the rest of the targets?
1034 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1095 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1035
1036 state.scissor.enabled = (regs.scissor_test.enable != 0); 1096 state.scissor.enabled = (regs.scissor_test.enable != 0);
1037 // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's 1097 if (regs.scissor_test.enable == 0) {
1038 // implemented. 1098 return;
1039 if (regs.scissor_test.enable != 0) {
1040 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1041 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1042 state.scissor.x = regs.scissor_test.min_x;
1043 state.scissor.y = regs.scissor_test.min_y;
1044 state.scissor.width = width;
1045 state.scissor.height = height;
1046 } 1099 }
1100 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1101 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1102 state.scissor.x = regs.scissor_test.min_x;
1103 state.scissor.y = regs.scissor_test.min_y;
1104 state.scissor.width = width;
1105 state.scissor.height = height;
1047} 1106}
1048 1107
1049void RasterizerOpenGL::SyncTransformFeedback() { 1108void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1068,9 +1127,8 @@ void RasterizerOpenGL::CheckAlphaTests() {
1068 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1127 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1069 1128
1070 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) { 1129 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
1071 LOG_CRITICAL( 1130 LOG_CRITICAL(Render_OpenGL, "Alpha Testing is enabled with Multiple Render Targets, "
1072 Render_OpenGL, 1131 "this behavior is undefined.");
1073 "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
1074 UNREACHABLE(); 1132 UNREACHABLE();
1075 } 1133 }
1076} 1134}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 47097c569..8ef0f6c12 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -88,7 +88,7 @@ private:
88 /// SamplerInfo struct. 88 /// SamplerInfo struct.
89 void Create(); 89 void Create();
90 /// Syncs the sampler object with the config, updating any necessary state. 90 /// Syncs the sampler object with the config, updating any necessary state.
91 void SyncWithConfig(const Tegra::Texture::TSCEntry& config); 91 void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info);
92 92
93 private: 93 private:
94 Tegra::Texture::TextureFilter mag_filter; 94 Tegra::Texture::TextureFilter mag_filter;
@@ -109,8 +109,8 @@ private:
109 * @param preserve_contents If true, tries to preserve data from a previously used framebuffer. 109 * @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
110 * @param single_color_target Specifies if a single color buffer target should be used. 110 * @param single_color_target Specifies if a single color buffer target should be used.
111 */ 111 */
112 void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, 112 void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true,
113 bool preserve_contents = true, 113 bool using_depth_fb = true, bool preserve_contents = true,
114 std::optional<std::size_t> single_color_target = {}); 114 std::optional<std::size_t> single_color_target = {});
115 115
116 /* 116 /*
@@ -133,8 +133,8 @@ private:
133 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 133 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
134 GLenum primitive_mode, u32 current_unit); 134 GLenum primitive_mode, u32 current_unit);
135 135
136 /// Syncs the viewport to match the guest state 136 /// Syncs the viewport and depth range to match the guest state
137 void SyncViewport(); 137 void SyncViewport(OpenGLState& current_state);
138 138
139 /// Syncs the clip enabled status to match the guest state 139 /// Syncs the clip enabled status to match the guest state
140 void SyncClipEnabled(); 140 void SyncClipEnabled();
@@ -148,9 +148,6 @@ private:
148 /// Syncs the primitve restart to match the guest state 148 /// Syncs the primitve restart to match the guest state
149 void SyncPrimitiveRestart(); 149 void SyncPrimitiveRestart();
150 150
151 /// Syncs the depth range to match the guest state
152 void SyncDepthRange();
153
154 /// Syncs the depth test state to match the guest state 151 /// Syncs the depth test state to match the guest state
155 void SyncDepthTestState(); 152 void SyncDepthTestState();
156 153
@@ -172,6 +169,9 @@ private:
172 /// Syncs the point state to match the guest state 169 /// Syncs the point state to match the guest state
173 void SyncPointState(); 170 void SyncPointState();
174 171
172 /// Syncs Color Mask
173 void SyncColorMask();
174
175 /// Check asserts for alpha testing. 175 /// Check asserts for alpha testing.
176 void CheckAlphaTests(); 176 void CheckAlphaTests();
177 177
@@ -207,7 +207,8 @@ private:
207 207
208 std::size_t CalculateIndexBufferSize() const; 208 std::size_t CalculateIndexBufferSize() const;
209 209
210 void SetupVertexArrays(); 210 void SetupVertexFormat();
211 void SetupVertexBuffer();
211 212
212 DrawParameters SetupDraw(); 213 DrawParameters SetupDraw();
213 214
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index f194a7687..b44ecfa1c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -15,7 +15,9 @@
15#include "core/memory.h" 15#include "core/memory.h"
16#include "core/settings.h" 16#include "core/settings.h"
17#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/renderer_opengl/gl_rasterizer.h"
18#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 19#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
20#include "video_core/renderer_opengl/gl_state.h"
19#include "video_core/renderer_opengl/utils.h" 21#include "video_core/renderer_opengl/utils.h"
20#include "video_core/surface.h" 22#include "video_core/surface.h"
21#include "video_core/textures/astc.h" 23#include "video_core/textures/astc.h"
@@ -58,16 +60,14 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
58 60
59std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only, 61std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only,
60 bool uncompressed) const { 62 bool uncompressed) const {
61 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 63 const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
64 const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
62 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; 65 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
63 u32 m_depth = (layer_only ? 1U : depth); 66 u32 m_depth = (layer_only ? 1U : depth);
64 u32 m_width = MipWidth(mip_level); 67 u32 m_width = MipWidth(mip_level);
65 u32 m_height = MipHeight(mip_level); 68 u32 m_height = MipHeight(mip_level);
66 m_width = uncompressed ? m_width 69 m_width = uncompressed ? m_width : std::max(1U, (m_width + tile_x - 1) / tile_x);
67 : std::max(1U, (m_width + compression_factor - 1) / compression_factor); 70 m_height = uncompressed ? m_height : std::max(1U, (m_height + tile_y - 1) / tile_y);
68 m_height = uncompressed
69 ? m_height
70 : std::max(1U, (m_height + compression_factor - 1) / compression_factor);
71 m_depth = std::max(1U, m_depth >> mip_level); 71 m_depth = std::max(1U, m_depth >> mip_level);
72 u32 m_block_height = MipBlockHeight(mip_level); 72 u32 m_block_height = MipBlockHeight(mip_level);
73 u32 m_block_depth = MipBlockDepth(mip_level); 73 u32 m_block_depth = MipBlockDepth(mip_level);
@@ -128,6 +128,13 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
128 params.target = SurfaceTarget::Texture2D; 128 params.target = SurfaceTarget::Texture2D;
129 } 129 }
130 break; 130 break;
131 case SurfaceTarget::TextureCubeArray:
132 params.depth = config.tic.Depth() * 6;
133 if (!entry.IsArray()) {
134 ASSERT(params.depth == 6);
135 params.target = SurfaceTarget::TextureCubemap;
136 }
137 break;
131 default: 138 default:
132 LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target)); 139 LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target));
133 UNREACHABLE(); 140 UNREACHABLE();
@@ -305,6 +312,10 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
305 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB 312 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
306 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB 313 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
307 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB 314 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
315 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
316 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
317 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8
318 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB
308 319
309 // Depth formats 320 // Depth formats
310 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 321 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -334,6 +345,8 @@ static GLenum SurfaceTargetToGL(SurfaceTarget target) {
334 return GL_TEXTURE_2D_ARRAY; 345 return GL_TEXTURE_2D_ARRAY;
335 case SurfaceTarget::TextureCubemap: 346 case SurfaceTarget::TextureCubemap:
336 return GL_TEXTURE_CUBE_MAP; 347 return GL_TEXTURE_CUBE_MAP;
348 case SurfaceTarget::TextureCubeArray:
349 return GL_TEXTURE_CUBE_MAP_ARRAY_ARB;
337 } 350 }
338 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); 351 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
339 UNREACHABLE(); 352 UNREACHABLE();
@@ -364,15 +377,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
364 377
365 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual 378 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
366 // pixel values. 379 // pixel values.
367 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 380 const u32 tile_size_x{GetDefaultBlockWidth(format)};
381 const u32 tile_size_y{GetDefaultBlockHeight(format)};
368 382
369 if (morton_to_gl) { 383 if (morton_to_gl) {
370 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 384 const std::vector<u8> data =
371 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth); 385 Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
386 stride, height, depth, block_height, block_depth);
372 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 387 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
373 memcpy(gl_buffer, data.data(), size_to_copy); 388 memcpy(gl_buffer, data.data(), size_to_copy);
374 } else { 389 } else {
375 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth, 390 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
391 (height + tile_size_y - 1) / tile_size_y, depth,
376 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), 392 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
377 gl_buffer, false, block_height, block_depth); 393 gl_buffer, false, block_height, block_depth);
378 } 394 }
@@ -440,6 +456,10 @@ static constexpr GLConversionArray morton_to_gl_fns = {
440 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, 456 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
441 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, 457 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
442 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, 458 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
459 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
460 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
461 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
462 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
443 MortonCopy<true, PixelFormat::Z32F>, 463 MortonCopy<true, PixelFormat::Z32F>,
444 MortonCopy<true, PixelFormat::Z16>, 464 MortonCopy<true, PixelFormat::Z16>,
445 MortonCopy<true, PixelFormat::Z24S8>, 465 MortonCopy<true, PixelFormat::Z24S8>,
@@ -508,6 +528,10 @@ static constexpr GLConversionArray gl_to_morton_fns = {
508 nullptr, 528 nullptr,
509 nullptr, 529 nullptr,
510 nullptr, 530 nullptr,
531 nullptr,
532 nullptr,
533 nullptr,
534 nullptr,
511 MortonCopy<false, PixelFormat::Z32F>, 535 MortonCopy<false, PixelFormat::Z32F>,
512 MortonCopy<false, PixelFormat::Z16>, 536 MortonCopy<false, PixelFormat::Z16>,
513 MortonCopy<false, PixelFormat::Z24S8>, 537 MortonCopy<false, PixelFormat::Z24S8>,
@@ -526,8 +550,8 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
526 if (params.is_layered) { 550 if (params.is_layered) {
527 u64 offset = params.GetMipmapLevelOffset(mip_level); 551 u64 offset = params.GetMipmapLevelOffset(mip_level);
528 u64 offset_gl = 0; 552 u64 offset_gl = 0;
529 u64 layer_size = params.LayerMemorySize(); 553 const u64 layer_size = params.LayerMemorySize();
530 u64 gl_size = params.LayerSizeGL(mip_level); 554 const u64 gl_size = params.LayerSizeGL(mip_level);
531 for (u32 i = 0; i < params.depth; i++) { 555 for (u32 i = 0; i < params.depth; i++) {
532 functions[static_cast<std::size_t>(params.pixel_format)]( 556 functions[static_cast<std::size_t>(params.pixel_format)](
533 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 557 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
@@ -537,7 +561,7 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
537 offset_gl += gl_size; 561 offset_gl += gl_size;
538 } 562 }
539 } else { 563 } else {
540 u64 offset = params.GetMipmapLevelOffset(mip_level); 564 const u64 offset = params.GetMipmapLevelOffset(mip_level);
541 functions[static_cast<std::size_t>(params.pixel_format)]( 565 functions[static_cast<std::size_t>(params.pixel_format)](
542 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 566 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
543 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(), 567 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
@@ -545,9 +569,11 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
545 } 569 }
546} 570}
547 571
572MICROPROFILE_DEFINE(OpenGL_BlitSurface, "OpenGL", "BlitSurface", MP_RGB(128, 192, 64));
548static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, 573static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
549 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, 574 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
550 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 575 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
576 MICROPROFILE_SCOPE(OpenGL_BlitSurface);
551 577
552 const auto& src_params{src_surface->GetSurfaceParams()}; 578 const auto& src_params{src_surface->GetSurfaceParams()};
553 const auto& dst_params{dst_surface->GetSurfaceParams()}; 579 const auto& dst_params{dst_surface->GetSurfaceParams()};
@@ -560,7 +586,7 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
560 state.draw.draw_framebuffer = draw_fb_handle; 586 state.draw.draw_framebuffer = draw_fb_handle;
561 // Set sRGB enabled if the destination surfaces need it 587 // Set sRGB enabled if the destination surfaces need it
562 state.framebuffer_srgb.enabled = dst_params.srgb_conversion; 588 state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
563 state.Apply(); 589 state.ApplyFramebufferState();
564 590
565 u32 buffers{}; 591 u32 buffers{};
566 592
@@ -687,18 +713,20 @@ static void FastCopySurface(const Surface& src_surface, const Surface& dst_surfa
687 0, 0, width, height, 1); 713 0, 0, width, height, 1);
688} 714}
689 715
716MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
690static void CopySurface(const Surface& src_surface, const Surface& dst_surface, 717static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
691 GLuint copy_pbo_handle, GLenum src_attachment = 0, 718 const GLuint copy_pbo_handle, const GLenum src_attachment = 0,
692 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 719 const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) {
720 MICROPROFILE_SCOPE(OpenGL_CopySurface);
693 ASSERT_MSG(dst_attachment == 0, "Unimplemented"); 721 ASSERT_MSG(dst_attachment == 0, "Unimplemented");
694 722
695 const auto& src_params{src_surface->GetSurfaceParams()}; 723 const auto& src_params{src_surface->GetSurfaceParams()};
696 const auto& dst_params{dst_surface->GetSurfaceParams()}; 724 const auto& dst_params{dst_surface->GetSurfaceParams()};
697 725
698 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 726 const auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
699 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 727 const auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
700 728
701 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); 729 const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
702 730
703 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 731 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
704 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 732 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
@@ -722,13 +750,10 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
722 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 750 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
723 "reinterpretation but the texture is tiled."); 751 "reinterpretation but the texture is tiled.");
724 } 752 }
725 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; 753 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
726 std::vector<u8> data(remaining_size);
727 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
728 data.size());
729 754
730 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, 755 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
731 data.data()); 756 Memory::GetPointer(dst_params.addr + src_params.size_in_bytes));
732 } 757 }
733 758
734 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 759 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -754,6 +779,7 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
754 break; 779 break;
755 case SurfaceTarget::Texture3D: 780 case SurfaceTarget::Texture3D:
756 case SurfaceTarget::Texture2DArray: 781 case SurfaceTarget::Texture2DArray:
782 case SurfaceTarget::TextureCubeArray:
757 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height, 783 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
758 static_cast<GLsizei>(dst_params.depth), dest_format.format, 784 static_cast<GLsizei>(dst_params.depth), dest_format.format,
759 dest_format.type, nullptr); 785 dest_format.type, nullptr);
@@ -806,6 +832,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
806 break; 832 break;
807 case SurfaceTarget::Texture3D: 833 case SurfaceTarget::Texture3D:
808 case SurfaceTarget::Texture2DArray: 834 case SurfaceTarget::Texture2DArray:
835 case SurfaceTarget::TextureCubeArray:
809 glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level, 836 glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
810 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(), 837 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
811 params.depth); 838 params.depth);
@@ -897,21 +924,26 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
897 * typical desktop GPUs. 924 * typical desktop GPUs.
898 */ 925 */
899static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, 926static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
900 u32 width, u32 height) { 927 u32 width, u32 height, u32 depth) {
901 switch (pixel_format) { 928 switch (pixel_format) {
902 case PixelFormat::ASTC_2D_4X4: 929 case PixelFormat::ASTC_2D_4X4:
903 case PixelFormat::ASTC_2D_8X8: 930 case PixelFormat::ASTC_2D_8X8:
904 case PixelFormat::ASTC_2D_8X5: 931 case PixelFormat::ASTC_2D_8X5:
905 case PixelFormat::ASTC_2D_5X4: 932 case PixelFormat::ASTC_2D_5X4:
933 case PixelFormat::ASTC_2D_5X5:
906 case PixelFormat::ASTC_2D_4X4_SRGB: 934 case PixelFormat::ASTC_2D_4X4_SRGB:
907 case PixelFormat::ASTC_2D_8X8_SRGB: 935 case PixelFormat::ASTC_2D_8X8_SRGB:
908 case PixelFormat::ASTC_2D_8X5_SRGB: 936 case PixelFormat::ASTC_2D_8X5_SRGB:
909 case PixelFormat::ASTC_2D_5X4_SRGB: { 937 case PixelFormat::ASTC_2D_5X4_SRGB:
938 case PixelFormat::ASTC_2D_5X5_SRGB:
939 case PixelFormat::ASTC_2D_10X8:
940 case PixelFormat::ASTC_2D_10X8_SRGB: {
910 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 941 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
911 u32 block_width{}; 942 u32 block_width{};
912 u32 block_height{}; 943 u32 block_height{};
913 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format); 944 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
914 data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height); 945 data =
946 Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
915 break; 947 break;
916 } 948 }
917 case PixelFormat::S8Z24: 949 case PixelFormat::S8Z24:
@@ -940,7 +972,11 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
940 case PixelFormat::ASTC_2D_4X4: 972 case PixelFormat::ASTC_2D_4X4:
941 case PixelFormat::ASTC_2D_8X8: 973 case PixelFormat::ASTC_2D_8X8:
942 case PixelFormat::ASTC_2D_4X4_SRGB: 974 case PixelFormat::ASTC_2D_4X4_SRGB:
943 case PixelFormat::ASTC_2D_8X8_SRGB: { 975 case PixelFormat::ASTC_2D_8X8_SRGB:
976 case PixelFormat::ASTC_2D_5X5:
977 case PixelFormat::ASTC_2D_5X5_SRGB:
978 case PixelFormat::ASTC_2D_10X8:
979 case PixelFormat::ASTC_2D_10X8_SRGB: {
944 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", 980 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
945 static_cast<u32>(pixel_format)); 981 static_cast<u32>(pixel_format));
946 UNREACHABLE(); 982 UNREACHABLE();
@@ -953,7 +989,7 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
953 } 989 }
954} 990}
955 991
956MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 992MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
957void CachedSurface::LoadGLBuffer() { 993void CachedSurface::LoadGLBuffer() {
958 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 994 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
959 gl_buffer.resize(params.max_mip_level); 995 gl_buffer.resize(params.max_mip_level);
@@ -971,7 +1007,7 @@ void CachedSurface::LoadGLBuffer() {
971 } 1007 }
972 for (u32 i = 0; i < params.max_mip_level; i++) 1008 for (u32 i = 0; i < params.max_mip_level; i++)
973 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i), 1009 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
974 params.MipHeight(i)); 1010 params.MipHeight(i), params.MipDepth(i));
975} 1011}
976 1012
977MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 1013MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
@@ -1055,6 +1091,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
1055 &gl_buffer[mip_map][buffer_offset]); 1091 &gl_buffer[mip_map][buffer_offset]);
1056 break; 1092 break;
1057 case SurfaceTarget::Texture2DArray: 1093 case SurfaceTarget::Texture2DArray:
1094 case SurfaceTarget::TextureCubeArray:
1058 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, 1095 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
1059 static_cast<GLsizei>(params.MipWidth(mip_map)), 1096 static_cast<GLsizei>(params.MipWidth(mip_map)),
1060 static_cast<GLsizei>(params.MipHeight(mip_map)), 1097 static_cast<GLsizei>(params.MipHeight(mip_map)),
@@ -1104,6 +1141,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
1104 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); 1141 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
1105 break; 1142 break;
1106 case SurfaceTarget::Texture2DArray: 1143 case SurfaceTarget::Texture2DArray:
1144 case SurfaceTarget::TextureCubeArray:
1107 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, 1145 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
1108 static_cast<GLsizei>(rect.GetWidth()), 1146 static_cast<GLsizei>(rect.GetWidth()),
1109 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, 1147 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
@@ -1133,7 +1171,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
1133 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 1171 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1134} 1172}
1135 1173
1136MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 1174MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
1137void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { 1175void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
1138 if (params.type == SurfaceType::Fill) 1176 if (params.type == SurfaceType::Fill)
1139 return; 1177 return;
@@ -1144,7 +1182,8 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
1144 UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); 1182 UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle);
1145} 1183}
1146 1184
1147RasterizerCacheOpenGL::RasterizerCacheOpenGL() { 1185RasterizerCacheOpenGL::RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer)
1186 : RasterizerCache{rasterizer} {
1148 read_framebuffer.Create(); 1187 read_framebuffer.Create();
1149 draw_framebuffer.Create(); 1188 draw_framebuffer.Create();
1150 copy_pbo.Create(); 1189 copy_pbo.Create();
@@ -1306,6 +1345,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1306 break; 1345 break;
1307 case SurfaceTarget::TextureCubemap: 1346 case SurfaceTarget::TextureCubemap:
1308 case SurfaceTarget::Texture3D: 1347 case SurfaceTarget::Texture3D:
1348 case SurfaceTarget::Texture2DArray:
1349 case SurfaceTarget::TextureCubeArray:
1309 AccurateCopySurface(old_surface, new_surface); 1350 AccurateCopySurface(old_surface, new_surface);
1310 break; 1351 break;
1311 default: 1352 default:
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index f255f4419..494f6b903 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -49,6 +49,8 @@ struct SurfaceParams {
49 return "Texture2DArray"; 49 return "Texture2DArray";
50 case SurfaceTarget::TextureCubemap: 50 case SurfaceTarget::TextureCubemap:
51 return "TextureCubemap"; 51 return "TextureCubemap";
52 case SurfaceTarget::TextureCubeArray:
53 return "TextureCubeArray";
52 default: 54 default:
53 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); 55 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
54 UNREACHABLE(); 56 UNREACHABLE();
@@ -139,7 +141,7 @@ struct SurfaceParams {
139 } 141 }
140 142
141 u32 MipDepth(u32 mip_level) const { 143 u32 MipDepth(u32 mip_level) const {
142 return std::max(1U, depth >> mip_level); 144 return is_layered ? depth : std::max(1U, depth >> mip_level);
143 } 145 }
144 146
145 // Auto block resizing algorithm from: 147 // Auto block resizing algorithm from:
@@ -262,6 +264,8 @@ struct hash<SurfaceReserveKey> {
262 264
263namespace OpenGL { 265namespace OpenGL {
264 266
267class RasterizerOpenGL;
268
265class CachedSurface final : public RasterizerCacheObject { 269class CachedSurface final : public RasterizerCacheObject {
266public: 270public:
267 CachedSurface(const SurfaceParams& params); 271 CachedSurface(const SurfaceParams& params);
@@ -309,7 +313,7 @@ private:
309 313
310class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 314class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
311public: 315public:
312 RasterizerCacheOpenGL(); 316 explicit RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer);
313 317
314 /// Get a surface based on the texture configuration 318 /// Get a surface based on the texture configuration
315 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config, 319 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config,
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
new file mode 100644
index 000000000..c17d5ac00
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -0,0 +1,186 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <utility>
6#include <glad/glad.h>
7#include "common/common_types.h"
8#include "common/microprofile.h"
9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/renderer_opengl/gl_shader_util.h"
11#include "video_core/renderer_opengl/gl_state.h"
12
13MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192));
14MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192));
15
16namespace OpenGL {
17
18void OGLTexture::Create() {
19 if (handle != 0)
20 return;
21
22 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
23 glGenTextures(1, &handle);
24}
25
26void OGLTexture::Release() {
27 if (handle == 0)
28 return;
29
30 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
31 glDeleteTextures(1, &handle);
32 OpenGLState::GetCurState().UnbindTexture(handle).Apply();
33 handle = 0;
34}
35
36void OGLSampler::Create() {
37 if (handle != 0)
38 return;
39
40 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
41 glGenSamplers(1, &handle);
42}
43
44void OGLSampler::Release() {
45 if (handle == 0)
46 return;
47
48 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
49 glDeleteSamplers(1, &handle);
50 OpenGLState::GetCurState().ResetSampler(handle).Apply();
51 handle = 0;
52}
53
54void OGLShader::Create(const char* source, GLenum type) {
55 if (handle != 0)
56 return;
57 if (source == nullptr)
58 return;
59
60 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
61 handle = GLShader::LoadShader(source, type);
62}
63
64void OGLShader::Release() {
65 if (handle == 0)
66 return;
67
68 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
69 glDeleteShader(handle);
70 handle = 0;
71}
72
73void OGLProgram::CreateFromSource(const char* vert_shader, const char* geo_shader,
74 const char* frag_shader, bool separable_program) {
75 OGLShader vert, geo, frag;
76 if (vert_shader)
77 vert.Create(vert_shader, GL_VERTEX_SHADER);
78 if (geo_shader)
79 geo.Create(geo_shader, GL_GEOMETRY_SHADER);
80 if (frag_shader)
81 frag.Create(frag_shader, GL_FRAGMENT_SHADER);
82
83 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
84 Create(separable_program, vert.handle, geo.handle, frag.handle);
85}
86
87void OGLProgram::Release() {
88 if (handle == 0)
89 return;
90
91 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
92 glDeleteProgram(handle);
93 OpenGLState::GetCurState().ResetProgram(handle).Apply();
94 handle = 0;
95}
96
97void OGLPipeline::Create() {
98 if (handle != 0)
99 return;
100
101 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
102 glGenProgramPipelines(1, &handle);
103}
104
105void OGLPipeline::Release() {
106 if (handle == 0)
107 return;
108
109 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
110 glDeleteProgramPipelines(1, &handle);
111 OpenGLState::GetCurState().ResetPipeline(handle).Apply();
112 handle = 0;
113}
114
115void OGLBuffer::Create() {
116 if (handle != 0)
117 return;
118
119 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
120 glGenBuffers(1, &handle);
121}
122
123void OGLBuffer::Release() {
124 if (handle == 0)
125 return;
126
127 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
128 glDeleteBuffers(1, &handle);
129 OpenGLState::GetCurState().ResetBuffer(handle).Apply();
130 handle = 0;
131}
132
133void OGLSync::Create() {
134 if (handle != 0)
135 return;
136
137 // Don't profile here, this one is expected to happen ingame.
138 handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
139}
140
141void OGLSync::Release() {
142 if (handle == 0)
143 return;
144
145 // Don't profile here, this one is expected to happen ingame.
146 glDeleteSync(handle);
147 handle = 0;
148}
149
150void OGLVertexArray::Create() {
151 if (handle != 0)
152 return;
153
154 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
155 glGenVertexArrays(1, &handle);
156}
157
158void OGLVertexArray::Release() {
159 if (handle == 0)
160 return;
161
162 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
163 glDeleteVertexArrays(1, &handle);
164 OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
165 handle = 0;
166}
167
168void OGLFramebuffer::Create() {
169 if (handle != 0)
170 return;
171
172 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
173 glGenFramebuffers(1, &handle);
174}
175
176void OGLFramebuffer::Release() {
177 if (handle == 0)
178 return;
179
180 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
181 glDeleteFramebuffers(1, &handle);
182 OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
183 handle = 0;
184}
185
186} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 3bc1b83b5..e33f1e973 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -8,7 +8,6 @@
8#include <glad/glad.h> 8#include <glad/glad.h>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/renderer_opengl/gl_shader_util.h" 10#include "video_core/renderer_opengl/gl_shader_util.h"
11#include "video_core/renderer_opengl/gl_state.h"
12 11
13namespace OpenGL { 12namespace OpenGL {
14 13
@@ -29,20 +28,10 @@ public:
29 } 28 }
30 29
31 /// Creates a new internal OpenGL resource and stores the handle 30 /// Creates a new internal OpenGL resource and stores the handle
32 void Create() { 31 void Create();
33 if (handle != 0)
34 return;
35 glGenTextures(1, &handle);
36 }
37 32
38 /// Deletes the internal OpenGL resource 33 /// Deletes the internal OpenGL resource
39 void Release() { 34 void Release();
40 if (handle == 0)
41 return;
42 glDeleteTextures(1, &handle);
43 OpenGLState::GetCurState().UnbindTexture(handle).Apply();
44 handle = 0;
45 }
46 35
47 GLuint handle = 0; 36 GLuint handle = 0;
48}; 37};
@@ -64,20 +53,10 @@ public:
64 } 53 }
65 54
66 /// Creates a new internal OpenGL resource and stores the handle 55 /// Creates a new internal OpenGL resource and stores the handle
67 void Create() { 56 void Create();
68 if (handle != 0)
69 return;
70 glGenSamplers(1, &handle);
71 }
72 57
73 /// Deletes the internal OpenGL resource 58 /// Deletes the internal OpenGL resource
74 void Release() { 59 void Release();
75 if (handle == 0)
76 return;
77 glDeleteSamplers(1, &handle);
78 OpenGLState::GetCurState().ResetSampler(handle).Apply();
79 handle = 0;
80 }
81 60
82 GLuint handle = 0; 61 GLuint handle = 0;
83}; 62};
@@ -98,20 +77,9 @@ public:
98 return *this; 77 return *this;
99 } 78 }
100 79
101 void Create(const char* source, GLenum type) { 80 void Create(const char* source, GLenum type);
102 if (handle != 0)
103 return;
104 if (source == nullptr)
105 return;
106 handle = GLShader::LoadShader(source, type);
107 }
108 81
109 void Release() { 82 void Release();
110 if (handle == 0)
111 return;
112 glDeleteShader(handle);
113 handle = 0;
114 }
115 83
116 GLuint handle = 0; 84 GLuint handle = 0;
117}; 85};
@@ -141,25 +109,10 @@ public:
141 109
142 /// Creates a new internal OpenGL resource and stores the handle 110 /// Creates a new internal OpenGL resource and stores the handle
143 void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader, 111 void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader,
144 bool separable_program = false) { 112 bool separable_program = false);
145 OGLShader vert, geo, frag;
146 if (vert_shader)
147 vert.Create(vert_shader, GL_VERTEX_SHADER);
148 if (geo_shader)
149 geo.Create(geo_shader, GL_GEOMETRY_SHADER);
150 if (frag_shader)
151 frag.Create(frag_shader, GL_FRAGMENT_SHADER);
152 Create(separable_program, vert.handle, geo.handle, frag.handle);
153 }
154 113
155 /// Deletes the internal OpenGL resource 114 /// Deletes the internal OpenGL resource
156 void Release() { 115 void Release();
157 if (handle == 0)
158 return;
159 glDeleteProgram(handle);
160 OpenGLState::GetCurState().ResetProgram(handle).Apply();
161 handle = 0;
162 }
163 116
164 GLuint handle = 0; 117 GLuint handle = 0;
165}; 118};
@@ -178,20 +131,10 @@ public:
178 } 131 }
179 132
180 /// Creates a new internal OpenGL resource and stores the handle 133 /// Creates a new internal OpenGL resource and stores the handle
181 void Create() { 134 void Create();
182 if (handle != 0)
183 return;
184 glGenProgramPipelines(1, &handle);
185 }
186 135
187 /// Deletes the internal OpenGL resource 136 /// Deletes the internal OpenGL resource
188 void Release() { 137 void Release();
189 if (handle == 0)
190 return;
191 glDeleteProgramPipelines(1, &handle);
192 OpenGLState::GetCurState().ResetPipeline(handle).Apply();
193 handle = 0;
194 }
195 138
196 GLuint handle = 0; 139 GLuint handle = 0;
197}; 140};
@@ -213,20 +156,10 @@ public:
213 } 156 }
214 157
215 /// Creates a new internal OpenGL resource and stores the handle 158 /// Creates a new internal OpenGL resource and stores the handle
216 void Create() { 159 void Create();
217 if (handle != 0)
218 return;
219 glGenBuffers(1, &handle);
220 }
221 160
222 /// Deletes the internal OpenGL resource 161 /// Deletes the internal OpenGL resource
223 void Release() { 162 void Release();
224 if (handle == 0)
225 return;
226 glDeleteBuffers(1, &handle);
227 OpenGLState::GetCurState().ResetBuffer(handle).Apply();
228 handle = 0;
229 }
230 163
231 GLuint handle = 0; 164 GLuint handle = 0;
232}; 165};
@@ -247,19 +180,10 @@ public:
247 } 180 }
248 181
249 /// Creates a new internal OpenGL resource and stores the handle 182 /// Creates a new internal OpenGL resource and stores the handle
250 void Create() { 183 void Create();
251 if (handle != 0)
252 return;
253 handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
254 }
255 184
256 /// Deletes the internal OpenGL resource 185 /// Deletes the internal OpenGL resource
257 void Release() { 186 void Release();
258 if (handle == 0)
259 return;
260 glDeleteSync(handle);
261 handle = 0;
262 }
263 187
264 GLsync handle = 0; 188 GLsync handle = 0;
265}; 189};
@@ -281,20 +205,10 @@ public:
281 } 205 }
282 206
283 /// Creates a new internal OpenGL resource and stores the handle 207 /// Creates a new internal OpenGL resource and stores the handle
284 void Create() { 208 void Create();
285 if (handle != 0)
286 return;
287 glGenVertexArrays(1, &handle);
288 }
289 209
290 /// Deletes the internal OpenGL resource 210 /// Deletes the internal OpenGL resource
291 void Release() { 211 void Release();
292 if (handle == 0)
293 return;
294 glDeleteVertexArrays(1, &handle);
295 OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
296 handle = 0;
297 }
298 212
299 GLuint handle = 0; 213 GLuint handle = 0;
300}; 214};
@@ -316,20 +230,10 @@ public:
316 } 230 }
317 231
318 /// Creates a new internal OpenGL resource and stores the handle 232 /// Creates a new internal OpenGL resource and stores the handle
319 void Create() { 233 void Create();
320 if (handle != 0)
321 return;
322 glGenFramebuffers(1, &handle);
323 }
324 234
325 /// Deletes the internal OpenGL resource 235 /// Deletes the internal OpenGL resource
326 void Release() { 236 void Release();
327 if (handle == 0)
328 return;
329 glDeleteFramebuffers(1, &handle);
330 OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
331 handle = 0;
332 }
333 237
334 GLuint handle = 0; 238 GLuint handle = 0;
335}; 239};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 9522fd344..a85a7c0c5 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -6,10 +6,10 @@
6#include "core/core.h" 6#include "core/core.h"
7#include "core/memory.h" 7#include "core/memory.h"
8#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_opengl/gl_rasterizer.h"
9#include "video_core/renderer_opengl/gl_shader_cache.h" 10#include "video_core/renderer_opengl/gl_shader_cache.h"
10#include "video_core/renderer_opengl/gl_shader_manager.h" 11#include "video_core/renderer_opengl/gl_shader_manager.h"
11#include "video_core/renderer_opengl/utils.h" 12#include "video_core/renderer_opengl/utils.h"
12#include "video_core/utils.h"
13 13
14namespace OpenGL { 14namespace OpenGL {
15 15
@@ -121,12 +121,16 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
121} 121}
122 122
123GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, 123GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
124 const std::string& glsl_topology, 124 const std::string& glsl_topology, u32 max_vertices,
125 const std::string& debug_name) { 125 const std::string& debug_name) {
126 if (target_program.handle != 0) { 126 if (target_program.handle != 0) {
127 return target_program.handle; 127 return target_program.handle;
128 } 128 }
129 const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"}; 129 std::string source = "#version 430 core\n";
130 source += "layout (" + glsl_topology + ") in;\n";
131 source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n';
132 source += geometry_programs.code;
133
130 OGLShader shader; 134 OGLShader shader;
131 shader.Create(source.c_str(), GL_GEOMETRY_SHADER); 135 shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
132 target_program.Create(true, shader.handle); 136 target_program.Create(true, shader.handle);
@@ -135,6 +139,8 @@ GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
135 return target_program.handle; 139 return target_program.handle;
136}; 140};
137 141
142ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {}
143
138Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 144Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
139 const VAddr program_addr{GetShaderAddress(program)}; 145 const VAddr program_addr{GetShaderAddress(program)};
140 146
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index a210f1731..ffbf21831 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -16,6 +16,8 @@
16namespace OpenGL { 16namespace OpenGL {
17 17
18class CachedShader; 18class CachedShader;
19class RasterizerOpenGL;
20
19using Shader = std::shared_ptr<CachedShader>; 21using Shader = std::shared_ptr<CachedShader>;
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 22using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 23
@@ -46,22 +48,23 @@ public:
46 } 48 }
47 switch (primitive_mode) { 49 switch (primitive_mode) {
48 case GL_POINTS: 50 case GL_POINTS:
49 return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints"); 51 return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
50 case GL_LINES: 52 case GL_LINES:
51 case GL_LINE_STRIP: 53 case GL_LINE_STRIP:
52 return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines"); 54 return LazyGeometryProgram(geometry_programs.lines, "lines", 2, "ShaderLines");
53 case GL_LINES_ADJACENCY: 55 case GL_LINES_ADJACENCY:
54 case GL_LINE_STRIP_ADJACENCY: 56 case GL_LINE_STRIP_ADJACENCY:
55 return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", 57 return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", 4,
56 "ShaderLinesAdjacency"); 58 "ShaderLinesAdjacency");
57 case GL_TRIANGLES: 59 case GL_TRIANGLES:
58 case GL_TRIANGLE_STRIP: 60 case GL_TRIANGLE_STRIP:
59 case GL_TRIANGLE_FAN: 61 case GL_TRIANGLE_FAN:
60 return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles"); 62 return LazyGeometryProgram(geometry_programs.triangles, "triangles", 3,
63 "ShaderTriangles");
61 case GL_TRIANGLES_ADJACENCY: 64 case GL_TRIANGLES_ADJACENCY:
62 case GL_TRIANGLE_STRIP_ADJACENCY: 65 case GL_TRIANGLE_STRIP_ADJACENCY:
63 return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency", 66 return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
64 "ShaderLines"); 67 6, "ShaderTrianglesAdjacency");
65 default: 68 default:
66 UNREACHABLE_MSG("Unknown primitive mode."); 69 UNREACHABLE_MSG("Unknown primitive mode.");
67 } 70 }
@@ -76,7 +79,7 @@ public:
76private: 79private:
77 /// Generates a geometry shader or returns one that already exists. 80 /// Generates a geometry shader or returns one that already exists.
78 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology, 81 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
79 const std::string& debug_name); 82 u32 max_vertices, const std::string& debug_name);
80 83
81 VAddr addr; 84 VAddr addr;
82 Maxwell::ShaderProgram program_type; 85 Maxwell::ShaderProgram program_type;
@@ -104,6 +107,8 @@ private:
104 107
105class ShaderCacheOpenGL final : public RasterizerCache<Shader> { 108class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
106public: 109public:
110 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer);
111
107 /// Gets the current specified shader stage program 112 /// Gets the current specified shader stage program
108 Shader GetStageProgram(Maxwell::ShaderProgram program); 113 Shader GetStageProgram(Maxwell::ShaderProgram program);
109}; 114};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 09b003c59..5fde22ad4 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -494,10 +494,10 @@ public:
494 // instruction for now. 494 // instruction for now.
495 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { 495 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
496 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry 496 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
497 // shader. These instructions use a dirty register as buffer index. To avoid some 497 // shader. These instructions use a dirty register as buffer index, to avoid some
498 // drivers from complaining for the out of boundary writes, guard them. 498 // drivers from complaining about out of boundary writes, guard them.
499 const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " + 499 const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
500 std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'}; 500 std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
501 shader.AddLine("amem[" + buf_index + "][" + 501 shader.AddLine("amem[" + buf_index + "][" +
502 std::to_string(static_cast<u32>(attribute)) + ']' + 502 std::to_string(static_cast<u32>(attribute)) + ']' +
503 GetSwizzle(elem) + " = " + src + ';'); 503 GetSwizzle(elem) + " = " + src + ';');
@@ -811,7 +811,11 @@ private:
811 std::optional<Register> vertex = {}) { 811 std::optional<Register> vertex = {}) {
812 auto GeometryPass = [&](const std::string& name) { 812 auto GeometryPass = [&](const std::string& name) {
813 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) { 813 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
814 return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) + ']'; 814 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games set
815 // an 0x80000000 index for those and the shader fails to build. Find out why this
816 // happens and what's its intent.
817 return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) +
818 " % MAX_VERTEX_INPUT]";
815 } 819 }
816 return name; 820 return name;
817 }; 821 };
@@ -2742,12 +2746,12 @@ private:
2742 } 2746 }
2743 case 3: { 2747 case 3: {
2744 if (is_array) { 2748 if (is_array) {
2745 UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented"); 2749 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2746 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2750 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2747 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2751 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2748 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2752 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2749 texture_type = Tegra::Shader::TextureType::Texture2D; 2753 coord =
2750 is_array = false; 2754 "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");";
2751 } else { 2755 } else {
2752 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2756 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2753 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2757 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -2777,7 +2781,11 @@ private:
2777 break; 2781 break;
2778 } 2782 }
2779 case Tegra::Shader::TextureProcessMode::LZ: { 2783 case Tegra::Shader::TextureProcessMode::LZ: {
2780 texture = "textureLod(" + sampler + ", coords, 0.0)"; 2784 if (depth_compare && is_array) {
2785 texture = "texture(" + sampler + ", coords)";
2786 } else {
2787 texture = "textureLod(" + sampler + ", coords, 0.0)";
2788 }
2781 break; 2789 break;
2782 } 2790 }
2783 case Tegra::Shader::TextureProcessMode::LL: { 2791 case Tegra::Shader::TextureProcessMode::LL: {
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 9d17edd63..eea090e52 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -82,8 +82,8 @@ void main() {
82} 82}
83 83
84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { 84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
85 std::string out = "#version 430 core\n"; 85 // Version is intentionally skipped in shader generation, it's added by the lazy compilation.
86 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 86 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
87 out += Decompiler::GetCommonDeclarations(); 87 out += Decompiler::GetCommonDeclarations();
88 out += "bool exec_geometry();\n"; 88 out += "bool exec_geometry();\n";
89 89
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 36fe1f04c..2a069cdd8 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -7,6 +7,7 @@
7#include <glad/glad.h> 7#include <glad/glad.h>
8 8
9#include "video_core/renderer_opengl/gl_resource_manager.h" 9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/renderer_opengl/gl_state.h"
10#include "video_core/renderer_opengl/maxwell_to_gl.h" 11#include "video_core/renderer_opengl/maxwell_to_gl.h"
11 12
12namespace OpenGL::GLShader { 13namespace OpenGL::GLShader {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index b6b426f34..98622a058 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -22,17 +22,15 @@ OpenGLState::OpenGLState() {
22 depth.test_enabled = false; 22 depth.test_enabled = false;
23 depth.test_func = GL_LESS; 23 depth.test_func = GL_LESS;
24 depth.write_mask = GL_TRUE; 24 depth.write_mask = GL_TRUE;
25 depth.depth_range_near = 0.0f;
26 depth.depth_range_far = 1.0f;
27 25
28 primitive_restart.enabled = false; 26 primitive_restart.enabled = false;
29 primitive_restart.index = 0; 27 primitive_restart.index = 0;
30 28 for (auto& item : color_mask) {
31 color_mask.red_enabled = GL_TRUE; 29 item.red_enabled = GL_TRUE;
32 color_mask.green_enabled = GL_TRUE; 30 item.green_enabled = GL_TRUE;
33 color_mask.blue_enabled = GL_TRUE; 31 item.blue_enabled = GL_TRUE;
34 color_mask.alpha_enabled = GL_TRUE; 32 item.alpha_enabled = GL_TRUE;
35 33 }
36 stencil.test_enabled = false; 34 stencil.test_enabled = false;
37 auto reset_stencil = [](auto& config) { 35 auto reset_stencil = [](auto& config) {
38 config.test_func = GL_ALWAYS; 36 config.test_func = GL_ALWAYS;
@@ -45,19 +43,33 @@ OpenGLState::OpenGLState() {
45 }; 43 };
46 reset_stencil(stencil.front); 44 reset_stencil(stencil.front);
47 reset_stencil(stencil.back); 45 reset_stencil(stencil.back);
48 46 for (auto& item : viewports) {
49 blend.enabled = true; 47 item.x = 0;
50 blend.rgb_equation = GL_FUNC_ADD; 48 item.y = 0;
51 blend.a_equation = GL_FUNC_ADD; 49 item.width = 0;
52 blend.src_rgb_func = GL_ONE; 50 item.height = 0;
53 blend.dst_rgb_func = GL_ZERO; 51 item.depth_range_near = 0.0f;
54 blend.src_a_func = GL_ONE; 52 item.depth_range_far = 1.0f;
55 blend.dst_a_func = GL_ZERO; 53 }
56 blend.color.red = 0.0f; 54 scissor.enabled = false;
57 blend.color.green = 0.0f; 55 scissor.x = 0;
58 blend.color.blue = 0.0f; 56 scissor.y = 0;
59 blend.color.alpha = 0.0f; 57 scissor.width = 0;
60 58 scissor.height = 0;
59 for (auto& item : blend) {
60 item.enabled = true;
61 item.rgb_equation = GL_FUNC_ADD;
62 item.a_equation = GL_FUNC_ADD;
63 item.src_rgb_func = GL_ONE;
64 item.dst_rgb_func = GL_ZERO;
65 item.src_a_func = GL_ONE;
66 item.dst_a_func = GL_ZERO;
67 }
68 independant_blend.enabled = false;
69 blend_color.red = 0.0f;
70 blend_color.green = 0.0f;
71 blend_color.blue = 0.0f;
72 blend_color.alpha = 0.0f;
61 logic_op.enabled = false; 73 logic_op.enabled = false;
62 logic_op.operation = GL_COPY; 74 logic_op.operation = GL_COPY;
63 75
@@ -73,17 +85,6 @@ OpenGLState::OpenGLState() {
73 draw.shader_program = 0; 85 draw.shader_program = 0;
74 draw.program_pipeline = 0; 86 draw.program_pipeline = 0;
75 87
76 scissor.enabled = false;
77 scissor.x = 0;
78 scissor.y = 0;
79 scissor.width = 0;
80 scissor.height = 0;
81
82 viewport.x = 0;
83 viewport.y = 0;
84 viewport.width = 0;
85 viewport.height = 0;
86
87 clip_distance = {}; 88 clip_distance = {};
88 89
89 point.size = 1; 90 point.size = 1;
@@ -134,6 +135,32 @@ void OpenGLState::ApplyCulling() const {
134 } 135 }
135} 136}
136 137
138void OpenGLState::ApplyColorMask() const {
139 if (GLAD_GL_ARB_viewport_array) {
140 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
141 const auto& updated = color_mask[i];
142 const auto& current = cur_state.color_mask[i];
143 if (updated.red_enabled != current.red_enabled ||
144 updated.green_enabled != current.green_enabled ||
145 updated.blue_enabled != current.blue_enabled ||
146 updated.alpha_enabled != current.alpha_enabled) {
147 glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled,
148 updated.blue_enabled, updated.alpha_enabled);
149 }
150 }
151 } else {
152 const auto& updated = color_mask[0];
153 const auto& current = cur_state.color_mask[0];
154 if (updated.red_enabled != current.red_enabled ||
155 updated.green_enabled != current.green_enabled ||
156 updated.blue_enabled != current.blue_enabled ||
157 updated.alpha_enabled != current.alpha_enabled) {
158 glColorMask(updated.red_enabled, updated.green_enabled, updated.blue_enabled,
159 updated.alpha_enabled);
160 }
161 }
162}
163
137void OpenGLState::ApplyDepth() const { 164void OpenGLState::ApplyDepth() const {
138 // Depth test 165 // Depth test
139 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled; 166 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
@@ -152,11 +179,6 @@ void OpenGLState::ApplyDepth() const {
152 if (depth.write_mask != cur_state.depth.write_mask) { 179 if (depth.write_mask != cur_state.depth.write_mask) {
153 glDepthMask(depth.write_mask); 180 glDepthMask(depth.write_mask);
154 } 181 }
155 // Depth range
156 if (depth.depth_range_near != cur_state.depth.depth_range_near ||
157 depth.depth_range_far != cur_state.depth.depth_range_far) {
158 glDepthRange(depth.depth_range_near, depth.depth_range_far);
159 }
160} 182}
161 183
162void OpenGLState::ApplyPrimitiveRestart() const { 184void OpenGLState::ApplyPrimitiveRestart() const {
@@ -208,7 +230,7 @@ void OpenGLState::ApplyStencilTest() const {
208 } 230 }
209} 231}
210 232
211void OpenGLState::ApplyScissorTest() const { 233void OpenGLState::ApplyScissor() const {
212 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled; 234 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
213 if (scissor_changed) { 235 if (scissor_changed) {
214 if (scissor.enabled) { 236 if (scissor.enabled) {
@@ -217,51 +239,142 @@ void OpenGLState::ApplyScissorTest() const {
217 glDisable(GL_SCISSOR_TEST); 239 glDisable(GL_SCISSOR_TEST);
218 } 240 }
219 } 241 }
220 if (scissor_changed || scissor_changed || scissor.x != cur_state.scissor.x || 242 if (scissor.enabled &&
221 scissor.y != cur_state.scissor.y || scissor.width != cur_state.scissor.width || 243 (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
222 scissor.height != cur_state.scissor.height) { 244 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
223 glScissor(scissor.x, scissor.y, scissor.width, scissor.height); 245 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
224 } 246 }
225} 247}
226 248
227void OpenGLState::ApplyBlending() const { 249void OpenGLState::ApplyViewport() const {
228 const bool blend_changed = blend.enabled != cur_state.blend.enabled; 250 if (GLAD_GL_ARB_viewport_array) {
251 for (GLuint i = 0;
252 i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) {
253 const auto& current = cur_state.viewports[i];
254 const auto& updated = viewports[i];
255 if (updated.x != current.x || updated.y != current.y ||
256 updated.width != current.width || updated.height != current.height) {
257 glViewportIndexedf(i, updated.x, updated.y, updated.width, updated.height);
258 }
259 if (updated.depth_range_near != current.depth_range_near ||
260 updated.depth_range_far != current.depth_range_far) {
261 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
262 }
263 }
264 } else {
265 const auto& current = cur_state.viewports[0];
266 const auto& updated = viewports[0];
267 if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
268 updated.height != current.height) {
269 glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y),
270 static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
271 }
272 if (updated.depth_range_near != current.depth_range_near ||
273 updated.depth_range_far != current.depth_range_far) {
274 glDepthRange(updated.depth_range_near, updated.depth_range_far);
275 }
276 }
277}
278
279void OpenGLState::ApplyGlobalBlending() const {
280 const Blend& current = cur_state.blend[0];
281 const Blend& updated = blend[0];
282 const bool blend_changed = updated.enabled != current.enabled;
229 if (blend_changed) { 283 if (blend_changed) {
230 if (blend.enabled) { 284 if (updated.enabled) {
231 ASSERT(!logic_op.enabled);
232 glEnable(GL_BLEND); 285 glEnable(GL_BLEND);
233 } else { 286 } else {
234 glDisable(GL_BLEND); 287 glDisable(GL_BLEND);
235 } 288 }
236 } 289 }
237 if (blend.enabled) { 290 if (!updated.enabled) {
238 if (blend_changed || blend.color.red != cur_state.blend.color.red || 291 return;
239 blend.color.green != cur_state.blend.color.green || 292 }
240 blend.color.blue != cur_state.blend.color.blue || 293 if (updated.separate_alpha) {
241 blend.color.alpha != cur_state.blend.color.alpha) { 294 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
242 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); 295 updated.dst_rgb_func != current.dst_rgb_func ||
296 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
297 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
298 updated.dst_a_func);
243 } 299 }
244 300
245 if (blend_changed || blend.src_rgb_func != cur_state.blend.src_rgb_func || 301 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
246 blend.dst_rgb_func != cur_state.blend.dst_rgb_func || 302 updated.a_equation != current.a_equation) {
247 blend.src_a_func != cur_state.blend.src_a_func || 303 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
248 blend.dst_a_func != cur_state.blend.dst_a_func) { 304 }
249 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, 305 } else {
250 blend.dst_a_func); 306 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
307 updated.dst_rgb_func != current.dst_rgb_func) {
308 glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
251 } 309 }
252 310
253 if (blend_changed || blend.rgb_equation != cur_state.blend.rgb_equation || 311 if (blend_changed || updated.rgb_equation != current.rgb_equation) {
254 blend.a_equation != cur_state.blend.a_equation) { 312 glBlendEquation(updated.rgb_equation);
255 glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
256 } 313 }
257 } 314 }
258} 315}
259 316
317void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
318 const Blend& updated = blend[target];
319 const Blend& current = cur_state.blend[target];
320 const bool blend_changed = updated.enabled != current.enabled || force;
321 if (blend_changed) {
322 if (updated.enabled) {
323 glEnablei(GL_BLEND, static_cast<GLuint>(target));
324 } else {
325 glDisablei(GL_BLEND, static_cast<GLuint>(target));
326 }
327 }
328 if (!updated.enabled) {
329 return;
330 }
331 if (updated.separate_alpha) {
332 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
333 updated.dst_rgb_func != current.dst_rgb_func ||
334 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
335 glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
336 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
337 }
338
339 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
340 updated.a_equation != current.a_equation) {
341 glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
342 updated.a_equation);
343 }
344 } else {
345 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
346 updated.dst_rgb_func != current.dst_rgb_func) {
347 glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
348 updated.dst_rgb_func);
349 }
350
351 if (blend_changed || updated.rgb_equation != current.rgb_equation) {
352 glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation);
353 }
354 }
355}
356
357void OpenGLState::ApplyBlending() const {
358 if (independant_blend.enabled) {
359 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
360 ApplyTargetBlending(i,
361 independant_blend.enabled != cur_state.independant_blend.enabled);
362 }
363 } else {
364 ApplyGlobalBlending();
365 }
366 if (blend_color.red != cur_state.blend_color.red ||
367 blend_color.green != cur_state.blend_color.green ||
368 blend_color.blue != cur_state.blend_color.blue ||
369 blend_color.alpha != cur_state.blend_color.alpha) {
370 glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha);
371 }
372}
373
260void OpenGLState::ApplyLogicOp() const { 374void OpenGLState::ApplyLogicOp() const {
261 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled; 375 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled;
262 if (logic_op_changed) { 376 if (logic_op_changed) {
263 if (logic_op.enabled) { 377 if (logic_op.enabled) {
264 ASSERT(!blend.enabled);
265 glEnable(GL_COLOR_LOGIC_OP); 378 glEnable(GL_COLOR_LOGIC_OP);
266 } else { 379 } else {
267 glDisable(GL_COLOR_LOGIC_OP); 380 glDisable(GL_COLOR_LOGIC_OP);
@@ -315,7 +428,7 @@ void OpenGLState::ApplySamplers() const {
315 } 428 }
316} 429}
317 430
318void OpenGLState::Apply() const { 431void OpenGLState::ApplyFramebufferState() const {
319 // Framebuffer 432 // Framebuffer
320 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 433 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
321 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 434 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -323,7 +436,9 @@ void OpenGLState::Apply() const {
323 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) { 436 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
324 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); 437 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
325 } 438 }
439}
326 440
441void OpenGLState::ApplyVertexBufferState() const {
327 // Vertex array 442 // Vertex array
328 if (draw.vertex_array != cur_state.draw.vertex_array) { 443 if (draw.vertex_array != cur_state.draw.vertex_array) {
329 glBindVertexArray(draw.vertex_array); 444 glBindVertexArray(draw.vertex_array);
@@ -333,7 +448,11 @@ void OpenGLState::Apply() const {
333 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) { 448 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
334 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer); 449 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
335 } 450 }
451}
336 452
453void OpenGLState::Apply() const {
454 ApplyFramebufferState();
455 ApplyVertexBufferState();
337 // Uniform buffer 456 // Uniform buffer
338 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) { 457 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
339 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer); 458 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
@@ -348,12 +467,6 @@ void OpenGLState::Apply() const {
348 if (draw.program_pipeline != cur_state.draw.program_pipeline) { 467 if (draw.program_pipeline != cur_state.draw.program_pipeline) {
349 glBindProgramPipeline(draw.program_pipeline); 468 glBindProgramPipeline(draw.program_pipeline);
350 } 469 }
351 // Viewport
352 if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
353 viewport.width != cur_state.viewport.width ||
354 viewport.height != cur_state.viewport.height) {
355 glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
356 }
357 // Clip distance 470 // Clip distance
358 for (std::size_t i = 0; i < clip_distance.size(); ++i) { 471 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
359 if (clip_distance[i] != cur_state.clip_distance[i]) { 472 if (clip_distance[i] != cur_state.clip_distance[i]) {
@@ -364,19 +477,13 @@ void OpenGLState::Apply() const {
364 } 477 }
365 } 478 }
366 } 479 }
367 // Color mask
368 if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
369 color_mask.green_enabled != cur_state.color_mask.green_enabled ||
370 color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
371 color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
372 glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
373 color_mask.alpha_enabled);
374 }
375 // Point 480 // Point
376 if (point.size != cur_state.point.size) { 481 if (point.size != cur_state.point.size) {
377 glPointSize(point.size); 482 glPointSize(point.size);
378 } 483 }
379 ApplyScissorTest(); 484 ApplyColorMask();
485 ApplyViewport();
486 ApplyScissor();
380 ApplyStencilTest(); 487 ApplyStencilTest();
381 ApplySRgb(); 488 ApplySRgb();
382 ApplyCulling(); 489 ApplyCulling();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index fe648aff6..e5d1baae6 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -46,11 +46,9 @@ public:
46 } cull; 46 } cull;
47 47
48 struct { 48 struct {
49 bool test_enabled; // GL_DEPTH_TEST 49 bool test_enabled; // GL_DEPTH_TEST
50 GLenum test_func; // GL_DEPTH_FUNC 50 GLenum test_func; // GL_DEPTH_FUNC
51 GLboolean write_mask; // GL_DEPTH_WRITEMASK 51 GLboolean write_mask; // GL_DEPTH_WRITEMASK
52 GLfloat depth_range_near; // GL_DEPTH_RANGE
53 GLfloat depth_range_far; // GL_DEPTH_RANGE
54 } depth; 52 } depth;
55 53
56 struct { 54 struct {
@@ -58,13 +56,14 @@ public:
58 GLuint index; 56 GLuint index;
59 } primitive_restart; // GL_PRIMITIVE_RESTART 57 } primitive_restart; // GL_PRIMITIVE_RESTART
60 58
61 struct { 59 struct ColorMask {
62 GLboolean red_enabled; 60 GLboolean red_enabled;
63 GLboolean green_enabled; 61 GLboolean green_enabled;
64 GLboolean blue_enabled; 62 GLboolean blue_enabled;
65 GLboolean alpha_enabled; 63 GLboolean alpha_enabled;
66 } color_mask; // GL_COLOR_WRITEMASK 64 };
67 65 std::array<ColorMask, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets>
66 color_mask; // GL_COLOR_WRITEMASK
68 struct { 67 struct {
69 bool test_enabled; // GL_STENCIL_TEST 68 bool test_enabled; // GL_STENCIL_TEST
70 struct { 69 struct {
@@ -78,22 +77,28 @@ public:
78 } front, back; 77 } front, back;
79 } stencil; 78 } stencil;
80 79
81 struct { 80 struct Blend {
82 bool enabled; // GL_BLEND 81 bool enabled; // GL_BLEND
82 bool separate_alpha; // Independent blend enabled
83 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB 83 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
84 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA 84 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
85 GLenum src_rgb_func; // GL_BLEND_SRC_RGB 85 GLenum src_rgb_func; // GL_BLEND_SRC_RGB
86 GLenum dst_rgb_func; // GL_BLEND_DST_RGB 86 GLenum dst_rgb_func; // GL_BLEND_DST_RGB
87 GLenum src_a_func; // GL_BLEND_SRC_ALPHA 87 GLenum src_a_func; // GL_BLEND_SRC_ALPHA
88 GLenum dst_a_func; // GL_BLEND_DST_ALPHA 88 GLenum dst_a_func; // GL_BLEND_DST_ALPHA
89 };
90 std::array<Blend, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> blend;
89 91
90 struct { 92 struct {
91 GLclampf red; 93 bool enabled;
92 GLclampf green; 94 } independant_blend;
93 GLclampf blue; 95
94 GLclampf alpha; 96 struct {
95 } color; // GL_BLEND_COLOR 97 GLclampf red;
96 } blend; 98 GLclampf green;
99 GLclampf blue;
100 GLclampf alpha;
101 } blend_color; // GL_BLEND_COLOR
97 102
98 struct { 103 struct {
99 bool enabled; // GL_LOGIC_OP_MODE 104 bool enabled; // GL_LOGIC_OP_MODE
@@ -138,6 +143,16 @@ public:
138 GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING 143 GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
139 } draw; 144 } draw;
140 145
146 struct viewport {
147 GLfloat x;
148 GLfloat y;
149 GLfloat width;
150 GLfloat height;
151 GLfloat depth_range_near; // GL_DEPTH_RANGE
152 GLfloat depth_range_far; // GL_DEPTH_RANGE
153 };
154 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports;
155
141 struct { 156 struct {
142 bool enabled; // GL_SCISSOR_TEST 157 bool enabled; // GL_SCISSOR_TEST
143 GLint x; 158 GLint x;
@@ -147,13 +162,6 @@ public:
147 } scissor; 162 } scissor;
148 163
149 struct { 164 struct {
150 GLint x;
151 GLint y;
152 GLsizei width;
153 GLsizei height;
154 } viewport;
155
156 struct {
157 float size; // GL_POINT_SIZE 165 float size; // GL_POINT_SIZE
158 } point; 166 } point;
159 167
@@ -173,6 +181,10 @@ public:
173 } 181 }
174 /// Apply this state as the current OpenGL state 182 /// Apply this state as the current OpenGL state
175 void Apply() const; 183 void Apply() const;
184 /// Apply only the state afecting the framebuffer
185 void ApplyFramebufferState() const;
186 /// Apply only the state afecting the vertex buffer
187 void ApplyVertexBufferState() const;
176 /// Set the initial OpenGL state 188 /// Set the initial OpenGL state
177 static void ApplyDefaultState(); 189 static void ApplyDefaultState();
178 /// Resets any references to the given resource 190 /// Resets any references to the given resource
@@ -191,14 +203,18 @@ private:
191 static bool s_rgb_used; 203 static bool s_rgb_used;
192 void ApplySRgb() const; 204 void ApplySRgb() const;
193 void ApplyCulling() const; 205 void ApplyCulling() const;
206 void ApplyColorMask() const;
194 void ApplyDepth() const; 207 void ApplyDepth() const;
195 void ApplyPrimitiveRestart() const; 208 void ApplyPrimitiveRestart() const;
196 void ApplyStencilTest() const; 209 void ApplyStencilTest() const;
197 void ApplyScissorTest() const; 210 void ApplyViewport() const;
211 void ApplyTargetBlending(std::size_t target, bool force) const;
212 void ApplyGlobalBlending() const;
198 void ApplyBlending() const; 213 void ApplyBlending() const;
199 void ApplyLogicOp() const; 214 void ApplyLogicOp() const;
200 void ApplyTextures() const; 215 void ApplyTextures() const;
201 void ApplySamplers() const; 216 void ApplySamplers() const;
217 void ApplyScissor() const;
202}; 218};
203 219
204} // namespace OpenGL 220} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index e409228cc..b97b895a4 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -6,9 +6,13 @@
6#include <vector> 6#include <vector>
7#include "common/alignment.h" 7#include "common/alignment.h"
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/microprofile.h"
9#include "video_core/renderer_opengl/gl_state.h" 10#include "video_core/renderer_opengl/gl_state.h"
10#include "video_core/renderer_opengl/gl_stream_buffer.h" 11#include "video_core/renderer_opengl/gl_stream_buffer.h"
11 12
13MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
14 MP_RGB(128, 128, 192));
15
12namespace OpenGL { 16namespace OpenGL {
13 17
14OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent) 18OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent)
@@ -75,6 +79,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
75 } 79 }
76 80
77 if (invalidate || !persistent) { 81 if (invalidate || !persistent) {
82 MICROPROFILE_SCOPE(OpenGL_StreamBuffer);
78 GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | 83 GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
79 (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | 84 (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
80 (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); 85 (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 87d511c38..3ce2cc6d2 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -159,10 +159,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
159 } 159 }
160 } 160 }
161 } 161 }
162 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", 162 LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode));
163 static_cast<u32>(filter_mode)); 163 return GL_LINEAR;
164 UNREACHABLE();
165 return {};
166} 164}
167 165
168inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { 166inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
@@ -183,9 +181,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
183 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
184 return GL_MIRROR_CLAMP_TO_EDGE; 182 return GL_MIRROR_CLAMP_TO_EDGE;
185 } 183 }
186 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 184 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
187 UNREACHABLE(); 185 return GL_REPEAT;
188 return {};
189} 186}
190 187
191inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { 188inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
@@ -207,10 +204,9 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
207 case Tegra::Texture::DepthCompareFunc::Always: 204 case Tegra::Texture::DepthCompareFunc::Always:
208 return GL_ALWAYS; 205 return GL_ALWAYS;
209 } 206 }
210 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}", 207 LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}",
211 static_cast<u32>(func)); 208 static_cast<u32>(func));
212 UNREACHABLE(); 209 return GL_GREATER;
213 return {};
214} 210}
215 211
216inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 212inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
@@ -226,9 +222,8 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
226 case Maxwell::Blend::Equation::Max: 222 case Maxwell::Blend::Equation::Max:
227 return GL_MAX; 223 return GL_MAX;
228 } 224 }
229 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); 225 LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
230 UNREACHABLE(); 226 return GL_FUNC_ADD;
231 return {};
232} 227}
233 228
234inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { 229inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
@@ -291,9 +286,8 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
291 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 286 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
292 return GL_ONE_MINUS_CONSTANT_ALPHA; 287 return GL_ONE_MINUS_CONSTANT_ALPHA;
293 } 288 }
294 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); 289 LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
295 UNREACHABLE(); 290 return GL_ZERO;
296 return {};
297} 291}
298 292
299inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { 293inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
@@ -312,9 +306,8 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
312 case Tegra::Texture::SwizzleSource::OneFloat: 306 case Tegra::Texture::SwizzleSource::OneFloat:
313 return GL_ONE; 307 return GL_ONE;
314 } 308 }
315 LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); 309 LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
316 UNREACHABLE(); 310 return GL_ZERO;
317 return {};
318} 311}
319 312
320inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { 313inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
@@ -344,33 +337,39 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
344 case Maxwell::ComparisonOp::AlwaysOld: 337 case Maxwell::ComparisonOp::AlwaysOld:
345 return GL_ALWAYS; 338 return GL_ALWAYS;
346 } 339 }
347 LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); 340 LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
348 UNREACHABLE(); 341 return GL_ALWAYS;
349 return {};
350} 342}
351 343
352inline GLenum StencilOp(Maxwell::StencilOp stencil) { 344inline GLenum StencilOp(Maxwell::StencilOp stencil) {
353 switch (stencil) { 345 switch (stencil) {
354 case Maxwell::StencilOp::Keep: 346 case Maxwell::StencilOp::Keep:
347 case Maxwell::StencilOp::KeepOGL:
355 return GL_KEEP; 348 return GL_KEEP;
356 case Maxwell::StencilOp::Zero: 349 case Maxwell::StencilOp::Zero:
350 case Maxwell::StencilOp::ZeroOGL:
357 return GL_ZERO; 351 return GL_ZERO;
358 case Maxwell::StencilOp::Replace: 352 case Maxwell::StencilOp::Replace:
353 case Maxwell::StencilOp::ReplaceOGL:
359 return GL_REPLACE; 354 return GL_REPLACE;
360 case Maxwell::StencilOp::Incr: 355 case Maxwell::StencilOp::Incr:
356 case Maxwell::StencilOp::IncrOGL:
361 return GL_INCR; 357 return GL_INCR;
362 case Maxwell::StencilOp::Decr: 358 case Maxwell::StencilOp::Decr:
359 case Maxwell::StencilOp::DecrOGL:
363 return GL_DECR; 360 return GL_DECR;
364 case Maxwell::StencilOp::Invert: 361 case Maxwell::StencilOp::Invert:
362 case Maxwell::StencilOp::InvertOGL:
365 return GL_INVERT; 363 return GL_INVERT;
366 case Maxwell::StencilOp::IncrWrap: 364 case Maxwell::StencilOp::IncrWrap:
365 case Maxwell::StencilOp::IncrWrapOGL:
367 return GL_INCR_WRAP; 366 return GL_INCR_WRAP;
368 case Maxwell::StencilOp::DecrWrap: 367 case Maxwell::StencilOp::DecrWrap:
368 case Maxwell::StencilOp::DecrWrapOGL:
369 return GL_DECR_WRAP; 369 return GL_DECR_WRAP;
370 } 370 }
371 LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); 371 LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
372 UNREACHABLE(); 372 return GL_KEEP;
373 return {};
374} 373}
375 374
376inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { 375inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
@@ -380,9 +379,8 @@ inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
380 case Maxwell::Cull::FrontFace::CounterClockWise: 379 case Maxwell::Cull::FrontFace::CounterClockWise:
381 return GL_CCW; 380 return GL_CCW;
382 } 381 }
383 LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); 382 LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
384 UNREACHABLE(); 383 return GL_CCW;
385 return {};
386} 384}
387 385
388inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { 386inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
@@ -394,9 +392,8 @@ inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
394 case Maxwell::Cull::CullFace::FrontAndBack: 392 case Maxwell::Cull::CullFace::FrontAndBack:
395 return GL_FRONT_AND_BACK; 393 return GL_FRONT_AND_BACK;
396 } 394 }
397 LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); 395 LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
398 UNREACHABLE(); 396 return GL_BACK;
399 return {};
400} 397}
401 398
402inline GLenum LogicOp(Maxwell::LogicOperation operation) { 399inline GLenum LogicOp(Maxwell::LogicOperation operation) {
@@ -434,9 +431,8 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
434 case Maxwell::LogicOperation::Set: 431 case Maxwell::LogicOperation::Set:
435 return GL_SET; 432 return GL_SET;
436 } 433 }
437 LOG_CRITICAL(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); 434 LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
438 UNREACHABLE(); 435 return GL_COPY;
439 return {};
440} 436}
441 437
442} // namespace MaxwellToGL 438} // namespace MaxwellToGL
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index d9a97e30b..9582dd2ca 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -19,6 +19,8 @@ SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_t
19 return SurfaceTarget::Texture3D; 19 return SurfaceTarget::Texture3D;
20 case Tegra::Texture::TextureType::TextureCubemap: 20 case Tegra::Texture::TextureType::TextureCubemap:
21 return SurfaceTarget::TextureCubemap; 21 return SurfaceTarget::TextureCubemap;
22 case Tegra::Texture::TextureType::TextureCubeArray:
23 return SurfaceTarget::TextureCubeArray;
22 case Tegra::Texture::TextureType::Texture1DArray: 24 case Tegra::Texture::TextureType::Texture1DArray:
23 return SurfaceTarget::Texture1DArray; 25 return SurfaceTarget::Texture1DArray;
24 case Tegra::Texture::TextureType::Texture2DArray: 26 case Tegra::Texture::TextureType::Texture2DArray:
@@ -39,6 +41,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
39 case SurfaceTarget::Texture1DArray: 41 case SurfaceTarget::Texture1DArray:
40 case SurfaceTarget::Texture2DArray: 42 case SurfaceTarget::Texture2DArray:
41 case SurfaceTarget::TextureCubemap: 43 case SurfaceTarget::TextureCubemap:
44 case SurfaceTarget::TextureCubeArray:
42 return true; 45 return true;
43 default: 46 default:
44 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); 47 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
@@ -297,10 +300,14 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
297 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4; 300 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
298 case Tegra::Texture::TextureFormat::ASTC_2D_5X4: 301 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
299 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4; 302 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
303 case Tegra::Texture::TextureFormat::ASTC_2D_5X5:
304 return is_srgb ? PixelFormat::ASTC_2D_5X5_SRGB : PixelFormat::ASTC_2D_5X5;
300 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 305 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
301 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; 306 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
302 case Tegra::Texture::TextureFormat::ASTC_2D_8X5: 307 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
303 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5; 308 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
309 case Tegra::Texture::TextureFormat::ASTC_2D_10X8:
310 return is_srgb ? PixelFormat::ASTC_2D_10X8_SRGB : PixelFormat::ASTC_2D_10X8;
304 case Tegra::Texture::TextureFormat::R16_G16: 311 case Tegra::Texture::TextureFormat::R16_G16:
305 switch (component_type) { 312 switch (component_type) {
306 case Tegra::Texture::ComponentType::FLOAT: 313 case Tegra::Texture::ComponentType::FLOAT:
@@ -440,12 +447,16 @@ bool IsPixelFormatASTC(PixelFormat format) {
440 switch (format) { 447 switch (format) {
441 case PixelFormat::ASTC_2D_4X4: 448 case PixelFormat::ASTC_2D_4X4:
442 case PixelFormat::ASTC_2D_5X4: 449 case PixelFormat::ASTC_2D_5X4:
450 case PixelFormat::ASTC_2D_5X5:
443 case PixelFormat::ASTC_2D_8X8: 451 case PixelFormat::ASTC_2D_8X8:
444 case PixelFormat::ASTC_2D_8X5: 452 case PixelFormat::ASTC_2D_8X5:
445 case PixelFormat::ASTC_2D_4X4_SRGB: 453 case PixelFormat::ASTC_2D_4X4_SRGB:
446 case PixelFormat::ASTC_2D_5X4_SRGB: 454 case PixelFormat::ASTC_2D_5X4_SRGB:
455 case PixelFormat::ASTC_2D_5X5_SRGB:
447 case PixelFormat::ASTC_2D_8X8_SRGB: 456 case PixelFormat::ASTC_2D_8X8_SRGB:
448 case PixelFormat::ASTC_2D_8X5_SRGB: 457 case PixelFormat::ASTC_2D_8X5_SRGB:
458 case PixelFormat::ASTC_2D_10X8:
459 case PixelFormat::ASTC_2D_10X8_SRGB:
449 return true; 460 return true;
450 default: 461 default:
451 return false; 462 return false;
@@ -453,27 +464,7 @@ bool IsPixelFormatASTC(PixelFormat format) {
453} 464}
454 465
455std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { 466std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
456 switch (format) { 467 return {GetDefaultBlockWidth(format), GetDefaultBlockHeight(format)};
457 case PixelFormat::ASTC_2D_4X4:
458 return {4, 4};
459 case PixelFormat::ASTC_2D_5X4:
460 return {5, 4};
461 case PixelFormat::ASTC_2D_8X8:
462 return {8, 8};
463 case PixelFormat::ASTC_2D_8X5:
464 return {8, 5};
465 case PixelFormat::ASTC_2D_4X4_SRGB:
466 return {4, 4};
467 case PixelFormat::ASTC_2D_5X4_SRGB:
468 return {5, 4};
469 case PixelFormat::ASTC_2D_8X8_SRGB:
470 return {8, 8};
471 case PixelFormat::ASTC_2D_8X5_SRGB:
472 return {8, 5};
473 default:
474 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
475 UNREACHABLE();
476 }
477} 468}
478 469
479bool IsFormatBCn(PixelFormat format) { 470bool IsFormatBCn(PixelFormat format) {
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 3232e437f..0dd3eb2e4 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -72,19 +72,23 @@ enum class PixelFormat {
72 ASTC_2D_8X8_SRGB = 54, 72 ASTC_2D_8X8_SRGB = 54,
73 ASTC_2D_8X5_SRGB = 55, 73 ASTC_2D_8X5_SRGB = 55,
74 ASTC_2D_5X4_SRGB = 56, 74 ASTC_2D_5X4_SRGB = 56,
75 ASTC_2D_5X5 = 57,
76 ASTC_2D_5X5_SRGB = 58,
77 ASTC_2D_10X8 = 59,
78 ASTC_2D_10X8_SRGB = 60,
75 79
76 MaxColorFormat, 80 MaxColorFormat,
77 81
78 // Depth formats 82 // Depth formats
79 Z32F = 57, 83 Z32F = 61,
80 Z16 = 58, 84 Z16 = 62,
81 85
82 MaxDepthFormat, 86 MaxDepthFormat,
83 87
84 // DepthStencil formats 88 // DepthStencil formats
85 Z24S8 = 59, 89 Z24S8 = 63,
86 S8Z24 = 60, 90 S8Z24 = 64,
87 Z32FS8 = 61, 91 Z32FS8 = 65,
88 92
89 MaxDepthStencilFormat, 93 MaxDepthStencilFormat,
90 94
@@ -118,6 +122,7 @@ enum class SurfaceTarget {
118 Texture1DArray, 122 Texture1DArray,
119 Texture2DArray, 123 Texture2DArray,
120 TextureCubemap, 124 TextureCubemap,
125 TextureCubeArray,
121}; 126};
122 127
123/** 128/**
@@ -188,6 +193,10 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
188 4, // ASTC_2D_8X8_SRGB 193 4, // ASTC_2D_8X8_SRGB
189 4, // ASTC_2D_8X5_SRGB 194 4, // ASTC_2D_8X5_SRGB
190 4, // ASTC_2D_5X4_SRGB 195 4, // ASTC_2D_5X4_SRGB
196 4, // ASTC_2D_5X5
197 4, // ASTC_2D_5X5_SRGB
198 4, // ASTC_2D_10X8
199 4, // ASTC_2D_10X8_SRGB
191 1, // Z32F 200 1, // Z32F
192 1, // Z16 201 1, // Z16
193 1, // Z24S8 202 1, // Z24S8
@@ -199,6 +208,81 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
199 return compression_factor_table[static_cast<std::size_t>(format)]; 208 return compression_factor_table[static_cast<std::size_t>(format)];
200} 209}
201 210
211static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
212 if (format == PixelFormat::Invalid)
213 return 0;
214 constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
215 1, // ABGR8U
216 1, // ABGR8S
217 1, // ABGR8UI
218 1, // B5G6R5U
219 1, // A2B10G10R10U
220 1, // A1B5G5R5U
221 1, // R8U
222 1, // R8UI
223 1, // RGBA16F
224 1, // RGBA16U
225 1, // RGBA16UI
226 1, // R11FG11FB10F
227 1, // RGBA32UI
228 4, // DXT1
229 4, // DXT23
230 4, // DXT45
231 4, // DXN1
232 4, // DXN2UNORM
233 4, // DXN2SNORM
234 4, // BC7U
235 4, // BC6H_UF16
236 4, // BC6H_SF16
237 4, // ASTC_2D_4X4
238 1, // G8R8U
239 1, // G8R8S
240 1, // BGRA8
241 1, // RGBA32F
242 1, // RG32F
243 1, // R32F
244 1, // R16F
245 1, // R16U
246 1, // R16S
247 1, // R16UI
248 1, // R16I
249 1, // RG16
250 1, // RG16F
251 1, // RG16UI
252 1, // RG16I
253 1, // RG16S
254 1, // RGB32F
255 1, // RGBA8_SRGB
256 1, // RG8U
257 1, // RG8S
258 1, // RG32UI
259 1, // R32UI
260 8, // ASTC_2D_8X8
261 8, // ASTC_2D_8X5
262 5, // ASTC_2D_5X4
263 1, // BGRA8_SRGB
264 4, // DXT1_SRGB
265 4, // DXT23_SRGB
266 4, // DXT45_SRGB
267 4, // BC7U_SRGB
268 4, // ASTC_2D_4X4_SRGB
269 8, // ASTC_2D_8X8_SRGB
270 8, // ASTC_2D_8X5_SRGB
271 5, // ASTC_2D_5X4_SRGB
272 5, // ASTC_2D_5X5
273 5, // ASTC_2D_5X5_SRGB
274 10, // ASTC_2D_10X8
275 10, // ASTC_2D_10X8_SRGB
276 1, // Z32F
277 1, // Z16
278 1, // Z24S8
279 1, // S8Z24
280 1, // Z32FS8
281 }};
282 ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
283 return block_width_table[static_cast<std::size_t>(format)];
284}
285
202static constexpr u32 GetDefaultBlockHeight(PixelFormat format) { 286static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
203 if (format == PixelFormat::Invalid) 287 if (format == PixelFormat::Invalid)
204 return 0; 288 return 0;
@@ -261,6 +345,10 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
261 8, // ASTC_2D_8X8_SRGB 345 8, // ASTC_2D_8X8_SRGB
262 5, // ASTC_2D_8X5_SRGB 346 5, // ASTC_2D_8X5_SRGB
263 4, // ASTC_2D_5X4_SRGB 347 4, // ASTC_2D_5X4_SRGB
348 5, // ASTC_2D_5X5
349 5, // ASTC_2D_5X5_SRGB
350 8, // ASTC_2D_10X8
351 8, // ASTC_2D_10X8_SRGB
264 1, // Z32F 352 1, // Z32F
265 1, // Z16 353 1, // Z16
266 1, // Z24S8 354 1, // Z24S8
@@ -299,7 +387,7 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
299 128, // BC7U 387 128, // BC7U
300 128, // BC6H_UF16 388 128, // BC6H_UF16
301 128, // BC6H_SF16 389 128, // BC6H_SF16
302 32, // ASTC_2D_4X4 390 128, // ASTC_2D_4X4
303 16, // G8R8U 391 16, // G8R8U
304 16, // G8R8S 392 16, // G8R8S
305 32, // BGRA8 393 32, // BGRA8
@@ -322,18 +410,22 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
322 16, // RG8S 410 16, // RG8S
323 64, // RG32UI 411 64, // RG32UI
324 32, // R32UI 412 32, // R32UI
325 16, // ASTC_2D_8X8 413 128, // ASTC_2D_8X8
326 16, // ASTC_2D_8X5 414 128, // ASTC_2D_8X5
327 32, // ASTC_2D_5X4 415 128, // ASTC_2D_5X4
328 32, // BGRA8_SRGB 416 32, // BGRA8_SRGB
329 64, // DXT1_SRGB 417 64, // DXT1_SRGB
330 128, // DXT23_SRGB 418 128, // DXT23_SRGB
331 128, // DXT45_SRGB 419 128, // DXT45_SRGB
332 128, // BC7U 420 128, // BC7U
333 32, // ASTC_2D_4X4_SRGB 421 128, // ASTC_2D_4X4_SRGB
334 16, // ASTC_2D_8X8_SRGB 422 128, // ASTC_2D_8X8_SRGB
335 16, // ASTC_2D_8X5_SRGB 423 128, // ASTC_2D_8X5_SRGB
336 32, // ASTC_2D_5X4_SRGB 424 128, // ASTC_2D_5X4_SRGB
425 128, // ASTC_2D_5X5
426 128, // ASTC_2D_5X5_SRGB
427 128, // ASTC_2D_10X8
428 128, // ASTC_2D_10X8_SRGB
337 32, // Z32F 429 32, // Z32F
338 16, // Z16 430 16, // Z16
339 32, // Z24S8 431 32, // Z24S8
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index b1feacae9..bc50a4876 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1598,27 +1598,29 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
1598namespace Tegra::Texture::ASTC { 1598namespace Tegra::Texture::ASTC {
1599 1599
1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
1601 uint32_t block_width, uint32_t block_height) { 1601 uint32_t depth, uint32_t block_width, uint32_t block_height) {
1602 uint32_t blockIdx = 0; 1602 uint32_t blockIdx = 0;
1603 std::vector<uint8_t> outData(height * width * 4); 1603 std::vector<uint8_t> outData(height * width * depth * 4);
1604 for (uint32_t j = 0; j < height; j += block_height) { 1604 for (uint32_t k = 0; k < depth; k++) {
1605 for (uint32_t i = 0; i < width; i += block_width) { 1605 for (uint32_t j = 0; j < height; j += block_height) {
1606 for (uint32_t i = 0; i < width; i += block_width) {
1606 1607
1607 uint8_t* blockPtr = data.data() + blockIdx * 16; 1608 uint8_t* blockPtr = data.data() + blockIdx * 16;
1608 1609
1609 // Blocks can be at most 12x12 1610 // Blocks can be at most 12x12
1610 uint32_t uncompData[144]; 1611 uint32_t uncompData[144];
1611 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData); 1612 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
1612 1613
1613 uint32_t decompWidth = std::min(block_width, width - i); 1614 uint32_t decompWidth = std::min(block_width, width - i);
1614 uint32_t decompHeight = std::min(block_height, height - j); 1615 uint32_t decompHeight = std::min(block_height, height - j);
1615 1616
1616 uint8_t* outRow = outData.data() + (j * width + i) * 4; 1617 uint8_t* outRow = outData.data() + (j * width + i) * 4;
1617 for (uint32_t jj = 0; jj < decompHeight; jj++) { 1618 for (uint32_t jj = 0; jj < decompHeight; jj++) {
1618 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); 1619 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
1619 } 1620 }
1620 1621
1621 blockIdx++; 1622 blockIdx++;
1623 }
1622 } 1624 }
1623 } 1625 }
1624 1626
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index f0d7c0e56..d419dd025 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -10,6 +10,6 @@
10namespace Tegra::Texture::ASTC { 10namespace Tegra::Texture::ASTC {
11 11
12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
13 uint32_t block_width, uint32_t block_height); 13 uint32_t depth, uint32_t block_width, uint32_t block_height);
14 14
15} // namespace Tegra::Texture::ASTC 15} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 550ca856c..19f30b1b5 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -45,7 +45,7 @@ constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
45 * Instead of going gob by gob, we map the coordinates inside a block and manage from 45 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1. 46 * those. Block_Width is assumed to be 1.
47 */ 47 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, 48void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, 49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset, 50 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x, 51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
@@ -81,7 +81,7 @@ void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unsw
81 * Instead of going gob by gob, we map the coordinates inside a block and manage from 81 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1. 82 * those. Block_Width is assumed to be 1.
83 */ 83 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, 84void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, 85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset, 86 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x, 87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
@@ -90,10 +90,10 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
90 u32 z_address = tile_offset; 90 u32 z_address = tile_offset;
91 const u32 x_startb = x_start * bytes_per_pixel; 91 const u32 x_startb = x_start * bytes_per_pixel;
92 const u32 x_endb = x_end * bytes_per_pixel; 92 const u32 x_endb = x_end * bytes_per_pixel;
93 const u32 copy_size = 16; 93 constexpr u32 copy_size = 16;
94 const u32 gob_size_x = 64; 94 constexpr u32 gob_size_x = 64;
95 const u32 gob_size_y = 8; 95 constexpr u32 gob_size_y = 8;
96 const u32 gob_size_z = 1; 96 constexpr u32 gob_size_z = 1;
97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z; 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
98 for (u32 z = z_start; z < z_end; z++) { 98 for (u32 z = z_start; z < z_end; z++) {
99 u32 y_address = z_address; 99 u32 y_address = z_address;
@@ -126,23 +126,23 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces 126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
127 */ 127 */
128template <bool fast> 128template <bool fast>
129void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width, 129void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
130 const u32 height, const u32 depth, const u32 bytes_per_pixel, 130 const u32 width, const u32 height, const u32 depth, const u32 bytes_per_pixel,
131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) { 131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; 132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel; 133 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x; 134 const u32 layer_z = height * stride_x;
135 const u32 gob_x_bytes = 64; 135 constexpr u32 gob_x_bytes = 64;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel; 136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
137 const u32 gob_elements_y = 8; 137 constexpr u32 gob_elements_y = 8;
138 const u32 gob_elements_z = 1; 138 constexpr u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x; 139 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height; 140 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth; 141 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements); 142 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements); 143 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements); 144 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; 145 constexpr u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
146 const u32 xy_block_size = gob_size * block_height; 146 const u32 xy_block_size = gob_size * block_height;
147 const u32 block_size = xy_block_size * block_depth; 147 const u32 block_size = xy_block_size * block_depth;
148 u32 tile_offset = 0; 148 u32 tile_offset = 0;
@@ -171,7 +171,7 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
171} 171}
172 172
173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, 173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
174 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, 174 u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
175 bool unswizzle, u32 block_height, u32 block_depth) { 175 bool unswizzle, u32 block_height, u32 block_depth) {
176 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 176 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, 177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
@@ -202,6 +202,8 @@ u32 BytesPerPixel(TextureFormat format) {
202 case TextureFormat::ASTC_2D_5X4: 202 case TextureFormat::ASTC_2D_5X4:
203 case TextureFormat::ASTC_2D_8X8: 203 case TextureFormat::ASTC_2D_8X8:
204 case TextureFormat::ASTC_2D_8X5: 204 case TextureFormat::ASTC_2D_8X5:
205 case TextureFormat::ASTC_2D_10X8:
206 case TextureFormat::ASTC_2D_5X5:
205 case TextureFormat::A8R8G8B8: 207 case TextureFormat::A8R8G8B8:
206 case TextureFormat::A2B10G10R10: 208 case TextureFormat::A2B10G10R10:
207 case TextureFormat::BF10GF11RF11: 209 case TextureFormat::BF10GF11RF11:
@@ -227,12 +229,14 @@ u32 BytesPerPixel(TextureFormat format) {
227 } 229 }
228} 230}
229 231
230std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 232std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
231 u32 height, u32 depth, u32 block_height, u32 block_depth) { 233 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
234 u32 block_height, u32 block_depth) {
232 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 235 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
233 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel, 236 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
234 Memory::GetPointer(address), unswizzled_data.data(), true, block_height, 237 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
235 block_depth); 238 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
239 block_height, block_depth);
236 return unswizzled_data; 240 return unswizzled_data;
237} 241}
238 242
@@ -292,6 +296,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
292 case TextureFormat::BC6H_SF16: 296 case TextureFormat::BC6H_SF16:
293 case TextureFormat::ASTC_2D_4X4: 297 case TextureFormat::ASTC_2D_4X4:
294 case TextureFormat::ASTC_2D_8X8: 298 case TextureFormat::ASTC_2D_8X8:
299 case TextureFormat::ASTC_2D_5X5:
300 case TextureFormat::ASTC_2D_10X8:
295 case TextureFormat::A8R8G8B8: 301 case TextureFormat::A8R8G8B8:
296 case TextureFormat::A2B10G10R10: 302 case TextureFormat::A2B10G10R10:
297 case TextureFormat::A1B5G5R5: 303 case TextureFormat::A1B5G5R5:
@@ -319,9 +325,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
319std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 325std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
320 u32 block_height, u32 block_depth) { 326 u32 block_height, u32 block_depth) {
321 if (tiled) { 327 if (tiled) {
322 const u32 gobs_in_x = 64; 328 constexpr u32 gobs_in_x = 64;
323 const u32 gobs_in_y = 8; 329 constexpr u32 gobs_in_y = 8;
324 const u32 gobs_in_z = 1; 330 constexpr u32 gobs_in_z = 1;
325 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); 331 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
326 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); 332 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
327 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); 333 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index b390219e4..ba065510b 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -19,8 +19,8 @@ inline std::size_t GetGOBSize() {
19/** 19/**
20 * Unswizzles a swizzled texture without changing its format. 20 * Unswizzles a swizzled texture without changing its format.
21 */ 21 */
22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 height, u32 depth, 23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight, 24 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight); 25 u32 block_depth = TICEntry::DefaultBlockHeight);
26 26
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index d12d2ecb8..e199d019a 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -168,20 +168,29 @@ struct TICEntry {
168 168
169 // High 16 bits of the pitch value 169 // High 16 bits of the pitch value
170 BitField<0, 16, u32> pitch_high; 170 BitField<0, 16, u32> pitch_high;
171 171 BitField<26, 1, u32> use_header_opt_control;
172 BitField<27, 1, u32> depth_texture;
172 BitField<28, 4, u32> max_mip_level; 173 BitField<28, 4, u32> max_mip_level;
173 }; 174 };
174 union { 175 union {
175 BitField<0, 16, u32> width_minus_1; 176 BitField<0, 16, u32> width_minus_1;
176 BitField<22, 1, u32> srgb_conversion; 177 BitField<22, 1, u32> srgb_conversion;
177 BitField<23, 4, TextureType> texture_type; 178 BitField<23, 4, TextureType> texture_type;
179 BitField<29, 3, u32> border_size;
178 }; 180 };
179 union { 181 union {
180 BitField<0, 16, u32> height_minus_1; 182 BitField<0, 16, u32> height_minus_1;
181 BitField<16, 15, u32> depth_minus_1; 183 BitField<16, 15, u32> depth_minus_1;
182 }; 184 };
185 union {
186 BitField<6, 13, u32> mip_lod_bias;
187 BitField<27, 3, u32> max_anisotropy;
188 };
183 189
184 INSERT_PADDING_BYTES(8); 190 union {
191 BitField<0, 4, u32> res_min_mip_level;
192 BitField<4, 4, u32> res_max_mip_level;
193 };
185 194
186 GPUVAddr Address() const { 195 GPUVAddr Address() const {
187 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); 196 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d4fd60a73..be69fb831 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -134,6 +134,14 @@ void Config::ReadValues() {
134 Service::Account::MAX_USERS - 1); 134 Service::Account::MAX_USERS - 1);
135 135
136 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 136 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
137
138 const auto enabled = qt_config->value("rng_seed_enabled", false).toBool();
139 if (enabled) {
140 Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong();
141 } else {
142 Settings::values.rng_seed = std::nullopt;
143 }
144
137 qt_config->endGroup(); 145 qt_config->endGroup();
138 146
139 qt_config->beginGroup("Miscellaneous"); 147 qt_config->beginGroup("Miscellaneous");
@@ -145,6 +153,7 @@ void Config::ReadValues() {
145 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 153 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
146 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 154 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
147 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); 155 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
156 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
148 qt_config->endGroup(); 157 qt_config->endGroup();
149 158
150 qt_config->beginGroup("WebService"); 159 qt_config->beginGroup("WebService");
@@ -162,6 +171,7 @@ void Config::ReadValues() {
162 171
163 qt_config->beginGroup("UIGameList"); 172 qt_config->beginGroup("UIGameList");
164 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 173 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
174 UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool();
165 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); 175 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt();
166 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt(); 176 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt();
167 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt(); 177 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt();
@@ -272,6 +282,10 @@ void Config::SaveValues() {
272 qt_config->setValue("current_user", Settings::values.current_user); 282 qt_config->setValue("current_user", Settings::values.current_user);
273 283
274 qt_config->setValue("language_index", Settings::values.language_index); 284 qt_config->setValue("language_index", Settings::values.language_index);
285
286 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
287 qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0));
288
275 qt_config->endGroup(); 289 qt_config->endGroup();
276 290
277 qt_config->beginGroup("Miscellaneous"); 291 qt_config->beginGroup("Miscellaneous");
@@ -283,6 +297,7 @@ void Config::SaveValues() {
283 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 297 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
284 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 298 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
285 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); 299 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
300 qt_config->setValue("dump_nso", Settings::values.dump_nso);
286 qt_config->endGroup(); 301 qt_config->endGroup();
287 302
288 qt_config->beginGroup("WebService"); 303 qt_config->beginGroup("WebService");
@@ -298,6 +313,7 @@ void Config::SaveValues() {
298 313
299 qt_config->beginGroup("UIGameList"); 314 qt_config->beginGroup("UIGameList");
300 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 315 qt_config->setValue("show_unknown", UISettings::values.show_unknown);
316 qt_config->setValue("show_add_ons", UISettings::values.show_add_ons);
301 qt_config->setValue("icon_size", UISettings::values.icon_size); 317 qt_config->setValue("icon_size", UISettings::values.icon_size);
302 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); 318 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
303 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id); 319 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 9e765fc93..fd5876b41 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() {
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
37} 38}
38 39
39void ConfigureDebug::applyConfiguration() { 40void ConfigureDebug::applyConfiguration() {
@@ -42,6 +43,7 @@ void ConfigureDebug::applyConfiguration() {
42 UISettings::values.show_console = ui->toggle_console->isChecked(); 43 UISettings::values.show_console = ui->toggle_console->isChecked();
43 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 44 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
44 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 45 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
46 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
45 Debugger::ToggleConsole(); 47 Debugger::ToggleConsole();
46 Log::Filter filter; 48 Log::Filter filter;
47 filter.ParseFilterString(Settings::values.log_filter); 49 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index ff4987604..9c5b702f8 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>400</width>
10 <height>300</height> 10 <height>357</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -130,6 +130,25 @@
130 </widget> 130 </widget>
131 </item> 131 </item>
132 <item> 132 <item>
133 <widget class="QGroupBox" name="groupBox_4">
134 <property name="title">
135 <string>Dump</string>
136 </property>
137 <layout class="QVBoxLayout" name="verticalLayout_4">
138 <item>
139 <widget class="QCheckBox" name="dump_decompressed_nso">
140 <property name="whatsThis">
141 <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
142 </property>
143 <property name="text">
144 <string>Dump Decompressed NSOs</string>
145 </property>
146 </widget>
147 </item>
148 </layout>
149 </widget>
150 </item>
151 <item>
133 <spacer name="verticalSpacer"> 152 <spacer name="verticalSpacer">
134 <property name="orientation"> 153 <property name="orientation">
135 <enum>Qt::Vertical</enum> 154 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
index 0112bd950..ae8cac243 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -52,6 +52,7 @@ ConfigureGameList::~ConfigureGameList() = default;
52 52
53void ConfigureGameList::applyConfiguration() { 53void ConfigureGameList::applyConfiguration() {
54 UISettings::values.show_unknown = ui->show_unknown->isChecked(); 54 UISettings::values.show_unknown = ui->show_unknown->isChecked();
55 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
55 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); 56 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
56 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 57 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
57 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); 58 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
@@ -64,6 +65,7 @@ void ConfigureGameList::RequestGameListUpdate() {
64 65
65void ConfigureGameList::setConfiguration() { 66void ConfigureGameList::setConfiguration() {
66 ui->show_unknown->setChecked(UISettings::values.show_unknown); 67 ui->show_unknown->setChecked(UISettings::values.show_unknown);
68 ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
67 ui->icon_size_combobox->setCurrentIndex( 69 ui->icon_size_combobox->setCurrentIndex(
68 ui->icon_size_combobox->findData(UISettings::values.icon_size)); 70 ui->icon_size_combobox->findData(UISettings::values.icon_size));
69 ui->row_1_text_combobox->setCurrentIndex( 71 ui->row_1_text_combobox->setCurrentIndex(
diff --git a/src/yuzu/configuration/configure_gamelist.ui b/src/yuzu/configuration/configure_gamelist.ui
index 7471fdb60..7a69377e7 100644
--- a/src/yuzu/configuration/configure_gamelist.ui
+++ b/src/yuzu/configuration/configure_gamelist.ui
@@ -1,126 +1,133 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureGameList</class> 3 <class>ConfigureGameList</class>
4 <widget class="QWidget" name="ConfigureGeneral"> 4 <widget class="QWidget" name="ConfigureGameList">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>300</width> 9 <width>300</width>
10 <height>377</height> 10 <height>377</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Form</string> 14 <string>Form</string>
15 </property> 15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout"> 16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout"> 18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
19 <item> 27 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox"> 28 <widget class="QCheckBox" name="show_unknown">
21 <property name="title"> 29 <property name="text">
22 <string>General</string> 30 <string>Show files with type 'Unknown'</string>
23 </property> 31 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> 32 </widget>
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
27 <item>
28 <widget class="QCheckBox" name="show_unknown">
29 <property name="text">
30 <string>Show files with type 'Unknown'</string>
31 </property>
32 </widget>
33 </item>
34 </layout>
35 </item>
36 </layout>
37 </widget>
38 </item> 33 </item>
39 <item> 34 <item>
40 <widget class="QGroupBox" name="IconSizeGroupBox"> 35 <widget class="QCheckBox" name="show_add_ons">
41 <property name="title"> 36 <property name="text">
42 <string>Icon Size</string> 37 <string>Show Add-Ons Column</string>
43 </property> 38 </property>
44 <layout class="QHBoxLayout" name="icon_size_qhbox_layout"> 39 </widget>
45 <item>
46 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
47 <item>
48 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
49 <item>
50 <widget class="QLabel" name="icon_size_label">
51 <property name="text">
52 <string>Icon Size:</string>
53 </property>
54 </widget>
55 </item>
56 <item>
57 <widget class="QComboBox" name="icon_size_combobox"/>
58 </item>
59 </layout>
60 </item>
61 </layout>
62 </item>
63 </layout>
64 </widget>
65 </item> 40 </item>
41 </layout>
42 </item>
43 </layout>
44 </widget>
45 </item>
46 <item>
47 <widget class="QGroupBox" name="IconSizeGroupBox">
48 <property name="title">
49 <string>Icon Size</string>
50 </property>
51 <layout class="QHBoxLayout" name="icon_size_qhbox_layout">
52 <item>
53 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
66 <item> 54 <item>
67 <widget class="QGroupBox" name="RowGroupBox"> 55 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
68 <property name="title"> 56 <item>
69 <string>Row Text</string> 57 <widget class="QLabel" name="icon_size_label">
58 <property name="text">
59 <string>Icon Size:</string>
70 </property> 60 </property>
71 <layout class="QHBoxLayout" name="RowHorizontalLayout"> 61 </widget>
72 <item> 62 </item>
73 <layout class="QVBoxLayout" name="RowVerticalLayout"> 63 <item>
74 <item> 64 <widget class="QComboBox" name="icon_size_combobox"/>
75 <layout class="QHBoxLayout" name="row_1_qhbox_layout"> 65 </item>
76 <item> 66 </layout>
77 <widget class="QLabel" name="row_1_label">
78 <property name="text">
79 <string>Row 1 Text:</string>
80 </property>
81 </widget>
82 </item>
83 <item>
84 <widget class="QComboBox" name="row_1_text_combobox"/>
85 </item>
86 </layout>
87 </item>
88 <item>
89 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
90 <item>
91 <widget class="QLabel" name="row_2_label">
92 <property name="text">
93 <string>Row 2 Text:</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QComboBox" name="row_2_text_combobox"/>
99 </item>
100 </layout>
101 </item>
102 </layout>
103 </item>
104 </layout>
105 </widget>
106 </item> 67 </item>
68 </layout>
69 </item>
70 </layout>
71 </widget>
72 </item>
73 <item>
74 <widget class="QGroupBox" name="RowGroupBox">
75 <property name="title">
76 <string>Row Text</string>
77 </property>
78 <layout class="QHBoxLayout" name="RowHorizontalLayout">
79 <item>
80 <layout class="QVBoxLayout" name="RowVerticalLayout">
107 <item> 81 <item>
108 <spacer name="verticalSpacer"> 82 <layout class="QHBoxLayout" name="row_1_qhbox_layout">
109 <property name="orientation"> 83 <item>
110 <enum>Qt::Vertical</enum> 84 <widget class="QLabel" name="row_1_label">
85 <property name="text">
86 <string>Row 1 Text:</string>
111 </property> 87 </property>
112 <property name="sizeHint" stdset="0"> 88 </widget>
113 <size> 89 </item>
114 <width>20</width> 90 <item>
115 <height>40</height> 91 <widget class="QComboBox" name="row_1_text_combobox"/>
116 </size> 92 </item>
93 </layout>
94 </item>
95 <item>
96 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
97 <item>
98 <widget class="QLabel" name="row_2_label">
99 <property name="text">
100 <string>Row 2 Text:</string>
117 </property> 101 </property>
118 </spacer> 102 </widget>
103 </item>
104 <item>
105 <widget class="QComboBox" name="row_2_text_combobox"/>
106 </item>
107 </layout>
119 </item> 108 </item>
120 </layout> 109 </layout>
121 </item> 110 </item>
111 </layout>
112 </widget>
113 </item>
114 <item>
115 <spacer name="verticalSpacer">
116 <property name="orientation">
117 <enum>Qt::Vertical</enum>
118 </property>
119 <property name="sizeHint" stdset="0">
120 <size>
121 <width>20</width>
122 <height>40</height>
123 </size>
124 </property>
125 </spacer>
126 </item>
122 </layout> 127 </layout>
123 </widget> 128 </item>
129 </layout>
130 </widget>
124 <resources/> 131 <resources/>
125 <connections/> 132 <connections/>
126</ui> 133</ui>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 8e77558fa..c22742007 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -3,6 +3,10 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/service/am/am.h"
7#include "core/hle/service/am/applet_ae.h"
8#include "core/hle/service/am/applet_oe.h"
9#include "core/hle/service/sm/sm.h"
6#include "core/settings.h" 10#include "core/settings.h"
7#include "ui_configure_general.h" 11#include "ui_configure_general.h"
8#include "yuzu/configuration/configure_general.h" 12#include "yuzu/configuration/configure_general.h"
@@ -23,7 +27,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
23 [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); 27 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
24 28
25 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 29 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
26 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
27} 30}
28 31
29ConfigureGeneral::~ConfigureGeneral() = default; 32ConfigureGeneral::~ConfigureGeneral() = default;
@@ -41,6 +44,33 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
41 ui->widget->Populate(registry); 44 ui->widget->Populate(registry);
42} 45}
43 46
47void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
48 if (last_state == new_state) {
49 return;
50 }
51
52 Core::System& system{Core::System::GetInstance()};
53 if (!system.IsPoweredOn()) {
54 return;
55 }
56 Service::SM::ServiceManager& sm = system.ServiceManager();
57
58 // Message queue is shared between these services, we just need to signal an operation
59 // change to one and it will handle both automatically
60 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
61 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
62 bool has_signalled = false;
63
64 if (applet_oe != nullptr) {
65 applet_oe->GetMessageQueue()->OperationModeChanged();
66 has_signalled = true;
67 }
68
69 if (applet_ae != nullptr && !has_signalled) {
70 applet_ae->GetMessageQueue()->OperationModeChanged();
71 }
72}
73
44void ConfigureGeneral::applyConfiguration() { 74void ConfigureGeneral::applyConfiguration() {
45 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 75 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
46 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 76 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -48,6 +78,9 @@ void ConfigureGeneral::applyConfiguration() {
48 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 78 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
49 79
50 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 80 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
81 const bool pre_docked_mode = Settings::values.use_docked_mode;
51 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 82 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
83 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
84
52 Settings::values.enable_nfc = ui->enable_nfc->isChecked(); 85 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
53} 86}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 4770034cc..2210d48da 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -25,6 +25,7 @@ public:
25 25
26private: 26private:
27 void setConfiguration(); 27 void setConfiguration();
28 void OnDockedModeChanged(bool last_state, bool new_state);
28 29
29 std::unique_ptr<Ui::ConfigureGeneral> ui; 30 std::unique_ptr<Ui::ConfigureGeneral> ui;
30}; 31};
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index b4b4a4a56..ab5d46492 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -137,6 +137,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); 137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); 138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
139 139
140 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
141 ui->rng_seed_edit->setEnabled(checked);
142 if (!checked)
143 ui->rng_seed_edit->setText(QStringLiteral("00000000"));
144 });
145
140 scene = new QGraphicsScene; 146 scene = new QGraphicsScene;
141 ui->current_user_icon->setScene(scene); 147 ui->current_user_icon->setScene(scene);
142 148
@@ -155,6 +161,13 @@ void ConfigureSystem::setConfiguration() {
155 161
156 PopulateUserList(); 162 PopulateUserList();
157 UpdateCurrentUser(); 163 UpdateCurrentUser();
164
165 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
166 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value());
167
168 const auto rng_seed =
169 QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper();
170 ui->rng_seed_edit->setText(rng_seed);
158} 171}
159 172
160void ConfigureSystem::PopulateUserList() { 173void ConfigureSystem::PopulateUserList() {
@@ -195,6 +208,12 @@ void ConfigureSystem::applyConfiguration() {
195 return; 208 return;
196 209
197 Settings::values.language_index = ui->combo_language->currentIndex(); 210 Settings::values.language_index = ui->combo_language->currentIndex();
211
212 if (ui->rng_seed_checkbox->isChecked())
213 Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16);
214 else
215 Settings::values.rng_seed = std::nullopt;
216
198 Settings::Apply(); 217 Settings::Apply();
199} 218}
200 219
@@ -240,7 +259,7 @@ void ConfigureSystem::RefreshConsoleID() {
240 259
241void ConfigureSystem::SelectUser(const QModelIndex& index) { 260void ConfigureSystem::SelectUser(const QModelIndex& index) {
242 Settings::values.current_user = 261 Settings::values.current_user =
243 std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1); 262 std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1));
244 263
245 UpdateCurrentUser(); 264 UpdateCurrentUser();
246 265
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 020b32a37..a91580893 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>360</width> 9 <width>366</width>
10 <height>483</height> 10 <height>483</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -22,98 +22,6 @@
22 <string>System Settings</string> 22 <string>System Settings</string>
23 </property> 23 </property>
24 <layout class="QGridLayout" name="gridLayout"> 24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="0">
26 <widget class="QLabel" name="label_language">
27 <property name="text">
28 <string>Language</string>
29 </property>
30 </widget>
31 </item>
32 <item row="0" column="0">
33 <widget class="QLabel" name="label_birthday">
34 <property name="text">
35 <string>Birthday</string>
36 </property>
37 </widget>
38 </item>
39 <item row="3" column="0">
40 <widget class="QLabel" name="label_console_id">
41 <property name="text">
42 <string>Console ID:</string>
43 </property>
44 </widget>
45 </item>
46 <item row="0" column="1">
47 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
48 <item>
49 <widget class="QComboBox" name="combo_birthmonth">
50 <item>
51 <property name="text">
52 <string>January</string>
53 </property>
54 </item>
55 <item>
56 <property name="text">
57 <string>February</string>
58 </property>
59 </item>
60 <item>
61 <property name="text">
62 <string>March</string>
63 </property>
64 </item>
65 <item>
66 <property name="text">
67 <string>April</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string>May</string>
73 </property>
74 </item>
75 <item>
76 <property name="text">
77 <string>June</string>
78 </property>
79 </item>
80 <item>
81 <property name="text">
82 <string>July</string>
83 </property>
84 </item>
85 <item>
86 <property name="text">
87 <string>August</string>
88 </property>
89 </item>
90 <item>
91 <property name="text">
92 <string>September</string>
93 </property>
94 </item>
95 <item>
96 <property name="text">
97 <string>October</string>
98 </property>
99 </item>
100 <item>
101 <property name="text">
102 <string>November</string>
103 </property>
104 </item>
105 <item>
106 <property name="text">
107 <string>December</string>
108 </property>
109 </item>
110 </widget>
111 </item>
112 <item>
113 <widget class="QComboBox" name="combo_birthday"/>
114 </item>
115 </layout>
116 </item>
117 <item row="1" column="1"> 25 <item row="1" column="1">
118 <widget class="QComboBox" name="combo_language"> 26 <widget class="QComboBox" name="combo_language">
119 <property name="toolTip"> 27 <property name="toolTip">
@@ -206,6 +114,13 @@
206 </item> 114 </item>
207 </widget> 115 </widget>
208 </item> 116 </item>
117 <item row="3" column="0">
118 <widget class="QLabel" name="label_console_id">
119 <property name="text">
120 <string>Console ID:</string>
121 </property>
122 </widget>
123 </item>
209 <item row="2" column="0"> 124 <item row="2" column="0">
210 <widget class="QLabel" name="label_sound"> 125 <widget class="QLabel" name="label_sound">
211 <property name="text"> 126 <property name="text">
@@ -213,6 +128,100 @@
213 </property> 128 </property>
214 </widget> 129 </widget>
215 </item> 130 </item>
131 <item row="0" column="0">
132 <widget class="QLabel" name="label_birthday">
133 <property name="text">
134 <string>Birthday</string>
135 </property>
136 </widget>
137 </item>
138 <item row="0" column="1">
139 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
140 <item>
141 <widget class="QComboBox" name="combo_birthmonth">
142 <item>
143 <property name="text">
144 <string>January</string>
145 </property>
146 </item>
147 <item>
148 <property name="text">
149 <string>February</string>
150 </property>
151 </item>
152 <item>
153 <property name="text">
154 <string>March</string>
155 </property>
156 </item>
157 <item>
158 <property name="text">
159 <string>April</string>
160 </property>
161 </item>
162 <item>
163 <property name="text">
164 <string>May</string>
165 </property>
166 </item>
167 <item>
168 <property name="text">
169 <string>June</string>
170 </property>
171 </item>
172 <item>
173 <property name="text">
174 <string>July</string>
175 </property>
176 </item>
177 <item>
178 <property name="text">
179 <string>August</string>
180 </property>
181 </item>
182 <item>
183 <property name="text">
184 <string>September</string>
185 </property>
186 </item>
187 <item>
188 <property name="text">
189 <string>October</string>
190 </property>
191 </item>
192 <item>
193 <property name="text">
194 <string>November</string>
195 </property>
196 </item>
197 <item>
198 <property name="text">
199 <string>December</string>
200 </property>
201 </item>
202 </widget>
203 </item>
204 <item>
205 <widget class="QComboBox" name="combo_birthday"/>
206 </item>
207 </layout>
208 </item>
209 <item row="3" column="1">
210 <widget class="QPushButton" name="button_regenerate_console_id">
211 <property name="sizePolicy">
212 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
213 <horstretch>0</horstretch>
214 <verstretch>0</verstretch>
215 </sizepolicy>
216 </property>
217 <property name="layoutDirection">
218 <enum>Qt::RightToLeft</enum>
219 </property>
220 <property name="text">
221 <string>Regenerate</string>
222 </property>
223 </widget>
224 </item>
216 <item row="2" column="1"> 225 <item row="2" column="1">
217 <widget class="QComboBox" name="combo_sound"> 226 <widget class="QComboBox" name="combo_sound">
218 <item> 227 <item>
@@ -232,19 +241,38 @@
232 </item> 241 </item>
233 </widget> 242 </widget>
234 </item> 243 </item>
235 <item row="3" column="1"> 244 <item row="1" column="0">
236 <widget class="QPushButton" name="button_regenerate_console_id"> 245 <widget class="QLabel" name="label_language">
246 <property name="text">
247 <string>Language</string>
248 </property>
249 </widget>
250 </item>
251 <item row="4" column="0">
252 <widget class="QCheckBox" name="rng_seed_checkbox">
253 <property name="text">
254 <string>RNG Seed</string>
255 </property>
256 </widget>
257 </item>
258 <item row="4" column="1">
259 <widget class="QLineEdit" name="rng_seed_edit">
237 <property name="sizePolicy"> 260 <property name="sizePolicy">
238 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 261 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
239 <horstretch>0</horstretch> 262 <horstretch>0</horstretch>
240 <verstretch>0</verstretch> 263 <verstretch>0</verstretch>
241 </sizepolicy> 264 </sizepolicy>
242 </property> 265 </property>
243 <property name="layoutDirection"> 266 <property name="font">
244 <enum>Qt::RightToLeft</enum> 267 <font>
268 <family>Lucida Console</family>
269 </font>
245 </property> 270 </property>
246 <property name="text"> 271 <property name="inputMask">
247 <string>Regenerate</string> 272 <string notr="true">HHHHHHHH</string>
273 </property>
274 <property name="maxLength">
275 <number>8</number>
248 </property> 276 </property>
249 </widget> 277 </widget>
250 </item> 278 </item>
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 0adbab27d..707747422 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,9 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = 389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
390 Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format), 390 *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width,
391 surface_width, surface_height, 1U); 391 surface_height, 1U);
392 392
393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
394 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index a5a4aa432..11a8c390b 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -215,12 +215,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
215 tree_view->setUniformRowHeights(true); 215 tree_view->setUniformRowHeights(true);
216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
217 217
218 item_model->insertColumns(0, COLUMN_COUNT); 218 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); 219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); 220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
221 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); 221
222 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); 222 if (UISettings::values.show_add_ons) {
223 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); 223 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
224 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
225 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
226 } else {
227 item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
228 item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
229 }
224 230
225 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 231 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
226 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 232 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
@@ -394,6 +400,25 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
394 } 400 }
395 401
396 tree_view->setEnabled(false); 402 tree_view->setEnabled(false);
403
404 // Update the columns in case UISettings has changed
405 item_model->removeColumns(0, item_model->columnCount());
406 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
407 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
408 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
409
410 if (UISettings::values.show_add_ons) {
411 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
412 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
413 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
414 } else {
415 item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
416 item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
417 item_model->removeColumns(COLUMN_COUNT - 1, 1);
418 }
419
420 LoadInterfaceLayout();
421
397 // Delete any rows that might already exist if we're repopulating 422 // Delete any rows that might already exist if we're repopulating
398 item_model->removeRows(0, item_model->rowCount()); 423 item_model->removeRows(0, item_model->rowCount());
399 424
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 3d865a12d..362902e46 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -123,17 +123,22 @@ void GameListWorker::AddInstalledTitlesToGameList() {
123 if (it != compatibility_list.end()) 123 if (it != compatibility_list.end())
124 compatibility = it->second.first; 124 compatibility = it->second.first;
125 125
126 emit EntryReady({ 126 QList<QStandardItem*> list{
127 new GameListItemPath( 127 new GameListItemPath(
128 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), 128 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
130 program_id), 130 program_id),
131 new GameListItemCompat(compatibility), 131 new GameListItemCompat(compatibility),
132 new GameListItem(FormatPatchNameVersions(patch, *loader)),
133 new GameListItem( 132 new GameListItem(
134 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 133 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
135 new GameListItemSize(file->GetSize()), 134 new GameListItemSize(file->GetSize()),
136 }); 135 };
136
137 if (UISettings::values.show_add_ons) {
138 list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
139 }
140
141 emit EntryReady(list);
137 } 142 }
138 143
139 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, 144 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
@@ -216,18 +221,23 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
216 if (it != compatibility_list.end()) 221 if (it != compatibility_list.end())
217 compatibility = it->second.first; 222 compatibility = it->second.first;
218 223
219 emit EntryReady({ 224 QList<QStandardItem*> list{
220 new GameListItemPath( 225 new GameListItemPath(
221 FormatGameName(physical_name), icon, QString::fromStdString(name), 226 FormatGameName(physical_name), icon, QString::fromStdString(name),
222 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 227 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
223 program_id), 228 program_id),
224 new GameListItemCompat(compatibility), 229 new GameListItemCompat(compatibility),
225 new GameListItem( 230 new GameListItem(
226 FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
227 new GameListItem(
228 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 231 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
229 new GameListItemSize(FileUtil::GetSize(physical_name)), 232 new GameListItemSize(FileUtil::GetSize(physical_name)),
230 }); 233 };
234
235 if (UISettings::values.show_add_ons) {
236 list.insert(2, new GameListItem(FormatPatchNameVersions(
237 patch, *loader, loader->IsRomFSUpdatable())));
238 }
239
240 emit EntryReady(std::move(list));
231 } else if (is_dir && recursion > 0) { 241 } else if (is_dir && recursion > 0) {
232 watch_list.append(QString::fromStdString(physical_name)); 242 watch_list.append(QString::fromStdString(physical_name));
233 AddFstEntriesToGameList(physical_name, recursion - 1); 243 AddFstEntriesToGameList(physical_name, recursion - 1);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e9253493b..4b969119c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -12,7 +12,7 @@
12#include "core/file_sys/vfs_real.h" 12#include "core/file_sys/vfs_real.h"
13#include "core/hle/service/acc/profile_manager.h" 13#include "core/hle/service/acc/profile_manager.h"
14 14
15// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows 15// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
16// defines. 16// defines.
17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( 17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { 18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
@@ -142,6 +142,9 @@ static void InitializeLogging() {
142 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 142 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
143 FileUtil::CreateFullPath(log_dir); 143 FileUtil::CreateFullPath(log_dir);
144 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 144 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
145#ifdef _WIN32
146 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
147#endif
145} 148}
146 149
147GMainWindow::GMainWindow() 150GMainWindow::GMainWindow()
@@ -305,6 +308,8 @@ void GMainWindow::InitializeHotkeys() {
305 Qt::ApplicationShortcut); 308 Qt::ApplicationShortcut);
306 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), 309 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
307 Qt::ApplicationShortcut); 310 Qt::ApplicationShortcut);
311 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
312 Qt::ApplicationShortcut);
308 hotkey_registry.LoadHotkeys(); 313 hotkey_registry.LoadHotkeys();
309 314
310 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 315 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -358,6 +363,12 @@ void GMainWindow::InitializeHotkeys() {
358 UpdateStatusBar(); 363 UpdateStatusBar();
359 } 364 }
360 }); 365 });
366 connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
367 this, [&] {
368 if (ui.action_Load_Amiibo->isEnabled()) {
369 OnLoadAmiibo();
370 }
371 });
361} 372}
362 373
363void GMainWindow::SetDefaultUIGeometry() { 374void GMainWindow::SetDefaultUIGeometry() {
@@ -454,6 +465,7 @@ void GMainWindow::ConnectMenuEvents() {
454 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 465 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
455 466
456 // Help 467 // Help
468 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
457 connect(ui.action_Rederive, &QAction::triggered, this, 469 connect(ui.action_Rederive, &QAction::triggered, this,
458 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); 470 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
459 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 471 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
@@ -929,7 +941,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
929 const auto full = res == "Full"; 941 const auto full = res == "Full";
930 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 942 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
931 943
932 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this); 944 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0,
945 static_cast<s32>(entry_size), this);
933 progress.setWindowModality(Qt::WindowModal); 946 progress.setWindowModality(Qt::WindowModal);
934 progress.setMinimumDuration(100); 947 progress.setMinimumDuration(100);
935 948
@@ -1380,6 +1393,11 @@ void GMainWindow::OnLoadAmiibo() {
1380 } 1393 }
1381} 1394}
1382 1395
1396void GMainWindow::OnOpenYuzuFolder() {
1397 QDesktopServices::openUrl(QUrl::fromLocalFile(
1398 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
1399}
1400
1383void GMainWindow::OnAbout() { 1401void GMainWindow::OnAbout() {
1384 AboutDialog aboutDialog(this); 1402 AboutDialog aboutDialog(this);
1385 aboutDialog.exec(); 1403 aboutDialog.exec();
@@ -1538,7 +1556,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1538 "derivation. It will be attempted but may not complete.<br><br>") + 1556 "derivation. It will be attempted but may not complete.<br><br>") +
1539 errors + 1557 errors +
1540 tr("<br><br>You can get all of these and dump all of your games easily by " 1558 tr("<br><br>You can get all of these and dump all of your games easily by "
1541 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the " 1559 "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
1542 "quickstart guide</a>. Alternatively, you can use another method of dumping " 1560 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1543 "to obtain all of your keys.")); 1561 "to obtain all of your keys."));
1544 } 1562 }
@@ -1618,7 +1636,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
1618 return; 1636 return;
1619 } 1637 }
1620 1638
1621 if (ui.action_Fullscreen->isChecked()) { 1639 if (!ui.action_Fullscreen->isChecked()) {
1622 UISettings::values.geometry = saveGeometry(); 1640 UISettings::values.geometry = saveGeometry();
1623 UISettings::values.renderwindow_geometry = render_window->saveGeometry(); 1641 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
1624 } 1642 }
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index af637d89e..929250e8c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -167,6 +167,7 @@ private slots:
167 void OnMenuRecentFile(); 167 void OnMenuRecentFile();
168 void OnConfigure(); 168 void OnConfigure();
169 void OnLoadAmiibo(); 169 void OnLoadAmiibo();
170 void OnOpenYuzuFolder();
170 void OnAbout(); 171 void OnAbout();
171 void OnToggleFilterBar(); 172 void OnToggleFilterBar();
172 void OnDisplayTitleBars(bool); 173 void OnDisplayTitleBars(bool);
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 48d099591..75e96387f 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -70,6 +70,8 @@
70 <addaction name="separator"/> 70 <addaction name="separator"/>
71 <addaction name="action_Load_Amiibo"/> 71 <addaction name="action_Load_Amiibo"/>
72 <addaction name="separator"/> 72 <addaction name="separator"/>
73 <addaction name="action_Open_yuzu_Folder"/>
74 <addaction name="separator"/>
73 <addaction name="action_Exit"/> 75 <addaction name="action_Exit"/>
74 </widget> 76 </widget>
75 <widget class="QMenu" name="menu_Emulation"> 77 <widget class="QMenu" name="menu_Emulation">
@@ -277,6 +279,11 @@
277 <bool>false</bool> 279 <bool>false</bool>
278 </property> 280 </property>
279 </action> 281 </action>
282 <action name="action_Open_yuzu_Folder">
283 <property name="text">
284 <string>Open yuzu Folder</string>
285 </property>
286 </action>
280 </widget> 287 </widget>
281 <resources/> 288 <resources/>
282 <connections/> 289 <connections/>
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index af1c9432f..e80aebc0a 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -60,6 +60,7 @@ struct Values {
60 60
61 // Game List 61 // Game List
62 bool show_unknown; 62 bool show_unknown;
63 bool show_add_ons;
63 uint32_t icon_size; 64 uint32_t icon_size;
64 uint8_t row_1_text_id; 65 uint8_t row_1_text_id;
65 uint8_t row_2_text_id; 66 uint8_t row_2_text_id;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index b456266a6..96f1ef636 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -132,6 +132,15 @@ void Config::ReadValues() {
132 Settings::values.current_user = std::clamp<int>( 132 Settings::values.current_user = std::clamp<int>(
133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); 133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
134 134
135 const auto enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
136 if (enabled) {
137 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0);
138 } else {
139 Settings::values.rng_seed = std::nullopt;
140 }
141
142 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
143
135 // Miscellaneous 144 // Miscellaneous
136 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 145 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
137 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 146 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
@@ -141,6 +150,7 @@ void Config::ReadValues() {
141 Settings::values.gdbstub_port = 150 Settings::values.gdbstub_port =
142 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 151 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
143 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); 152 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
153 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
144 154
145 // Web Service 155 // Web Service
146 Settings::values.enable_telemetry = 156 Settings::values.enable_telemetry =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e0b223cd6..ecf625e7b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -178,6 +178,11 @@ use_docked_mode =
178# 1 (default): Yes, 0 : No 178# 1 (default): Yes, 0 : No
179enable_nfc = 179enable_nfc =
180 180
181# Sets the seed for the RNG generator built into the switch
182# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
183rng_seed_enabled =
184rng_seed =
185
181# Sets the account username, max length is 32 characters 186# Sets the account username, max length is 32 characters
182# yuzu (default) 187# yuzu (default)
183username = yuzu 188username = yuzu
@@ -201,6 +206,8 @@ log_filter = *:Trace
201# Port for listening to GDB connections. 206# Port for listening to GDB connections.
202use_gdbstub=false 207use_gdbstub=false
203gdbstub_port=24689 208gdbstub_port=24689
209# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
210dump_nso=false
204 211
205[WebService] 212[WebService]
206# Whether or not to enable telemetry 213# Whether or not to enable telemetry
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c8b93b85b..806127b12 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -76,6 +76,9 @@ static void InitializeLogging() {
76 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 76 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
77 FileUtil::CreateFullPath(log_dir); 77 FileUtil::CreateFullPath(log_dir);
78 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 78 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
79#ifdef _WIN32
80 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
81#endif
79} 82}
80 83
81/// Application entry point 84/// Application entry point