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/cubeb_sink.cpp3
-rw-r--r--src/audio_core/stream.cpp4
-rw-r--r--src/audio_core/time_stretch.cpp9
-rw-r--r--src/audio_core/time_stretch.h1
-rw-r--r--src/common/bit_field.h19
-rw-r--r--src/common/logging/backend.cpp14
-rw-r--r--src/common/logging/backend.h14
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/common/string_util.cpp57
-rw-r--r--src/common/string_util.h33
-rw-r--r--src/common/telemetry.h4
-rw-r--r--src/core/core.cpp30
-rw-r--r--src/core/core.h31
-rw-r--r--src/core/crypto/key_manager.cpp47
-rw-r--r--src/core/crypto/key_manager.h9
-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.cpp6
-rw-r--r--src/core/file_sys/card_image.h6
-rw-r--r--src/core/file_sys/content_archive.cpp41
-rw-r--r--src/core/file_sys/content_archive.h13
-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/fsmitm_romfsbuild.h1
-rw-r--r--src/core/file_sys/ips_layer.cpp6
-rw-r--r--src/core/file_sys/partition_filesystem.cpp16
-rw-r--r--src/core/file_sys/partition_filesystem.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp38
-rw-r--r--src/core/file_sys/registered_cache.cpp90
-rw-r--r--src/core/file_sys/registered_cache.h34
-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.cpp6
-rw-r--r--src/core/file_sys/submission_package.h5
-rw-r--r--src/core/file_sys/vfs.cpp12
-rw-r--r--src/core/file_sys/vfs.h36
-rw-r--r--src/core/file_sys/vfs_layered.cpp3
-rw-r--r--src/core/file_sys/vfs_layered.h3
-rw-r--r--src/core/file_sys/vfs_offset.cpp4
-rw-r--r--src/core/file_sys/vfs_offset.h2
-rw-r--r--src/core/file_sys/vfs_real.cpp3
-rw-r--r--src/core/file_sys/vfs_real.h3
-rw-r--r--src/core/file_sys/vfs_static.h4
-rw-r--r--src/core/file_sys/vfs_vector.cpp7
-rw-r--r--src/core/file_sys/vfs_vector.h3
-rw-r--r--src/core/file_sys/xts_archive.cpp3
-rw-r--r--src/core/file_sys/xts_archive.h3
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/ipc_helpers.h3
-rw-r--r--src/core/hle/kernel/errors.h89
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/kernel.cpp4
-rw-r--r--src/core/hle/kernel/mutex.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp90
-rw-r--r--src/core/hle/kernel/process.h38
-rw-r--r--src/core/hle/kernel/scheduler.cpp28
-rw-r--r--src/core/hle/kernel/scheduler.h19
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_session.cpp4
-rw-r--r--src/core/hle/kernel/shared_memory.cpp7
-rw-r--r--src/core/hle/kernel/svc.cpp232
-rw-r--r--src/core/hle/kernel/svc.h31
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/thread.cpp54
-rw-r--r--src/core/hle/kernel/thread.h13
-rw-r--r--src/core/hle/kernel/vm_manager.cpp102
-rw-r--r--src/core/hle/kernel/vm_manager.h24
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp100
-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.cpp221
-rw-r--r--src/core/hle/service/acc/profile_manager.h35
-rw-r--r--src/core/hle/service/am/am.cpp133
-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/aoc/aoc_u.cpp6
-rw-r--r--src/core/hle/service/audio/audren_u.cpp31
-rw-r--r--src/core/hle/service/audio/hwopus.cpp4
-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.cpp203
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp69
-rw-r--r--src/core/hle/service/hid/hid.cpp18
-rw-r--r--src/core/hle/service/ldr/ldr.cpp425
-rw-r--r--src/core/hle/service/nfc/nfc.cpp53
-rw-r--r--src/core/hle/service/nfp/nfp.cpp270
-rw-r--r--src/core/hle/service/nfp/nfp.h23
-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.cpp8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp12
-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/usb/usb.cpp49
-rw-r--r--src/core/hle/service/vi/vi.cpp31
-rw-r--r--src/core/loader/loader.h5
-rw-r--r--src/core/loader/nro.cpp33
-rw-r--r--src/core/loader/nro.h3
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/loader/nsp.cpp37
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/core/memory_hook.h15
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/settings.h8
-rw-r--r--src/core/telemetry_session.cpp9
-rw-r--r--src/core/telemetry_session.h6
-rw-r--r--src/tests/core/arm/arm_test_common.cpp10
-rw-r--r--src/tests/core/arm/arm_test_common.h10
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/command_processor.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp94
-rw-r--r--src/video_core/engines/maxwell_3d.h95
-rw-r--r--src/video_core/engines/shader_bytecode.h47
-rw-r--r--src/video_core/engines/shader_header.h5
-rw-r--r--src/video_core/macro_interpreter.cpp25
-rw-r--r--src/video_core/macro_interpreter.h17
-rw-r--r--src/video_core/memory_manager.cpp77
-rw-r--r--src/video_core/memory_manager.h13
-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_base.h6
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp272
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h28
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp615
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h774
-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.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp453
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp437
-rw-r--r--src/video_core/renderer_opengl/gl_state.h84
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp5
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h93
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp25
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/renderer_opengl/utils.cpp38
-rw-r--r--src/video_core/renderer_opengl/utils.h15
-rw-r--r--src/video_core/surface.cpp490
-rw-r--r--src/video_core/surface.h477
-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.cpp60
-rw-r--r--src/video_core/textures/decoders.h17
-rw-r--r--src/video_core/textures/texture.h18
-rw-r--r--src/video_core/utils.h26
-rw-r--r--src/web_service/telemetry_json.cpp21
-rw-r--r--src/web_service/telemetry_json.h1
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/compatdb.cpp27
-rw-r--r--src/yuzu/compatdb.h4
-rw-r--r--src/yuzu/configuration/config.cpp40
-rw-r--r--src/yuzu/configuration/config.h14
-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.cpp16
-rw-r--r--src/yuzu/configuration/configure_gamelist.h2
-rw-r--r--src/yuzu/configuration/configure_gamelist.ui223
-rw-r--r--src/yuzu/configuration/configure_general.cpp40
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui29
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_input.h6
-rw-r--r--src/yuzu/configuration/configure_system.cpp309
-rw-r--r--src/yuzu/configuration/configure_system.h48
-rw-r--r--src/yuzu/configuration/configure_system.ui410
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp33
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints_p.h2
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp10
-rw-r--r--src/yuzu/debugger/wait_tree.h1
-rw-r--r--src/yuzu/game_list.cpp44
-rw-r--r--src/yuzu/game_list_worker.cpp37
-rw-r--r--src/yuzu/main.cpp223
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu/main.ui34
-rw-r--r--src/yuzu/ui_settings.h3
-rw-r--r--src/yuzu/util/limitable_input_dialog.cpp59
-rw-r--r--src/yuzu/util/limitable_input_dialog.h31
-rw-r--r--src/yuzu_cmd/config.cpp18
-rw-r--r--src/yuzu_cmd/default_ini.h11
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
205 files changed, 7375 insertions, 3047 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/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 392039688..d31a1c844 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -121,7 +121,8 @@ CubebSink::CubebSink(std::string target_device_name) {
121 const auto collection_end{collection.device + collection.count}; 121 const auto collection_end{collection.device + collection.count};
122 const auto device{ 122 const auto device{
123 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { 123 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
124 return target_device_name == info.friendly_name; 124 return info.friendly_name != nullptr &&
125 target_device_name == info.friendly_name;
125 })}; 126 })};
126 if (device != collection_end) { 127 if (device != collection_end) {
127 output_device = device->devid; 128 output_device = device->devid;
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/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
index d72d67994..2fe0b3aef 100644
--- a/src/audio_core/time_stretch.cpp
+++ b/src/audio_core/time_stretch.cpp
@@ -10,8 +10,7 @@
10 10
11namespace AudioCore { 11namespace AudioCore {
12 12
13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) 13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
14 : m_sample_rate(sample_rate), m_channel_count(channel_count) {
15 m_sound_touch.setChannels(channel_count); 14 m_sound_touch.setChannels(channel_count);
16 m_sound_touch.setSampleRate(sample_rate); 15 m_sound_touch.setSampleRate(sample_rate);
17 m_sound_touch.setPitch(1.0); 16 m_sound_touch.setPitch(1.0);
@@ -33,10 +32,10 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
33 // We were given actual_samples number of samples, and num_samples were requested from us. 32 // We were given actual_samples number of samples, and num_samples were requested from us.
34 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out); 33 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
35 34
36 const double max_latency = 1.0; // seconds 35 const double max_latency = 0.25; // seconds
37 const double max_backlog = m_sample_rate * max_latency; 36 const double max_backlog = m_sample_rate * max_latency;
38 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog; 37 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
39 if (backlog_fullness > 5.0) { 38 if (backlog_fullness > 4.0) {
40 // Too many samples in backlog: Don't push anymore on 39 // Too many samples in backlog: Don't push anymore on
41 num_in = 0; 40 num_in = 0;
42 } 41 }
@@ -50,7 +49,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
50 49
51 // This low-pass filter smoothes out variance in the calculated stretch ratio. 50 // This low-pass filter smoothes out variance in the calculated stretch ratio.
52 // The time-scale determines how responsive this filter is. 51 // The time-scale determines how responsive this filter is.
53 constexpr double lpf_time_scale = 2.0; // seconds 52 constexpr double lpf_time_scale = 0.712; // seconds
54 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale); 53 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
55 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio); 54 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
56 55
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
index decd760f1..bb2270b96 100644
--- a/src/audio_core/time_stretch.h
+++ b/src/audio_core/time_stretch.h
@@ -27,7 +27,6 @@ public:
27 27
28private: 28private:
29 u32 m_sample_rate; 29 u32 m_sample_rate;
30 u32 m_channel_count;
31 soundtouch::SoundTouch m_sound_touch; 30 soundtouch::SoundTouch m_sound_touch;
32 double m_stretch_ratio = 1.0; 31 double m_stretch_ratio = 1.0;
33}; 32};
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 9f5918851..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) \
@@ -196,6 +203,7 @@ void FileBackend::Write(const Entry& entry) {
196 SUB(Service, NFP) \ 203 SUB(Service, NFP) \
197 SUB(Service, NIFM) \ 204 SUB(Service, NIFM) \
198 SUB(Service, NIM) \ 205 SUB(Service, NIM) \
206 SUB(Service, NPNS) \
199 SUB(Service, NS) \ 207 SUB(Service, NS) \
200 SUB(Service, NVDRV) \ 208 SUB(Service, NVDRV) \
201 SUB(Service, PCIE) \ 209 SUB(Service, PCIE) \
@@ -204,10 +212,12 @@ void FileBackend::Write(const Entry& entry) {
204 SUB(Service, PM) \ 212 SUB(Service, PM) \
205 SUB(Service, PREPO) \ 213 SUB(Service, PREPO) \
206 SUB(Service, PSC) \ 214 SUB(Service, PSC) \
215 SUB(Service, PSM) \
207 SUB(Service, SET) \ 216 SUB(Service, SET) \
208 SUB(Service, SM) \ 217 SUB(Service, SM) \
209 SUB(Service, SPL) \ 218 SUB(Service, SPL) \
210 SUB(Service, SSL) \ 219 SUB(Service, SSL) \
220 SUB(Service, TCAP) \
211 SUB(Service, Time) \ 221 SUB(Service, Time) \
212 SUB(Service, USB) \ 222 SUB(Service, USB) \
213 SUB(Service, VI) \ 223 SUB(Service, VI) \
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/logging/log.h b/src/common/logging/log.h
index c9161155a..d4ec31ec3 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -83,6 +83,7 @@ enum class Class : ClassType {
83 Service_NFP, ///< The NFP service 83 Service_NFP, ///< The NFP service
84 Service_NIFM, ///< The NIFM (Network interface) service 84 Service_NIFM, ///< The NIFM (Network interface) service
85 Service_NIM, ///< The NIM service 85 Service_NIM, ///< The NIM service
86 Service_NPNS, ///< The NPNS service
86 Service_NS, ///< The NS services 87 Service_NS, ///< The NS services
87 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 88 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
88 Service_PCIE, ///< The PCIe service 89 Service_PCIE, ///< The PCIe service
@@ -96,6 +97,7 @@ enum class Class : ClassType {
96 Service_SM, ///< The SM (Service manager) service 97 Service_SM, ///< The SM (Service manager) service
97 Service_SPL, ///< The SPL service 98 Service_SPL, ///< The SPL service
98 Service_SSL, ///< The SSL service 99 Service_SSL, ///< The SSL service
100 Service_TCAP, ///< The TCAP service.
99 Service_Time, ///< The time service 101 Service_Time, ///< The time service
100 Service_USB, ///< The USB (Universal Serial Bus) service 102 Service_USB, ///< The USB (Universal Serial Bus) service
101 Service_VI, ///< The VI (Video interface) service 103 Service_VI, ///< The VI (Video interface) service
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/common/telemetry.h b/src/common/telemetry.h
index 8d6ab986b..854a73fae 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
153 153
154 /// Completion method, called once all fields have been visited 154 /// Completion method, called once all fields have been visited
155 virtual void Complete() = 0; 155 virtual void Complete() = 0;
156 virtual bool SubmitTestcase() = 0;
156}; 157};
157 158
158/** 159/**
@@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
178 void Visit(const Field<std::chrono::microseconds>& /*field*/) override {} 179 void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
179 180
180 void Complete() override {} 181 void Complete() override {}
182 bool SubmitTestcase() override {
183 return false;
184 }
181}; 185};
182 186
183/// Appends build-specific information to the given FieldCollection, 187/// Appends build-specific information to the given FieldCollection,
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7cb86ed92..6d5b5a2d0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -185,7 +185,7 @@ struct System::Impl {
185 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 185 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
186 return ResultStatus::ErrorGetLoader; 186 return ResultStatus::ErrorGetLoader;
187 } 187 }
188 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = 188 std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
189 app_loader->LoadKernelSystemMode(); 189 app_loader->LoadKernelSystemMode();
190 190
191 if (system_mode.second != Loader::ResultStatus::Success) { 191 if (system_mode.second != Loader::ResultStatus::Success) {
@@ -312,6 +312,10 @@ Cpu& System::CurrentCpuCore() {
312 return impl->CurrentCpuCore(); 312 return impl->CurrentCpuCore();
313} 313}
314 314
315const Cpu& System::CurrentCpuCore() const {
316 return impl->CurrentCpuCore();
317}
318
315System::ResultStatus System::RunLoop(bool tight_loop) { 319System::ResultStatus System::RunLoop(bool tight_loop) {
316 return impl->RunLoop(tight_loop); 320 return impl->RunLoop(tight_loop);
317} 321}
@@ -342,7 +346,11 @@ PerfStatsResults System::GetAndResetPerfStats() {
342 return impl->GetAndResetPerfStats(); 346 return impl->GetAndResetPerfStats();
343} 347}
344 348
345Core::TelemetrySession& System::TelemetrySession() const { 349TelemetrySession& System::TelemetrySession() {
350 return *impl->telemetry_session;
351}
352
353const TelemetrySession& System::TelemetrySession() const {
346 return *impl->telemetry_session; 354 return *impl->telemetry_session;
347} 355}
348 356
@@ -350,7 +358,11 @@ ARM_Interface& System::CurrentArmInterface() {
350 return CurrentCpuCore().ArmInterface(); 358 return CurrentCpuCore().ArmInterface();
351} 359}
352 360
353std::size_t System::CurrentCoreIndex() { 361const ARM_Interface& System::CurrentArmInterface() const {
362 return CurrentCpuCore().ArmInterface();
363}
364
365std::size_t System::CurrentCoreIndex() const {
354 return CurrentCpuCore().CoreIndex(); 366 return CurrentCpuCore().CoreIndex();
355} 367}
356 368
@@ -358,6 +370,10 @@ Kernel::Scheduler& System::CurrentScheduler() {
358 return CurrentCpuCore().Scheduler(); 370 return CurrentCpuCore().Scheduler();
359} 371}
360 372
373const Kernel::Scheduler& System::CurrentScheduler() const {
374 return CurrentCpuCore().Scheduler();
375}
376
361Kernel::Scheduler& System::Scheduler(std::size_t core_index) { 377Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
362 return CpuCore(core_index).Scheduler(); 378 return CpuCore(core_index).Scheduler();
363} 379}
@@ -378,6 +394,10 @@ ARM_Interface& System::ArmInterface(std::size_t core_index) {
378 return CpuCore(core_index).ArmInterface(); 394 return CpuCore(core_index).ArmInterface();
379} 395}
380 396
397const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
398 return CpuCore(core_index).ArmInterface();
399}
400
381Cpu& System::CpuCore(std::size_t core_index) { 401Cpu& System::CpuCore(std::size_t core_index) {
382 ASSERT(core_index < NUM_CPU_CORES); 402 ASSERT(core_index < NUM_CPU_CORES);
383 return *impl->cpu_cores[core_index]; 403 return *impl->cpu_cores[core_index];
@@ -392,6 +412,10 @@ ExclusiveMonitor& System::Monitor() {
392 return *impl->cpu_exclusive_monitor; 412 return *impl->cpu_exclusive_monitor;
393} 413}
394 414
415const ExclusiveMonitor& System::Monitor() const {
416 return *impl->cpu_exclusive_monitor;
417}
418
395Tegra::GPU& System::GPU() { 419Tegra::GPU& System::GPU() {
396 return *impl->gpu_core; 420 return *impl->gpu_core;
397} 421}
diff --git a/src/core/core.h b/src/core/core.h
index 173be45f8..cfacceb81 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -129,11 +129,11 @@ public:
129 */ 129 */
130 bool IsPoweredOn() const; 130 bool IsPoweredOn() const;
131 131
132 /** 132 /// Gets a reference to the telemetry session for this emulation session.
133 * Returns a reference to the telemetry session for this emulation session. 133 Core::TelemetrySession& TelemetrySession();
134 * @returns Reference to the telemetry session. 134
135 */ 135 /// Gets a reference to the telemetry session for this emulation session.
136 Core::TelemetrySession& TelemetrySession() const; 136 const Core::TelemetrySession& TelemetrySession() const;
137 137
138 /// Prepare the core emulation for a reschedule 138 /// Prepare the core emulation for a reschedule
139 void PrepareReschedule(); 139 void PrepareReschedule();
@@ -144,24 +144,36 @@ public:
144 /// Gets an ARM interface to the CPU core that is currently running 144 /// Gets an ARM interface to the CPU core that is currently running
145 ARM_Interface& CurrentArmInterface(); 145 ARM_Interface& CurrentArmInterface();
146 146
147 /// Gets an ARM interface to the CPU core that is currently running
148 const ARM_Interface& CurrentArmInterface() const;
149
147 /// Gets the index of the currently running CPU core 150 /// Gets the index of the currently running CPU core
148 std::size_t CurrentCoreIndex(); 151 std::size_t CurrentCoreIndex() const;
149 152
150 /// Gets the scheduler for the CPU core that is currently running 153 /// Gets the scheduler for the CPU core that is currently running
151 Kernel::Scheduler& CurrentScheduler(); 154 Kernel::Scheduler& CurrentScheduler();
152 155
153 /// Gets an ARM interface to the CPU core with the specified index 156 /// Gets the scheduler for the CPU core that is currently running
157 const Kernel::Scheduler& CurrentScheduler() const;
158
159 /// Gets a reference to an ARM interface for the CPU core with the specified index
154 ARM_Interface& ArmInterface(std::size_t core_index); 160 ARM_Interface& ArmInterface(std::size_t core_index);
155 161
162 /// Gets a const reference to an ARM interface from the CPU core with the specified index
163 const ARM_Interface& ArmInterface(std::size_t core_index) const;
164
156 /// Gets a CPU interface to the CPU core with the specified index 165 /// Gets a CPU interface to the CPU core with the specified index
157 Cpu& CpuCore(std::size_t core_index); 166 Cpu& CpuCore(std::size_t core_index);
158 167
159 /// Gets a CPU interface to the CPU core with the specified index 168 /// Gets a CPU interface to the CPU core with the specified index
160 const Cpu& CpuCore(std::size_t core_index) const; 169 const Cpu& CpuCore(std::size_t core_index) const;
161 170
162 /// Gets the exclusive monitor 171 /// Gets a reference to the exclusive monitor
163 ExclusiveMonitor& Monitor(); 172 ExclusiveMonitor& Monitor();
164 173
174 /// Gets a constant reference to the exclusive monitor
175 const ExclusiveMonitor& Monitor() const;
176
165 /// Gets a mutable reference to the GPU interface 177 /// Gets a mutable reference to the GPU interface
166 Tegra::GPU& GPU(); 178 Tegra::GPU& GPU();
167 179
@@ -230,6 +242,9 @@ private:
230 /// Returns the currently running CPU core 242 /// Returns the currently running CPU core
231 Cpu& CurrentCpuCore(); 243 Cpu& CurrentCpuCore();
232 244
245 /// Returns the currently running CPU core
246 const Cpu& CurrentCpuCore() const;
247
233 /** 248 /**
234 * Initialize the emulated system. 249 * Initialize the emulated system.
235 * @param emu_window Reference to the host-system window used for video output and keyboard 250 * @param emu_window Reference to the host-system window used for video output and keyboard
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index fd0786068..904afa039 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -141,28 +141,28 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
141 return mac_key; 141 return mac_key;
142} 142}
143 143
144boost::optional<Key128> DeriveSDSeed() { 144std::optional<Key128> DeriveSDSeed() {
145 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 145 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
146 "/system/save/8000000000000043", 146 "/system/save/8000000000000043",
147 "rb+"); 147 "rb+");
148 if (!save_43.IsOpen()) 148 if (!save_43.IsOpen())
149 return boost::none; 149 return {};
150 150
151 const FileUtil::IOFile sd_private( 151 const FileUtil::IOFile sd_private(
152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
153 if (!sd_private.IsOpen()) 153 if (!sd_private.IsOpen())
154 return boost::none; 154 return {};
155 155
156 std::array<u8, 0x10> private_seed{}; 156 std::array<u8, 0x10> private_seed{};
157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { 157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
158 return boost::none; 158 return {};
159 } 159 }
160 160
161 std::array<u8, 0x10> buffer{}; 161 std::array<u8, 0x10> buffer{};
162 std::size_t offset = 0; 162 std::size_t offset = 0;
163 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 163 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
164 if (!save_43.Seek(offset, SEEK_SET)) { 164 if (!save_43.Seek(offset, SEEK_SET)) {
165 return boost::none; 165 return {};
166 } 166 }
167 167
168 save_43.ReadBytes(buffer.data(), buffer.size()); 168 save_43.ReadBytes(buffer.data(), buffer.size());
@@ -172,12 +172,12 @@ boost::optional<Key128> DeriveSDSeed() {
172 } 172 }
173 173
174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) { 174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
175 return boost::none; 175 return {};
176 } 176 }
177 177
178 Key128 seed{}; 178 Key128 seed{};
179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { 179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
180 return boost::none; 180 return {};
181 } 181 }
182 return seed; 182 return seed;
183} 183}
@@ -291,26 +291,26 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
291} 291}
292 292
293template <size_t size> 293template <size_t size>
294static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { 294static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
295 u64 offset = 0; 295 u64 offset = 0;
296 for (size_t i = 0x20; i < data.size() - 0x10; ++i) { 296 for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
297 if (data[i] == 0x1) { 297 if (data[i] == 0x1) {
298 offset = i + 1; 298 offset = i + 1;
299 break; 299 break;
300 } else if (data[i] != 0x0) { 300 } else if (data[i] != 0x0) {
301 return boost::none; 301 return {};
302 } 302 }
303 } 303 }
304 304
305 return offset; 305 return offset;
306} 306}
307 307
308boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, 308std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
309 const RSAKeyPair<2048>& key) { 309 const RSAKeyPair<2048>& key) {
310 u32 cert_authority; 310 u32 cert_authority;
311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); 311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
312 if (cert_authority == 0) 312 if (cert_authority == 0)
313 return boost::none; 313 return {};
314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) { 314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
315 LOG_INFO(Crypto, 315 LOG_INFO(Crypto,
316 "Attempting to parse ticket with non-standard certificate authority {:08X}.", 316 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
@@ -321,7 +321,7 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); 321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
322 322
323 if (rights_id == Key128{}) 323 if (rights_id == Key128{})
324 return boost::none; 324 return {};
325 325
326 Key128 key_temp{}; 326 Key128 key_temp{};
327 327
@@ -356,17 +356,17 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
356 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); 356 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
357 357
358 if (m_0 != 0) 358 if (m_0 != 0)
359 return boost::none; 359 return {};
360 360
361 m_1 = m_1 ^ MGF1<0x20>(m_2); 361 m_1 = m_1 ^ MGF1<0x20>(m_2);
362 m_2 = m_2 ^ MGF1<0xDF>(m_1); 362 m_2 = m_2 ^ MGF1<0xDF>(m_1);
363 363
364 const auto offset = FindTicketOffset(m_2); 364 const auto offset = FindTicketOffset(m_2);
365 if (offset == boost::none) 365 if (!offset)
366 return boost::none; 366 return {};
367 ASSERT(offset.get() > 0); 367 ASSERT(*offset > 0);
368 368
369 std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size()); 369 std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
370 370
371 return std::make_pair(rights_id, key_temp); 371 return std::make_pair(rights_id, key_temp);
372} 372}
@@ -395,7 +395,7 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
395 if (base.size() < begin + length) 395 if (base.size() < begin + length)
396 return false; 396 return false;
397 return std::all_of(base.begin() + begin, base.begin() + begin + length, 397 return std::all_of(base.begin() + begin, base.begin() + begin + length,
398 [](u8 c) { return std::isdigit(c); }); 398 [](u8 c) { return std::isxdigit(c); });
399} 399}
400 400
401void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 401void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -661,8 +661,8 @@ void KeyManager::DeriveSDSeedLazy() {
661 return; 661 return;
662 662
663 const auto res = DeriveSDSeed(); 663 const auto res = DeriveSDSeed();
664 if (res != boost::none) 664 if (res)
665 SetKey(S128KeyType::SDSeed, res.get()); 665 SetKey(S128KeyType::SDSeed, *res);
666} 666}
667 667
668static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { 668static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -713,7 +713,6 @@ void KeyManager::DeriveBase() {
713 713
714 const auto sbk = GetKey(S128KeyType::SecureBoot); 714 const auto sbk = GetKey(S128KeyType::SecureBoot);
715 const auto tsec = GetKey(S128KeyType::TSEC); 715 const auto tsec = GetKey(S128KeyType::TSEC);
716 const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
717 716
718 for (size_t i = 0; i < revisions.size(); ++i) { 717 for (size_t i = 0; i < revisions.size(); ++i) {
719 if (!revisions[i]) 718 if (!revisions[i])
@@ -890,9 +889,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
890 889
891 for (const auto& raw : res) { 890 for (const auto& raw : res) {
892 const auto pair = ParseTicket(raw, rsa_key); 891 const auto pair = ParseTicket(raw, rsa_key);
893 if (pair == boost::none) 892 if (!pair)
894 continue; 893 continue;
895 const auto& [rid, key] = pair.value(); 894 const auto& [rid, key] = *pair;
896 u128 rights_id; 895 u128 rights_id;
897 std::memcpy(rights_id.data(), rid.data(), rid.size()); 896 std::memcpy(rights_id.data(), rid.data(), rid.size());
898 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); 897 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index cccb3c0ae..22f268c65 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -6,9 +6,10 @@
6 6
7#include <array> 7#include <array>
8#include <map> 8#include <map>
9#include <optional>
9#include <string> 10#include <string>
11
10#include <boost/container/flat_map.hpp> 12#include <boost/container/flat_map.hpp>
11#include <boost/optional.hpp>
12#include <fmt/format.h> 13#include <fmt/format.h>
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "core/crypto/partition_data_manager.h" 15#include "core/crypto/partition_data_manager.h"
@@ -191,14 +192,14 @@ Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master
191std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob, 192std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
192 const Key128& key); 193 const Key128& key);
193 194
194boost::optional<Key128> DeriveSDSeed(); 195std::optional<Key128> DeriveSDSeed();
195Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); 196Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
196 197
197std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save); 198std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
198 199
199// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset 200// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
200// 0x140-0x144 is zero) 201// 0x140-0x144 is zero)
201boost::optional<std::pair<Key128, Key128>> ParseTicket( 202std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
202 const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key); 203 const RSAKeyPair<2048>& eticket_extended_key);
203 204
204} // namespace Core::Crypto 205} // namespace Core::Crypto
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 ecdd7505b..2c145bd09 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -168,10 +168,6 @@ VirtualDir XCI::GetParentDirectory() const {
168 return file->GetContainingDirectory(); 168 return file->GetContainingDirectory();
169} 169}
170 170
171bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
172 return false;
173}
174
175Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { 171Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
176 if (partitions[static_cast<std::size_t>(part)] == nullptr) { 172 if (partitions[static_cast<std::size_t>(part)] == nullptr) {
177 return Loader::ResultStatus::ErrorXCIMissingPartition; 173 return Loader::ResultStatus::ErrorXCIMissingPartition;
@@ -180,7 +176,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
180 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()) {
181 if (file->GetExtension() != "nca") 177 if (file->GetExtension() != "nca")
182 continue; 178 continue;
183 auto nca = std::make_shared<NCA>(file); 179 auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
184 // TODO(DarkLordZach): Add proper Rev1+ Support 180 // TODO(DarkLordZach): Add proper Rev1+ Support
185 if (nca->IsUpdate()) 181 if (nca->IsUpdate())
186 continue; 182 continue;
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 48cbef666..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 {
@@ -94,9 +95,6 @@ public:
94 95
95 VirtualDir GetParentDirectory() const override; 96 VirtualDir GetParentDirectory() const override;
96 97
97protected:
98 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
99
100private: 98private:
101 Loader::ResultStatus AddNCAFromPartition(XCIPartition part); 99 Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
102 100
@@ -110,5 +108,7 @@ private:
110 std::shared_ptr<NSP> secure_partition; 108 std::shared_ptr<NSP> secure_partition;
111 std::shared_ptr<NCA> program; 109 std::shared_ptr<NCA> program;
112 std::vector<std::shared_ptr<NCA>> ncas; 110 std::vector<std::shared_ptr<NCA>> ncas;
111
112 Core::Crypto::KeyManager keys;
113}; 113};
114} // namespace FileSys 114} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 6c356d85d..19b6f8600 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -4,10 +4,9 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <optional>
7#include <utility> 8#include <utility>
8 9
9#include <boost/optional.hpp>
10
11#include "common/logging/log.h" 10#include "common/logging/log.h"
12#include "core/crypto/aes_util.h" 11#include "core/crypto/aes_util.h"
13#include "core/crypto/ctr_encryption_layer.h" 12#include "core/crypto/ctr_encryption_layer.h"
@@ -102,8 +101,9 @@ static bool IsValidNCA(const NCAHeader& header) {
102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
103} 102}
104 103
105NCA::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,
106 : 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_)) {
107 if (file == nullptr) { 107 if (file == nullptr) {
108 status = Loader::ResultStatus::ErrorNullFile; 108 status = Loader::ResultStatus::ErrorNullFile;
109 return; 109 return;
@@ -306,18 +306,18 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
306 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); 306 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
307 subsection_buckets.back().entries.push_back({size, {0}, 0}); 307 subsection_buckets.back().entries.push_back({size, {0}, 0});
308 308
309 boost::optional<Core::Crypto::Key128> key = boost::none; 309 std::optional<Core::Crypto::Key128> key = {};
310 if (encrypted) { 310 if (encrypted) {
311 if (has_rights_id) { 311 if (has_rights_id) {
312 status = Loader::ResultStatus::Success; 312 status = Loader::ResultStatus::Success;
313 key = GetTitlekey(); 313 key = GetTitlekey();
314 if (key == boost::none) { 314 if (!key) {
315 status = Loader::ResultStatus::ErrorMissingTitlekey; 315 status = Loader::ResultStatus::ErrorMissingTitlekey;
316 return false; 316 return false;
317 } 317 }
318 } else { 318 } else {
319 key = GetKeyAreaKey(NCASectionCryptoType::BKTR); 319 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
320 if (key == boost::none) { 320 if (!key) {
321 status = Loader::ResultStatus::ErrorMissingKeyAreaKey; 321 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
322 return false; 322 return false;
323 } 323 }
@@ -332,7 +332,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
332 auto bktr = std::make_shared<BKTR>( 332 auto bktr = std::make_shared<BKTR>(
333 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset), 333 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
334 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted, 334 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
335 encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset, 335 encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
336 section.raw.section_ctr); 336 section.raw.section_ctr);
337 337
338 // BKTR applies to entire IVFC, so make an offset version to level 6 338 // BKTR applies to entire IVFC, so make an offset version to level 6
@@ -388,11 +388,11 @@ u8 NCA::GetCryptoRevision() const {
388 return master_key_id; 388 return master_key_id;
389} 389}
390 390
391boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { 391std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
392 const auto master_key_id = GetCryptoRevision(); 392 const auto master_key_id = GetCryptoRevision();
393 393
394 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) 394 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
395 return boost::none; 395 return {};
396 396
397 std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); 397 std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
398 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( 398 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -416,25 +416,25 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
416 return out; 416 return out;
417} 417}
418 418
419boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() { 419std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
420 const auto master_key_id = GetCryptoRevision(); 420 const auto master_key_id = GetCryptoRevision();
421 421
422 u128 rights_id{}; 422 u128 rights_id{};
423 memcpy(rights_id.data(), header.rights_id.data(), 16); 423 memcpy(rights_id.data(), header.rights_id.data(), 16);
424 if (rights_id == u128{}) { 424 if (rights_id == u128{}) {
425 status = Loader::ResultStatus::ErrorInvalidRightsID; 425 status = Loader::ResultStatus::ErrorInvalidRightsID;
426 return boost::none; 426 return {};
427 } 427 }
428 428
429 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); 429 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
430 if (titlekey == Core::Crypto::Key128{}) { 430 if (titlekey == Core::Crypto::Key128{}) {
431 status = Loader::ResultStatus::ErrorMissingTitlekey; 431 status = Loader::ResultStatus::ErrorMissingTitlekey;
432 return boost::none; 432 return {};
433 } 433 }
434 434
435 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { 435 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
436 status = Loader::ResultStatus::ErrorMissingTitlekek; 436 status = Loader::ResultStatus::ErrorMissingTitlekek;
437 return boost::none; 437 return {};
438 } 438 }
439 439
440 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( 440 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -458,25 +458,25 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
458 case NCASectionCryptoType::BKTR: 458 case NCASectionCryptoType::BKTR:
459 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 459 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
460 { 460 {
461 boost::optional<Core::Crypto::Key128> key = boost::none; 461 std::optional<Core::Crypto::Key128> key = {};
462 if (has_rights_id) { 462 if (has_rights_id) {
463 status = Loader::ResultStatus::Success; 463 status = Loader::ResultStatus::Success;
464 key = GetTitlekey(); 464 key = GetTitlekey();
465 if (key == boost::none) { 465 if (!key) {
466 if (status == Loader::ResultStatus::Success) 466 if (status == Loader::ResultStatus::Success)
467 status = Loader::ResultStatus::ErrorMissingTitlekey; 467 status = Loader::ResultStatus::ErrorMissingTitlekey;
468 return nullptr; 468 return nullptr;
469 } 469 }
470 } else { 470 } else {
471 key = GetKeyAreaKey(NCASectionCryptoType::CTR); 471 key = GetKeyAreaKey(NCASectionCryptoType::CTR);
472 if (key == boost::none) { 472 if (!key) {
473 status = Loader::ResultStatus::ErrorMissingKeyAreaKey; 473 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
474 return nullptr; 474 return nullptr;
475 } 475 }
476 } 476 }
477 477
478 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( 478 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
479 std::move(in), key.value(), starting_offset); 479 starting_offset);
480 std::vector<u8> iv(16); 480 std::vector<u8> iv(16);
481 for (u8 i = 0; i < 8; ++i) 481 for (u8 i = 0; i < 8; ++i)
482 iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; 482 iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
@@ -546,7 +546,4 @@ u64 NCA::GetBaseIVFCOffset() const {
546 return ivfc_offset; 546 return ivfc_offset;
547} 547}
548 548
549bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
550 return false;
551}
552} // namespace FileSys 549} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 1c903cd3f..99294cbb4 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -6,9 +6,10 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10#include <vector> 11#include <vector>
11#include <boost/optional.hpp> 12
12#include "common/common_funcs.h" 13#include "common/common_funcs.h"
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "common/swap.h" 15#include "common/swap.h"
@@ -78,7 +79,8 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
78class NCA : public ReadOnlyVfsDirectory { 79class NCA : public ReadOnlyVfsDirectory {
79public: 80public:
80 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, 81 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
81 u64 bktr_base_ivfc_offset = 0); 82 u64 bktr_base_ivfc_offset = 0,
83 Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
82 ~NCA() override; 84 ~NCA() override;
83 85
84 Loader::ResultStatus GetStatus() const; 86 Loader::ResultStatus GetStatus() const;
@@ -100,9 +102,6 @@ public:
100 // Returns the base ivfc offset used in BKTR patching. 102 // Returns the base ivfc offset used in BKTR patching.
101 u64 GetBaseIVFCOffset() const; 103 u64 GetBaseIVFCOffset() const;
102 104
103protected:
104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
105
106private: 105private:
107 bool CheckSupportedNCA(const NCAHeader& header); 106 bool CheckSupportedNCA(const NCAHeader& header);
108 bool HandlePotentialHeaderDecryption(); 107 bool HandlePotentialHeaderDecryption();
@@ -114,8 +113,8 @@ private:
114 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry); 113 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
115 114
116 u8 GetCryptoRevision() const; 115 u8 GetCryptoRevision() const;
117 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 116 std::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
118 boost::optional<Core::Crypto::Key128> GetTitlekey(); 117 std::optional<Core::Crypto::Key128> GetTitlekey();
119 VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset); 118 VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
120 119
121 std::vector<VirtualDir> dirs; 120 std::vector<VirtualDir> dirs;
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/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
index 3d377b0af..a62502193 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.h
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -27,7 +27,6 @@
27#include <map> 27#include <map>
28#include <memory> 28#include <memory>
29#include <string> 29#include <string>
30#include <boost/detail/container_fwd.hpp>
31#include "common/common_types.h" 30#include "common/common_types.h"
32#include "core/file_sys/vfs.h" 31#include "core/file_sys/vfs.h"
33 32
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 554eae9bc..485c4913a 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -99,16 +99,16 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
99 u16 rle_size{}; 99 u16 rle_size{};
100 if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) 100 if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
101 return nullptr; 101 return nullptr;
102 rle_size = Common::swap16(data_size); 102 rle_size = Common::swap16(rle_size);
103 offset += sizeof(u16); 103 offset += sizeof(u16);
104 104
105 const auto data = ips->ReadByte(offset++); 105 const auto data = ips->ReadByte(offset++);
106 if (data == boost::none) 106 if (!data)
107 return nullptr; 107 return nullptr;
108 108
109 if (real_offset + rle_size > in_data.size()) 109 if (real_offset + rle_size > in_data.size())
110 rle_size = static_cast<u16>(in_data.size() - real_offset); 110 rle_size = static_cast<u16>(in_data.size() - real_offset);
111 std::memset(in_data.data() + real_offset, data.get(), rle_size); 111 std::memset(in_data.data() + real_offset, *data, rle_size);
112 } else { // Standard Patch 112 } else { // Standard Patch
113 auto read = data_size; 113 auto read = data_size;
114 if (real_offset + read > in_data.size()) 114 if (real_offset + read > in_data.size())
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 5791c76ff..a5259a593 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
83} 83}
84 84
85std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { 85std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
86 return pfs_dirs; 86 return {};
87} 87}
88 88
89std::string PartitionFilesystem::GetName() const { 89std::string PartitionFilesystem::GetName() const {
@@ -103,18 +103,4 @@ void PartitionFilesystem::PrintDebugInfo() const {
103 pfs_files[i]->GetName(), pfs_files[i]->GetSize()); 103 pfs_files[i]->GetName(), pfs_files[i]->GetSize());
104 } 104 }
105} 105}
106
107bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
108 const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
109 if (iter == pfs_files.end())
110 return false;
111
112 const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
113 pfs_files[offset] = std::move(pfs_files.back());
114 pfs_files.pop_back();
115
116 pfs_dirs.emplace_back(std::move(dir));
117
118 return true;
119}
120} // namespace FileSys 106} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index 739c63a7f..248fdfdeb 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -35,9 +35,6 @@ public:
35 std::shared_ptr<VfsDirectory> GetParentDirectory() const override; 35 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
36 void PrintDebugInfo() const; 36 void PrintDebugInfo() const;
37 37
38protected:
39 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
40
41private: 38private:
42 struct Header { 39 struct Header {
43 u32_le magic; 40 u32_le magic;
@@ -84,7 +81,6 @@ private:
84 std::size_t content_offset = 0; 81 std::size_t content_offset = 0;
85 82
86 std::vector<VirtualFile> pfs_files; 83 std::vector<VirtualFile> pfs_files;
87 std::vector<VirtualDir> pfs_dirs;
88}; 84};
89 85
90} // namespace FileSys 86} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0117cb0bf..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
@@ -61,13 +62,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
61 // Game Updates 62 // Game Updates
62 const auto update_tid = GetUpdateTitleID(title_id); 63 const auto update_tid = GetUpdateTitleID(title_id);
63 const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); 64 const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
64 if (update != nullptr) { 65
65 if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 66 if (update != nullptr && update->GetExeFS() != nullptr &&
66 update->GetExeFS() != nullptr) { 67 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
67 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", 68 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
68 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); 69 FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
69 exefs = update->GetExeFS(); 70 exefs = update->GetExeFS();
70 }
71 } 71 }
72 72
73 return exefs; 73 return exefs;
@@ -120,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
120 const auto build_id_raw = Common::HexArrayToString(header.build_id); 120 const auto build_id_raw = Common::HexArrayToString(header.build_id);
121 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);
122 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
123 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); 135 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
124 136
125 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 137 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
@@ -168,7 +180,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
168 180
169static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 181static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
170 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 182 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
171 if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { 183 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
184 load_dir == nullptr || load_dir->GetSize() <= 0) {
172 return; 185 return;
173 } 186 }
174 187
@@ -218,7 +231,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
218 title_id, static_cast<u8>(type)) 231 title_id, static_cast<u8>(type))
219 .c_str(); 232 .c_str();
220 233
221 if (type == ContentRecordType::Program) 234 if (type == ContentRecordType::Program || type == ContentRecordType::Data)
222 LOG_INFO(Loader, log_string); 235 LOG_INFO(Loader, log_string);
223 else 236 else
224 LOG_DEBUG(Loader, log_string); 237 LOG_DEBUG(Loader, log_string);
@@ -236,7 +249,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
236 if (new_nca->GetStatus() == Loader::ResultStatus::Success && 249 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
237 new_nca->GetRomFS() != nullptr) { 250 new_nca->GetRomFS() != nullptr) {
238 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", 251 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
239 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); 252 FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
240 romfs = new_nca->GetRomFS(); 253 romfs = new_nca->GetRomFS();
241 } 254 }
242 } else if (update_raw != nullptr) { 255 } else if (update_raw != nullptr) {
@@ -280,12 +293,11 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
280 } else { 293 } else {
281 if (installed->HasEntry(update_tid, ContentRecordType::Program)) { 294 if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
282 const auto meta_ver = installed->GetEntryVersion(update_tid); 295 const auto meta_ver = installed->GetEntryVersion(update_tid);
283 if (meta_ver == boost::none || meta_ver.get() == 0) { 296 if (meta_ver.value_or(0) == 0) {
284 out.insert_or_assign("Update", ""); 297 out.insert_or_assign("Update", "");
285 } else { 298 } else {
286 out.insert_or_assign( 299 out.insert_or_assign(
287 "Update", 300 "Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
288 FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
289 } 301 }
290 } else if (update_raw != nullptr) { 302 } else if (update_raw != nullptr) {
291 out.insert_or_assign("Update", "PACKED"); 303 out.insert_or_assign("Update", "PACKED");
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 1febb398e..a3f8f2f73 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -2,6 +2,7 @@
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 <algorithm>
5#include <regex> 6#include <regex>
6#include <mbedtls/sha256.h> 7#include <mbedtls/sha256.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -30,6 +31,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
30 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); 31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
31} 32}
32 33
34bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36}
37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
39 return !operator==(lhs, rhs);
40}
41
33static bool FollowsTwoDigitDirFormat(std::string_view name) { 42static bool FollowsTwoDigitDirFormat(std::string_view name) {
34 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | 43 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
35 std::regex_constants::icase); 44 std::regex_constants::icase);
@@ -97,9 +106,12 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
97 106
98VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
99 std::string_view path) const { 108 std::string_view path) const {
100 if (dir->GetFileRelative(path) != nullptr) 109 const auto file = dir->GetFileRelative(path);
101 return dir->GetFileRelative(path); 110 if (file != nullptr)
102 if (dir->GetDirectoryRelative(path) != nullptr) { 111 return file;
112
113 const auto nca_dir = dir->GetDirectoryRelative(path);
114 if (nca_dir != nullptr) {
103 const auto nca_dir = dir->GetDirectoryRelative(path); 115 const auto nca_dir = dir->GetDirectoryRelative(path);
104 VirtualFile file = nullptr; 116 VirtualFile file = nullptr;
105 117
@@ -150,28 +162,28 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
150 return file; 162 return file;
151} 163}
152 164
153static boost::optional<NcaID> CheckMapForContentRecord( 165static std::optional<NcaID> CheckMapForContentRecord(
154 const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { 166 const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
155 if (map.find(title_id) == map.end()) 167 if (map.find(title_id) == map.end())
156 return boost::none; 168 return {};
157 169
158 const auto& cnmt = map.at(title_id); 170 const auto& cnmt = map.at(title_id);
159 171
160 const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(), 172 const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
161 [type](const ContentRecord& rec) { return rec.type == type; }); 173 [type](const ContentRecord& rec) { return rec.type == type; });
162 if (iter == cnmt.GetContentRecords().end()) 174 if (iter == cnmt.GetContentRecords().end())
163 return boost::none; 175 return {};
164 176
165 return boost::make_optional(iter->nca_id); 177 return std::make_optional(iter->nca_id);
166} 178}
167 179
168boost::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id, 180std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
169 ContentRecordType type) const { 181 ContentRecordType type) const {
170 if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end()) 182 if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end())
171 return meta_id.at(title_id); 183 return meta_id.at(title_id);
172 184
173 const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type); 185 const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type);
174 if (res1 != boost::none) 186 if (res1)
175 return res1; 187 return res1;
176 return CheckMapForContentRecord(meta, title_id, type); 188 return CheckMapForContentRecord(meta, title_id, type);
177} 189}
@@ -216,7 +228,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
216 228
217 if (file == nullptr) 229 if (file == nullptr)
218 continue; 230 continue;
219 const auto nca = std::make_shared<NCA>(parser(file, id)); 231 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
220 if (nca->GetStatus() != Loader::ResultStatus::Success || 232 if (nca->GetStatus() != Loader::ResultStatus::Success ||
221 nca->GetType() != NCAContentType::Meta) { 233 nca->GetType() != NCAContentType::Meta) {
222 continue; 234 continue;
@@ -274,17 +286,14 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
274 286
275VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 287VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
276 const auto id = GetNcaIDFromMetadata(title_id, type); 288 const auto id = GetNcaIDFromMetadata(title_id, type);
277 if (id == boost::none) 289 return id ? GetFileAtID(*id) : nullptr;
278 return nullptr;
279
280 return GetFileAtID(id.get());
281} 290}
282 291
283VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const { 292VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
284 return GetEntryUnparsed(entry.title_id, entry.type); 293 return GetEntryUnparsed(entry.title_id, entry.type);
285} 294}
286 295
287boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { 296std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
288 const auto meta_iter = meta.find(title_id); 297 const auto meta_iter = meta.find(title_id);
289 if (meta_iter != meta.end()) 298 if (meta_iter != meta.end())
290 return meta_iter->second.GetTitleVersion(); 299 return meta_iter->second.GetTitleVersion();
@@ -293,15 +302,12 @@ boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
293 if (yuzu_meta_iter != yuzu_meta.end()) 302 if (yuzu_meta_iter != yuzu_meta.end())
294 return yuzu_meta_iter->second.GetTitleVersion(); 303 return yuzu_meta_iter->second.GetTitleVersion();
295 304
296 return boost::none; 305 return {};
297} 306}
298 307
299VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { 308VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
300 const auto id = GetNcaIDFromMetadata(title_id, type); 309 const auto id = GetNcaIDFromMetadata(title_id, type);
301 if (id == boost::none) 310 return id ? parser(GetFileAtID(*id), *id) : nullptr;
302 return nullptr;
303
304 return parser(GetFileAtID(id.get()), id.get());
305} 311}
306 312
307VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const { 313VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
@@ -312,7 +318,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
312 const auto raw = GetEntryRaw(title_id, type); 318 const auto raw = GetEntryRaw(title_id, type);
313 if (raw == nullptr) 319 if (raw == nullptr)
314 return nullptr; 320 return nullptr;
315 return std::make_unique<NCA>(raw); 321 return std::make_unique<NCA>(raw, nullptr, 0, keys);
316} 322}
317 323
318std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 324std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
@@ -355,8 +361,8 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
355} 361}
356 362
357std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( 363std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
358 boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type, 364 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
359 boost::optional<u64> title_id) const { 365 std::optional<u64> title_id) const {
360 std::vector<RegisteredCacheEntry> out; 366 std::vector<RegisteredCacheEntry> out;
361 IterateAllMetadata<RegisteredCacheEntry>( 367 IterateAllMetadata<RegisteredCacheEntry>(
362 out, 368 out,
@@ -364,11 +370,11 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
364 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 370 return RegisteredCacheEntry{c.GetTitleID(), r.type};
365 }, 371 },
366 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 372 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
367 if (title_type != boost::none && title_type.get() != c.GetType()) 373 if (title_type && *title_type != c.GetType())
368 return false; 374 return false;
369 if (record_type != boost::none && record_type.get() != r.type) 375 if (record_type && *record_type != r.type)
370 return false; 376 return false;
371 if (title_id != boost::none && title_id.get() != c.GetTitleID()) 377 if (title_id && *title_id != c.GetTitleID())
372 return false; 378 return false;
373 return true; 379 return true;
374 }); 380 });
@@ -450,7 +456,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType
450 456
451InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, 457InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
452 bool overwrite_if_exists, 458 bool overwrite_if_exists,
453 boost::optional<NcaID> override_id) { 459 std::optional<NcaID> override_id) {
454 const auto in = nca->GetBaseFile(); 460 const auto in = nca->GetBaseFile();
455 Core::Crypto::SHA256Hash hash{}; 461 Core::Crypto::SHA256Hash hash{};
456 462
@@ -459,12 +465,12 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
459 // game is massive), we're going to cheat and only hash the first MB of the NCA. 465 // game is massive), we're going to cheat and only hash the first MB of the NCA.
460 // Also, for XCIs the NcaID matters, so if the override id isn't none, use that. 466 // Also, for XCIs the NcaID matters, so if the override id isn't none, use that.
461 NcaID id{}; 467 NcaID id{};
462 if (override_id == boost::none) { 468 if (override_id) {
469 id = *override_id;
470 } else {
463 const auto& data = in->ReadBytes(0x100000); 471 const auto& data = in->ReadBytes(0x100000);
464 mbedtls_sha256(data.data(), data.size(), hash.data(), 0); 472 mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
465 memcpy(id.data(), hash.data(), 16); 473 memcpy(id.data(), hash.data(), 16);
466 } else {
467 id = override_id.get();
468 } 474 }
469 475
470 std::string path = GetRelativePathFromNcaID(id, false, true); 476 std::string path = GetRelativePathFromNcaID(id, false, true);
@@ -534,14 +540,14 @@ bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
534 return HasEntry(entry.title_id, entry.type); 540 return HasEntry(entry.title_id, entry.type);
535} 541}
536 542
537boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { 543std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
538 for (const auto& c : caches) { 544 for (const auto& c : caches) {
539 const auto res = c->GetEntryVersion(title_id); 545 const auto res = c->GetEntryVersion(title_id);
540 if (res != boost::none) 546 if (res)
541 return res; 547 return res;
542 } 548 }
543 549
544 return boost::none; 550 return {};
545} 551}
546 552
547VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 553VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
@@ -593,12 +599,15 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
593 }, 599 },
594 [](const CNMT& c, const ContentRecord& r) { return true; }); 600 [](const CNMT& c, const ContentRecord& r) { return true; });
595 } 601 }
602
603 std::sort(out.begin(), out.end());
604 out.erase(std::unique(out.begin(), out.end()), out.end());
596 return out; 605 return out;
597} 606}
598 607
599std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( 608std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
600 boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type, 609 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
601 boost::optional<u64> title_id) const { 610 std::optional<u64> title_id) const {
602 std::vector<RegisteredCacheEntry> out; 611 std::vector<RegisteredCacheEntry> out;
603 for (const auto& c : caches) { 612 for (const auto& c : caches) {
604 c->IterateAllMetadata<RegisteredCacheEntry>( 613 c->IterateAllMetadata<RegisteredCacheEntry>(
@@ -607,15 +616,18 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
607 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 616 return RegisteredCacheEntry{c.GetTitleID(), r.type};
608 }, 617 },
609 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 618 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
610 if (title_type != boost::none && title_type.get() != c.GetType()) 619 if (title_type && *title_type != c.GetType())
611 return false; 620 return false;
612 if (record_type != boost::none && record_type.get() != r.type) 621 if (record_type && *record_type != r.type)
613 return false; 622 return false;
614 if (title_id != boost::none && title_id.get() != c.GetTitleID()) 623 if (title_id && *title_id != c.GetTitleID())
615 return false; 624 return false;
616 return true; 625 return true;
617 }); 626 });
618 } 627 }
628
629 std::sort(out.begin(), out.end());
630 out.erase(std::unique(out.begin(), out.end()), out.end());
619 return out; 631 return out;
620} 632}
621} // namespace FileSys 633} // namespace FileSys
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 5ddacba47..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 {
@@ -50,6 +51,10 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
50// boost flat_map requires operator< for O(log(n)) lookups. 51// boost flat_map requires operator< for O(log(n)) lookups.
51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 52bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
52 53
54// std unique requires operator== to identify duplicates.
55bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
56bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
57
53/* 58/*
54 * A class that catalogues NCAs in the registered directory structure. 59 * A class that catalogues NCAs in the registered directory structure.
55 * Nintendo's registered format follows this structure: 60 * Nintendo's registered format follows this structure:
@@ -60,8 +65,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
60 * | 00 65 * | 00
61 * | 01 <- Actual content split along 4GB boundaries. (optional) 66 * | 01 <- Actual content split along 4GB boundaries. (optional)
62 * 67 *
63 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient when 68 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
64 * 4GB splitting can be ignored.) 69 * when 4GB splitting can be ignored.)
65 */ 70 */
66class RegisteredCache { 71class RegisteredCache {
67 friend class RegisteredCacheUnion; 72 friend class RegisteredCacheUnion;
@@ -80,7 +85,7 @@ public:
80 bool HasEntry(u64 title_id, ContentRecordType type) const; 85 bool HasEntry(u64 title_id, ContentRecordType type) const;
81 bool HasEntry(RegisteredCacheEntry entry) const; 86 bool HasEntry(RegisteredCacheEntry entry) const;
82 87
83 boost::optional<u32> GetEntryVersion(u64 title_id) const; 88 std::optional<u32> GetEntryVersion(u64 title_id) const;
84 89
85 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 90 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
86 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; 91 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -92,11 +97,10 @@ public:
92 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 97 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
93 98
94 std::vector<RegisteredCacheEntry> ListEntries() const; 99 std::vector<RegisteredCacheEntry> ListEntries() const;
95 // If a parameter is not boost::none, it will be filtered for from all entries. 100 // If a parameter is not std::nullopt, it will be filtered for from all entries.
96 std::vector<RegisteredCacheEntry> ListEntriesFilter( 101 std::vector<RegisteredCacheEntry> ListEntriesFilter(
97 boost::optional<TitleType> title_type = boost::none, 102 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
98 boost::optional<ContentRecordType> record_type = boost::none, 103 std::optional<u64> title_id = {}) const;
99 boost::optional<u64> title_id = boost::none) const;
100 104
101 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 105 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
102 // there is a meta NCA and all of them are accessible. 106 // there is a meta NCA and all of them are accessible.
@@ -121,16 +125,17 @@ private:
121 std::vector<NcaID> AccumulateFiles() const; 125 std::vector<NcaID> AccumulateFiles() const;
122 void ProcessFiles(const std::vector<NcaID>& ids); 126 void ProcessFiles(const std::vector<NcaID>& ids);
123 void AccumulateYuzuMeta(); 127 void AccumulateYuzuMeta();
124 boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; 128 std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
125 VirtualFile GetFileAtID(NcaID id) const; 129 VirtualFile GetFileAtID(NcaID id) const;
126 VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const; 130 VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
127 InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, 131 InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
128 bool overwrite_if_exists, 132 bool overwrite_if_exists, std::optional<NcaID> override_id = {});
129 boost::optional<NcaID> override_id = boost::none);
130 bool RawInstallYuzuMeta(const CNMT& cnmt); 133 bool RawInstallYuzuMeta(const CNMT& cnmt);
131 134
132 VirtualDir dir; 135 VirtualDir dir;
133 RegisteredCacheParsingFunction parser; 136 RegisteredCacheParsingFunction parser;
137 Core::Crypto::KeyManager keys;
138
134 // maps tid -> NcaID of meta 139 // maps tid -> NcaID of meta
135 boost::container::flat_map<u64, NcaID> meta_id; 140 boost::container::flat_map<u64, NcaID> meta_id;
136 // maps tid -> meta 141 // maps tid -> meta
@@ -149,7 +154,7 @@ public:
149 bool HasEntry(u64 title_id, ContentRecordType type) const; 154 bool HasEntry(u64 title_id, ContentRecordType type) const;
150 bool HasEntry(RegisteredCacheEntry entry) const; 155 bool HasEntry(RegisteredCacheEntry entry) const;
151 156
152 boost::optional<u32> GetEntryVersion(u64 title_id) const; 157 std::optional<u32> GetEntryVersion(u64 title_id) const;
153 158
154 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 159 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
155 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; 160 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -161,11 +166,10 @@ public:
161 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 166 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162 167
163 std::vector<RegisteredCacheEntry> ListEntries() const; 168 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries. 169 // If a parameter is not std::nullopt, it will be filtered for from all entries.
165 std::vector<RegisteredCacheEntry> ListEntriesFilter( 170 std::vector<RegisteredCacheEntry> ListEntriesFilter(
166 boost::optional<TitleType> title_type = boost::none, 171 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
167 boost::optional<ContentRecordType> record_type = boost::none, 172 std::optional<u64> title_id = {}) const;
168 boost::optional<u64> title_id = boost::none) const;
169 173
170private: 174private:
171 std::vector<RegisteredCache*> caches; 175 std::vector<RegisteredCache*> caches;
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 ab5dc900c..e1a4210db 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -205,10 +205,6 @@ VirtualDir NSP::GetParentDirectory() const {
205 return file->GetContainingDirectory(); 205 return file->GetContainingDirectory();
206} 206}
207 207
208bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
209 return false;
210}
211
212void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { 208void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
213 exefs = pfs; 209 exefs = pfs;
214 210
@@ -256,7 +252,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
256 continue; 252 continue;
257 } 253 }
258 254
259 auto next_nca = std::make_shared<NCA>(next_file); 255 auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
260 if (next_nca->GetType() == NCAContentType::Program) 256 if (next_nca->GetType() == NCAContentType::Program)
261 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
262 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 da3dc5e9f..9a28ed5bb 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -55,9 +55,6 @@ public:
55 55
56 VirtualDir GetParentDirectory() const override; 56 VirtualDir GetParentDirectory() const override;
57 57
58protected:
59 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
60
61private: 58private:
62 void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); 59 void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
63 void ReadNCAs(const std::vector<VirtualFile>& files); 60 void ReadNCAs(const std::vector<VirtualFile>& files);
@@ -73,6 +70,8 @@ private:
73 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; 70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
74 std::vector<VirtualFile> ticket_files; 71 std::vector<VirtualFile> ticket_files;
75 72
73 Core::Crypto::KeyManager keys;
74
76 VirtualFile romfs; 75 VirtualFile romfs;
77 VirtualDir exefs; 76 VirtualDir exefs;
78}; 77};
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index bfe50da73..7b584de7f 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -167,13 +167,13 @@ std::string VfsFile::GetExtension() const {
167 167
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
169 169
170boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const { 170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
171 u8 out{}; 171 u8 out{};
172 std::size_t size = Read(&out, 1, offset); 172 std::size_t size = Read(&out, 1, offset);
173 if (size == 1) 173 if (size == 1)
174 return out; 174 return out;
175 175
176 return boost::none; 176 return {};
177} 177}
178 178
179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { 179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
@@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
472 std::vector<u8> temp(std::min(block_size, src->GetSize())); 472 std::vector<u8> temp(std::min(block_size, src->GetSize()));
473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) { 473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
474 const auto read = std::min(block_size, src->GetSize() - i); 474 const auto read = std::min(block_size, src->GetSize() - i);
475 const auto block = src->Read(temp.data(), read, i);
476 475
477 if (dest->Write(temp.data(), read, i) != read) 476 if (src->Read(temp.data(), read, i) != read) {
478 return false; 477 return false;
478 }
479
480 if (dest->Write(temp.data(), read, i) != read) {
481 return false;
482 }
479 } 483 }
480 484
481 return true; 485 return true;
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 7f0d520ca..002f99d4e 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -4,13 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
7#include <map> 8#include <map>
8#include <memory> 9#include <memory>
10#include <optional>
9#include <string> 11#include <string>
10#include <string_view> 12#include <string_view>
11#include <type_traits> 13#include <type_traits>
12#include <vector> 14#include <vector>
13#include <boost/optional.hpp> 15
14#include "common/common_types.h" 16#include "common/common_types.h"
15#include "core/file_sys/vfs_types.h" 17#include "core/file_sys/vfs_types.h"
16 18
@@ -103,8 +105,8 @@ public:
103 // into file. Returns number of bytes successfully written. 105 // into file. Returns number of bytes successfully written.
104 virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; 106 virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
105 107
106 // Reads exactly one byte at the offset provided, returning boost::none on error. 108 // Reads exactly one byte at the offset provided, returning std::nullopt on error.
107 virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const; 109 virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
108 // Reads size bytes starting at offset in file into a vector. 110 // Reads size bytes starting at offset in file into a vector.
109 virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; 111 virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
110 // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), 112 // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
@@ -262,36 +264,8 @@ public:
262 // item name -> type. 264 // item name -> type.
263 virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; 265 virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
264 266
265 // Interprets the file with name file instead as a directory of type directory.
266 // The directory must have a constructor that takes a single argument of type
267 // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
268 // subdirectory in one call.
269 template <typename Directory>
270 bool InterpretAsDirectory(std::string_view file) {
271 auto file_p = GetFile(file);
272
273 if (file_p == nullptr) {
274 return false;
275 }
276
277 return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
278 }
279
280 bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
281 const std::string& file) {
282 auto file_p = GetFile(file);
283 if (file_p == nullptr)
284 return false;
285 return ReplaceFileWithSubdirectory(file_p, function(file_p));
286 }
287
288 // Returns the full path of this directory as a string, recursively 267 // Returns the full path of this directory as a string, recursively
289 virtual std::string GetFullPath() const; 268 virtual std::string GetFullPath() const;
290
291protected:
292 // Backend for InterpretAsDirectory.
293 // Removes all references to file and adds a reference to dir in the directory's implementation.
294 virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
295}; 269};
296 270
297// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work 271// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index bfee01725..338e398da 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -126,7 +126,4 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) {
126 return true; 126 return true;
127} 127}
128 128
129bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
130 return false;
131}
132} // namespace FileSys 129} // namespace FileSys
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
index d85310f57..8a25c3428 100644
--- a/src/core/file_sys/vfs_layered.h
+++ b/src/core/file_sys/vfs_layered.h
@@ -39,9 +39,6 @@ public:
39 bool DeleteFile(std::string_view name) override; 39 bool DeleteFile(std::string_view name) override;
40 bool Rename(std::string_view name) override; 40 bool Rename(std::string_view name) override;
41 41
42protected:
43 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
44
45private: 42private:
46 std::vector<VirtualDir> dirs; 43 std::vector<VirtualDir> dirs;
47 std::string name; 44 std::string name;
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index a4c6719a0..c96f88488 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -57,11 +57,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
57 return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); 57 return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
58} 58}
59 59
60boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { 60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
61 if (r_offset < size) 61 if (r_offset < size)
62 return file->ReadByte(offset + r_offset); 62 return file->ReadByte(offset + r_offset);
63 63
64 return boost::none; 64 return {};
65} 65}
66 66
67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { 67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 8062702a7..f7b7a3256 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -29,7 +29,7 @@ public:
29 bool IsReadable() const override; 29 bool IsReadable() const override;
30 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; 30 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
31 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; 31 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
32 boost::optional<u8> ReadByte(std::size_t offset) const override; 32 std::optional<u8> ReadByte(std::size_t offset) const override;
33 std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; 33 std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
34 std::vector<u8> ReadAllBytes() const override; 34 std::vector<u8> ReadAllBytes() const override;
35 bool WriteByte(u8 data, std::size_t offset) override; 35 bool WriteByte(u8 data, std::size_t offset) override;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 9defad04c..e21300a7c 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -430,7 +430,4 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
430 return out; 430 return out;
431} 431}
432 432
433bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
434 return false;
435}
436} // namespace FileSys 433} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 5b61db90d..a0a857a31 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -100,9 +100,6 @@ public:
100 std::string GetFullPath() const override; 100 std::string GetFullPath() const override;
101 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; 101 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
102 102
103protected:
104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
105
106private: 103private:
107 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); 104 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
108 105
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 44fab51d1..9f5a90b1b 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -53,10 +53,10 @@ public:
53 return 0; 53 return 0;
54 } 54 }
55 55
56 boost::optional<u8> ReadByte(std::size_t offset) const override { 56 std::optional<u8> ReadByte(std::size_t offset) const override {
57 if (offset < size) 57 if (offset < size)
58 return value; 58 return value;
59 return boost::none; 59 return {};
60 } 60 }
61 61
62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { 62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 389c7e003..808f31e81 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -132,11 +132,4 @@ void VectorVfsDirectory::AddFile(VirtualFile file) {
132void VectorVfsDirectory::AddDirectory(VirtualDir dir) { 132void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
133 dirs.push_back(std::move(dir)); 133 dirs.push_back(std::move(dir));
134} 134}
135
136bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
137 if (!DeleteFile(file->GetName()))
138 return false;
139 dirs.emplace_back(std::move(dir));
140 return true;
141}
142} // namespace FileSys 135} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index 48a414c98..3e3f790c3 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -57,9 +57,6 @@ public:
57 virtual void AddFile(VirtualFile file); 57 virtual void AddFile(VirtualFile file);
58 virtual void AddDirectory(VirtualDir dir); 58 virtual void AddDirectory(VirtualDir dir);
59 59
60protected:
61 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
62
63private: 60private:
64 std::vector<VirtualFile> files; 61 std::vector<VirtualFile> files;
65 std::vector<VirtualDir> dirs; 62 std::vector<VirtualDir> dirs;
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index b2b164368..eec51c64e 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -163,7 +163,4 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
163 return file->GetContainingDirectory(); 163 return file->GetContainingDirectory();
164} 164}
165 165
166bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
167 return false;
168}
169} // namespace FileSys 166} // namespace FileSys
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 8fedd8585..7704dee90 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -51,9 +51,6 @@ public:
51 51
52 std::shared_ptr<VfsDirectory> GetParentDirectory() const override; 52 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
53 53
54protected:
55 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
56
57private: 54private:
58 Loader::ResultStatus Parse(std::string_view path); 55 Loader::ResultStatus Parse(std::string_view path);
59 56
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 419f45896..ed84197b3 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -14,11 +14,6 @@ namespace IPC {
14/// Size of the command buffer area, in 32-bit words. 14/// Size of the command buffer area, in 32-bit words.
15constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); 15constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
16 16
17// These errors are commonly returned by invalid IPC translations, so alias them here for
18// convenience.
19// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
20constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
21
22enum class ControlCommand : u32 { 17enum class ControlCommand : u32 {
23 ConvertSessionToDomain = 0, 18 ConvertSessionToDomain = 0,
24 ConvertDomainToSession = 1, 19 ConvertDomainToSession = 1,
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index a4bfe2eb0..0a7142ada 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -117,8 +117,7 @@ public:
117 117
118 AlignWithPadding(); 118 AlignWithPadding();
119 119
120 const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr}; 120 if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) {
121 if (context.Session()->IsDomain() && request_has_domain_header) {
122 IPC::DomainMessageHeader domain_header{}; 121 IPC::DomainMessageHeader domain_header{};
123 domain_header.num_objects = num_domain_objects; 122 domain_header.num_objects = num_domain_objects;
124 PushRaw(domain_header); 123 PushRaw(domain_header);
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 885259618..8b58d701d 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -8,71 +8,28 @@
8 8
9namespace Kernel { 9namespace Kernel {
10 10
11namespace ErrCodes { 11// Confirmed Switch kernel error codes
12enum { 12
13 // TODO(Subv): Remove these 3DS OS error codes. 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
14 SessionClosedByRemote = 26, 14constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
15 NoPendingSessions = 35, 15constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
16 InvalidBufferDescriptor = 48, 16constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
17 17constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
18 // Confirmed Switch OS error codes 18constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
19 MaxConnectionsReached = 7, 19constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
20 InvalidSize = 101, 20constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
21 InvalidAddress = 102, 21constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
22 HandleTableFull = 105, 22constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
23 InvalidMemoryState = 106, 23constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
24 InvalidMemoryPermissions = 108, 24constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
25 InvalidMemoryRange = 110, 25constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
26 InvalidThreadPriority = 112, 26constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
27 InvalidProcessorId = 113, 27constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
28 InvalidHandle = 114, 28constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
29 InvalidCombination = 116, 29constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
30 Timeout = 117, 30constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
31 SynchronizationCanceled = 118, 31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
32 TooLarge = 119, 32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
33 InvalidEnumValue = 120, 33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
34 NoSuchEntry = 121,
35 AlreadyRegistered = 122,
36 InvalidState = 125,
37 ResourceLimitExceeded = 132,
38};
39}
40
41// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
42// double check that the code matches before re-using the constant.
43
44// TODO(bunnei): Replace -1 with correct errors for Switch OS
45constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
46constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
47constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
48constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
49 ErrCodes::MaxConnectionsReached);
50constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
51constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
52constexpr ResultCode ERR_INVALID_COMBINATION(-1);
53constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
54 ErrCodes::InvalidCombination);
55constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
56constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
57constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
58constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
59 ErrCodes::InvalidMemoryPermissions);
60constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
61constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
62constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
63constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
64constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
65constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
66constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
67 ErrCodes::InvalidThreadPriority);
68constexpr ResultCode ERR_INVALID_POINTER(-1);
69constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
70constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
71/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
72constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
73constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
74constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
75/// Returned when Accept() is called on a port with no sessions to be accepted.
76constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
77 34
78} // namespace Kernel 35} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index f01491daa..a38e34b74 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -161,8 +161,12 @@ public:
161 return buffer_c_desciptors; 161 return buffer_c_desciptors;
162 } 162 }
163 163
164 const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { 164 const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
165 return domain_message_header; 165 return domain_message_header.get();
166 }
167
168 bool HasDomainMessageHeader() const {
169 return domain_message_header != nullptr;
166 } 170 }
167 171
168 /// Helper function to read a buffer using the appropriate buffer descriptor 172 /// Helper function to read a buffer using the appropriate buffer descriptor
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4b6b32dd5..1fd4ba5d2 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -32,7 +32,7 @@ namespace Kernel {
32 */ 32 */
33static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) { 33static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
34 const auto proper_handle = static_cast<Handle>(thread_handle); 34 const auto proper_handle = static_cast<Handle>(thread_handle);
35 auto& system = Core::System::GetInstance(); 35 const auto& system = Core::System::GetInstance();
36 36
37 // Lock the global kernel mutex when we enter the kernel HLE. 37 // Lock the global kernel mutex when we enter the kernel HLE.
38 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 38 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
@@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
90/// The timer callback event, called when a timer is fired 90/// The timer callback event, called when a timer is fired
91static void TimerCallback(u64 timer_handle, int cycles_late) { 91static void TimerCallback(u64 timer_handle, int cycles_late) {
92 const auto proper_handle = static_cast<Handle>(timer_handle); 92 const auto proper_handle = static_cast<Handle>(timer_handle);
93 auto& system = Core::System::GetInstance(); 93 const auto& system = Core::System::GetInstance();
94 SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle); 94 SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
95 95
96 if (timer == nullptr) { 96 if (timer == nullptr) {
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index dd541ffcc..0743670ad 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -6,8 +6,6 @@
6#include <utility> 6#include <utility>
7#include <vector> 7#include <vector>
8 8
9#include <boost/range/algorithm_ext/erase.hpp>
10
11#include "common/assert.h" 9#include "common/assert.h"
12#include "core/core.h" 10#include "core/core.h"
13#include "core/hle/kernel/errors.h" 11#include "core/hle/kernel/errors.h"
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 073dd5a7d..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}
@@ -232,86 +236,24 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
232 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 236 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
233 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 237 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
234 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 238 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
239
240 // Clear instruction cache in CPU JIT
241 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
242 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
243 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
244 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
235} 245}
236 246
237ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 247ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
238 if (target < vm_manager.GetHeapRegionBaseAddress() || 248 return vm_manager.HeapAllocate(target, size, perms);
239 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
240 return ERR_INVALID_ADDRESS;
241 }
242
243 if (heap_memory == nullptr) {
244 // Initialize heap
245 heap_memory = std::make_shared<std::vector<u8>>();
246 heap_start = heap_end = target;
247 } else {
248 vm_manager.UnmapRange(heap_start, heap_end - heap_start);
249 }
250
251 // If necessary, expand backing vector to cover new heap extents.
252 if (target < heap_start) {
253 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
254 heap_start = target;
255 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
256 }
257 if (target + size > heap_end) {
258 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
259 heap_end = target + size;
260 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
261 }
262 ASSERT(heap_end - heap_start == heap_memory->size());
263
264 CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
265 size, MemoryState::Heap));
266 vm_manager.Reprotect(vma, perms);
267
268 heap_used = size;
269
270 return MakeResult<VAddr>(heap_end - size);
271} 249}
272 250
273ResultCode Process::HeapFree(VAddr target, u32 size) { 251ResultCode Process::HeapFree(VAddr target, u32 size) {
274 if (target < vm_manager.GetHeapRegionBaseAddress() || 252 return vm_manager.HeapFree(target, size);
275 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
276 return ERR_INVALID_ADDRESS;
277 }
278
279 if (size == 0) {
280 return RESULT_SUCCESS;
281 }
282
283 ResultCode result = vm_manager.UnmapRange(target, size);
284 if (result.IsError())
285 return result;
286
287 heap_used -= size;
288
289 return RESULT_SUCCESS;
290} 253}
291 254
292ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 255ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
293 auto vma = vm_manager.FindVMA(src_addr); 256 return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
294
295 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
296 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
297
298 // The returned VMA might be a bigger one encompassing the desired address.
299 auto vma_offset = src_addr - vma->first;
300 ASSERT_MSG(vma_offset + size <= vma->second.size,
301 "Shared memory exceeds bounds of mapped block");
302
303 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
304 std::size_t backing_block_offset = vma->second.offset + vma_offset;
305
306 CASCADE_RESULT(auto new_vma,
307 vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
308 MemoryState::Mapped));
309 // Protect mirror with permissions from old region
310 vm_manager.Reprotect(new_vma, vma->second.permissions);
311 // Remove permissions from old region
312 vm_manager.Reprotect(vma, VMAPermission::None);
313
314 return RESULT_SUCCESS;
315} 257}
316 258
317ResultCode 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 148478488..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 {
@@ -202,6 +205,21 @@ public:
202 return is_64bit_process; 205 return is_64bit_process;
203 } 206 }
204 207
208 /// Gets the total running time of the process instance in ticks.
209 u64 GetCPUTimeTicks() const {
210 return total_process_running_time_ticks;
211 }
212
213 /// Updates the total running time, adding the given ticks to it.
214 void UpdateCPUTimeTicks(u64 ticks) {
215 total_process_running_time_ticks += ticks;
216 }
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
205 /** 223 /**
206 * Loads process-specifics configuration info with metadata provided 224 * Loads process-specifics configuration info with metadata provided
207 * by an executable. 225 * by an executable.
@@ -241,7 +259,8 @@ public:
241 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 259 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
242 ResultCode HeapFree(VAddr target, u32 size); 260 ResultCode HeapFree(VAddr target, u32 size);
243 261
244 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);
245 264
246 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); 265 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
247 266
@@ -282,17 +301,6 @@ private:
282 u32 allowed_thread_priority_mask = 0xFFFFFFFF; 301 u32 allowed_thread_priority_mask = 0xFFFFFFFF;
283 u32 is_virtual_address_memory_enabled = 0; 302 u32 is_virtual_address_memory_enabled = 0;
284 303
285 // Memory used to back the allocations in the regular heap. A single vector is used to cover
286 // the entire virtual address space extents that bound the allocations, including any holes.
287 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
288 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
289 std::shared_ptr<std::vector<u8>> heap_memory;
290
291 // The left/right bounds of the address space covered by heap_memory.
292 VAddr heap_start = 0;
293 VAddr heap_end = 0;
294 u64 heap_used = 0;
295
296 /// The Thread Local Storage area is allocated as processes create threads, 304 /// The Thread Local Storage area is allocated as processes create threads,
297 /// 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
298 /// 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
@@ -305,9 +313,15 @@ private:
305 /// specified by metadata provided to the process during loading. 313 /// specified by metadata provided to the process during loading.
306 bool is_64bit_process = true; 314 bool is_64bit_process = true;
307 315
316 /// Total running time for the process in ticks.
317 u64 total_process_running_time_ticks = 0;
318
308 /// Per-process handle table for storing created object handles in. 319 /// Per-process handle table for storing created object handles in.
309 HandleTable handle_table; 320 HandleTable handle_table;
310 321
322 /// Random values for svcGetInfo RandomEntropy
323 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
324
311 std::string name; 325 std::string name;
312}; 326};
313 327
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 1342c597e..5a5f4cef1 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/core_timing.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/scheduler.h" 15#include "core/hle/kernel/scheduler.h"
@@ -34,6 +35,10 @@ Thread* Scheduler::GetCurrentThread() const {
34 return current_thread.get(); 35 return current_thread.get();
35} 36}
36 37
38u64 Scheduler::GetLastContextSwitchTicks() const {
39 return last_context_switch_time;
40}
41
37Thread* Scheduler::PopNextReadyThread() { 42Thread* Scheduler::PopNextReadyThread() {
38 Thread* next = nullptr; 43 Thread* next = nullptr;
39 Thread* thread = GetCurrentThread(); 44 Thread* thread = GetCurrentThread();
@@ -54,7 +59,10 @@ Thread* Scheduler::PopNextReadyThread() {
54} 59}
55 60
56void Scheduler::SwitchContext(Thread* new_thread) { 61void Scheduler::SwitchContext(Thread* new_thread) {
57 Thread* previous_thread = GetCurrentThread(); 62 Thread* const previous_thread = GetCurrentThread();
63 Process* const previous_process = Core::CurrentProcess();
64
65 UpdateLastContextSwitchTime(previous_thread, previous_process);
58 66
59 // Save context for previous thread 67 // Save context for previous thread
60 if (previous_thread) { 68 if (previous_thread) {
@@ -78,8 +86,6 @@ void Scheduler::SwitchContext(Thread* new_thread) {
78 // Cancel any outstanding wakeup events for this thread 86 // Cancel any outstanding wakeup events for this thread
79 new_thread->CancelWakeupTimer(); 87 new_thread->CancelWakeupTimer();
80 88
81 auto* const previous_process = Core::CurrentProcess();
82
83 current_thread = new_thread; 89 current_thread = new_thread;
84 90
85 ready_queue.remove(new_thread->GetPriority(), new_thread); 91 ready_queue.remove(new_thread->GetPriority(), new_thread);
@@ -102,6 +108,22 @@ void Scheduler::SwitchContext(Thread* new_thread) {
102 } 108 }
103} 109}
104 110
111void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
112 const u64 prev_switch_ticks = last_context_switch_time;
113 const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
114 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
115
116 if (thread != nullptr) {
117 thread->UpdateCPUTimeTicks(update_ticks);
118 }
119
120 if (process != nullptr) {
121 process->UpdateCPUTimeTicks(update_ticks);
122 }
123
124 last_context_switch_time = most_recent_switch_ticks;
125}
126
105void Scheduler::Reschedule() { 127void Scheduler::Reschedule() {
106 std::lock_guard<std::mutex> lock(scheduler_mutex); 128 std::lock_guard<std::mutex> lock(scheduler_mutex);
107 129
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 2c94641ec..c63032b7d 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -17,6 +17,8 @@ class ARM_Interface;
17 17
18namespace Kernel { 18namespace Kernel {
19 19
20class Process;
21
20class Scheduler final { 22class Scheduler final {
21public: 23public:
22 explicit Scheduler(Core::ARM_Interface& cpu_core); 24 explicit Scheduler(Core::ARM_Interface& cpu_core);
@@ -31,6 +33,9 @@ public:
31 /// Gets the current running thread 33 /// Gets the current running thread
32 Thread* GetCurrentThread() const; 34 Thread* GetCurrentThread() const;
33 35
36 /// Gets the timestamp for the last context switch in ticks.
37 u64 GetLastContextSwitchTicks() const;
38
34 /// Adds a new thread to the scheduler 39 /// Adds a new thread to the scheduler
35 void AddThread(SharedPtr<Thread> thread, u32 priority); 40 void AddThread(SharedPtr<Thread> thread, u32 priority);
36 41
@@ -64,6 +69,19 @@ private:
64 */ 69 */
65 void SwitchContext(Thread* new_thread); 70 void SwitchContext(Thread* new_thread);
66 71
72 /**
73 * Called on every context switch to update the internal timestamp
74 * This also updates the running time ticks for the given thread and
75 * process using the following difference:
76 *
77 * ticks += most_recent_ticks - last_context_switch_ticks
78 *
79 * The internal tick timestamp for the scheduler is simply the
80 * most recent tick count retrieved. No special arithmetic is
81 * applied to it.
82 */
83 void UpdateLastContextSwitchTime(Thread* thread, Process* process);
84
67 /// Lists all thread ids that aren't deleted/etc. 85 /// Lists all thread ids that aren't deleted/etc.
68 std::vector<SharedPtr<Thread>> thread_list; 86 std::vector<SharedPtr<Thread>> thread_list;
69 87
@@ -73,6 +91,7 @@ private:
73 SharedPtr<Thread> current_thread = nullptr; 91 SharedPtr<Thread> current_thread = nullptr;
74 92
75 Core::ARM_Interface& cpu_core; 93 Core::ARM_Interface& cpu_core;
94 u64 last_context_switch_time = 0;
76 95
77 static std::mutex scheduler_mutex; 96 static std::mutex scheduler_mutex;
78}; 97};
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 3792e3e18..d6ceeb2da 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
18 18
19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { 19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
20 if (pending_sessions.empty()) { 20 if (pending_sessions.empty()) {
21 return ERR_NO_PENDING_SESSIONS; 21 return ERR_NOT_FOUND;
22 } 22 }
23 23
24 auto session = std::move(pending_sessions.back()); 24 auto session = std::move(pending_sessions.back());
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
28 28
29bool ServerPort::ShouldWait(Thread* thread) const { 29bool ServerPort::ShouldWait(Thread* thread) const {
30 // If there are no pending sessions, we wait until a new one is added. 30 // If there are no pending sessions, we wait until a new one is added.
31 return pending_sessions.size() == 0; 31 return pending_sessions.empty();
32} 32}
33 33
34void ServerPort::Acquire(Thread* thread) { 34void ServerPort::Acquire(Thread* thread) {
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 5fc320403..80897f3a4 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) {
63} 63}
64 64
65ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { 65ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
66 auto& domain_message_header = context.GetDomainMessageHeader(); 66 auto* const domain_message_header = context.GetDomainMessageHeader();
67 if (domain_message_header) { 67 if (domain_message_header) {
68 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs 68 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
69 context.SetDomainRequestHandlers(domain_request_handlers); 69 context.SetDomainRequestHandlers(domain_request_handlers);
@@ -111,7 +111,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
111 111
112 ResultCode result = RESULT_SUCCESS; 112 ResultCode result = RESULT_SUCCESS;
113 // If the session has been converted to a domain, handle the domain request 113 // If the session has been converted to a domain, handle the domain request
114 if (IsDomain() && context.GetDomainMessageHeader()) { 114 if (IsDomain() && context.HasDomainMessageHeader()) {
115 result = HandleDomainSyncRequest(context); 115 result = HandleDomainSyncRequest(context);
116 // If there is no domain header, the regular session handler is used 116 // If there is no domain header, the regular session handler is used
117 } else if (hle_handler != nullptr) { 117 } else if (hle_handler != nullptr) {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index d061e6155..a016a86b6 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
80 80
81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, 81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
82 MemoryPermission other_permissions) { 82 MemoryPermission other_permissions) {
83 83 const MemoryPermission own_other_permissions =
84 MemoryPermission own_other_permissions =
85 target_process == owner_process ? this->permissions : this->other_permissions; 84 target_process == owner_process ? this->permissions : this->other_permissions;
86 85
87 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare 86 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
88 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { 87 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
89 return ERR_INVALID_COMBINATION; 88 return ERR_INVALID_MEMORY_PERMISSIONS;
90 } 89 }
91 90
92 // Error out if the requested permissions don't match what the creator process allows. 91 // Error out if the requested permissions don't match what the creator process allows.
93 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { 92 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
94 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", 93 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
95 GetObjectId(), address, name); 94 GetObjectId(), address, name);
96 return ERR_INVALID_COMBINATION; 95 return ERR_INVALID_MEMORY_PERMISSIONS;
97 } 96 }
98 97
99 // Error out if the provided permissions are not compatible with what the creator process needs. 98 // Error out if the provided permissions are not compatible with what the creator process needs.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 67ea67666..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}
@@ -375,9 +418,19 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
375 return Mutex::Release(mutex_addr); 418 return Mutex::Release(mutex_addr);
376} 419}
377 420
421enum class BreakType : u32 {
422 Panic = 0,
423 AssertionFailed = 1,
424 PreNROLoad = 3,
425 PostNROLoad = 4,
426 PreNROUnload = 5,
427 PostNROUnload = 6,
428};
429
378struct BreakReason { 430struct BreakReason {
379 union { 431 union {
380 u32 raw; 432 u32 raw;
433 BitField<0, 30, BreakType> break_type;
381 BitField<31, 1, u32> signal_debugger; 434 BitField<31, 1, u32> signal_debugger;
382 }; 435 };
383}; 436};
@@ -385,16 +438,80 @@ struct BreakReason {
385/// Break program execution 438/// Break program execution
386static void Break(u32 reason, u64 info1, u64 info2) { 439static void Break(u32 reason, u64 info1, u64 info2) {
387 BreakReason break_reason{reason}; 440 BreakReason break_reason{reason};
388 if (break_reason.signal_debugger) { 441 bool has_dumped_buffer{};
389 LOG_ERROR( 442
443 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
444 if (sz == 0 || addr == 0 || has_dumped_buffer) {
445 return;
446 }
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 };
466 switch (break_reason.break_type) {
467 case BreakType::Panic:
468 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
469 info1, info2);
470 handle_debug_buffer(info1, info2);
471 break;
472 case BreakType::AssertionFailed:
473 LOG_CRITICAL(Debug_Emulated,
474 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
475 info1, info2);
476 handle_debug_buffer(info1, info2);
477 break;
478 case BreakType::PreNROLoad:
479 LOG_WARNING(
390 Debug_Emulated, 480 Debug_Emulated,
391 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 481 "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
392 reason, info1, info2); 482 info1, info2);
393 } else { 483 break;
484 case BreakType::PostNROLoad:
485 LOG_WARNING(Debug_Emulated,
486 "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
487 info2);
488 break;
489 case BreakType::PreNROUnload:
490 LOG_WARNING(
491 Debug_Emulated,
492 "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
493 info1, info2);
494 break;
495 case BreakType::PostNROUnload:
496 LOG_WARNING(Debug_Emulated,
497 "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
498 info2);
499 break;
500 default:
501 LOG_WARNING(
502 Debug_Emulated,
503 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
504 static_cast<u32>(break_reason.break_type.Value()), info1, info2);
505 handle_debug_buffer(info1, info2);
506 break;
507 }
508
509 if (!break_reason.signal_debugger) {
394 LOG_CRITICAL( 510 LOG_CRITICAL(
395 Debug_Emulated, 511 Debug_Emulated,
396 "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}",
397 reason, info1, info2); 513 reason, info1, info2);
514 handle_debug_buffer(info1, info2);
398 ASSERT(false); 515 ASSERT(false);
399 516
400 Core::CurrentProcess()->PrepareForTermination(); 517 Core::CurrentProcess()->PrepareForTermination();
@@ -421,6 +538,37 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
421 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 538 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
422 info_sub_id, handle); 539 info_sub_id, handle);
423 540
541 enum class GetInfoType : u64 {
542 // 1.0.0+
543 AllowedCpuIdBitmask = 0,
544 AllowedThreadPrioBitmask = 1,
545 MapRegionBaseAddr = 2,
546 MapRegionSize = 3,
547 HeapRegionBaseAddr = 4,
548 HeapRegionSize = 5,
549 TotalMemoryUsage = 6,
550 TotalHeapUsage = 7,
551 IsCurrentProcessBeingDebugged = 8,
552 ResourceHandleLimit = 9,
553 IdleTickCount = 10,
554 RandomEntropy = 11,
555 PerformanceCounter = 0xF0000002,
556 // 2.0.0+
557 ASLRRegionBaseAddr = 12,
558 ASLRRegionSize = 13,
559 NewMapRegionBaseAddr = 14,
560 NewMapRegionSize = 15,
561 // 3.0.0+
562 IsVirtualAddressMemoryEnabled = 16,
563 PersonalMmHeapUsage = 17,
564 TitleId = 18,
565 // 4.0.0+
566 PrivilegedProcessId = 19,
567 // 5.0.0+
568 UserExceptionContextAddr = 20,
569 ThreadTickCount = 0xF0000002,
570 };
571
424 const auto* current_process = Core::CurrentProcess(); 572 const auto* current_process = Core::CurrentProcess();
425 const auto& vm_manager = current_process->VMManager(); 573 const auto& vm_manager = current_process->VMManager();
426 574
@@ -453,7 +601,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
453 *result = 0; 601 *result = 0;
454 break; 602 break;
455 case GetInfoType::RandomEntropy: 603 case GetInfoType::RandomEntropy:
456 *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;
457 break; 614 break;
458 case GetInfoType::ASLRRegionBaseAddr: 615 case GetInfoType::ASLRRegionBaseAddr:
459 *result = vm_manager.GetASLRRegionBaseAddress(); 616 *result = vm_manager.GetASLRRegionBaseAddress();
@@ -483,6 +640,36 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
483 "(STUBBED) Attempted to query user exception context address, returned 0"); 640 "(STUBBED) Attempted to query user exception context address, returned 0");
484 *result = 0; 641 *result = 0;
485 break; 642 break;
643 case GetInfoType::ThreadTickCount: {
644 constexpr u64 num_cpus = 4;
645 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
646 return ERR_INVALID_COMBINATION;
647 }
648
649 const auto thread =
650 current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
651 if (!thread) {
652 return ERR_INVALID_HANDLE;
653 }
654
655 const auto& system = Core::System::GetInstance();
656 const auto& scheduler = system.CurrentScheduler();
657 const auto* const current_thread = scheduler.GetCurrentThread();
658 const bool same_thread = current_thread == thread;
659
660 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
661 u64 out_ticks = 0;
662 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
663 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
664
665 out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
666 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
667 out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
668 }
669
670 *result = out_ticks;
671 break;
672 }
486 default: 673 default:
487 UNIMPLEMENTED(); 674 UNIMPLEMENTED();
488 } 675 }
@@ -548,16 +735,17 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
548 } 735 }
549 736
550 const auto* const current_process = Core::CurrentProcess(); 737 const auto* const current_process = Core::CurrentProcess();
551 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
552 if (!thread) {
553 return ERR_INVALID_HANDLE;
554 }
555 738
556 // Note: The kernel uses the current process's resource limit instead of 739 // Note: The kernel uses the current process's resource limit instead of
557 // the one from the thread owner's resource limit. 740 // the one from the thread owner's resource limit.
558 const ResourceLimit& resource_limit = current_process->GetResourceLimit(); 741 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
559 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 742 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
560 return ERR_NOT_AUTHORIZED; 743 return ERR_INVALID_THREAD_PRIORITY;
744 }
745
746 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
747 if (!thread) {
748 return ERR_INVALID_HANDLE;
561 } 749 }
562 750
563 thread->SetPriority(priority); 751 thread->SetPriority(priority);
@@ -699,7 +887,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
699 auto* const current_process = Core::CurrentProcess(); 887 auto* const current_process = Core::CurrentProcess();
700 const ResourceLimit& resource_limit = current_process->GetResourceLimit(); 888 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
701 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 889 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
702 return ERR_NOT_AUTHORIZED; 890 return ERR_INVALID_THREAD_PRIORITY;
703 } 891 }
704 892
705 if (processor_id == THREADPROCESSORID_DEFAULT) { 893 if (processor_id == THREADPROCESSORID_DEFAULT) {
@@ -993,7 +1181,7 @@ static ResultCode CloseHandle(Handle handle) {
993 1181
994/// Reset an event 1182/// Reset an event
995static ResultCode ResetSignal(Handle handle) { 1183static ResultCode ResetSignal(Handle handle) {
996 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1184 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
997 1185
998 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1186 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
999 auto event = handle_table.Get<Event>(handle); 1187 auto event = handle_table.Get<Event>(handle);
@@ -1048,7 +1236,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1048 } 1236 }
1049 1237
1050 if (mask == 0) { 1238 if (mask == 0) {
1051 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1239 return ERR_INVALID_COMBINATION;
1052 } 1240 }
1053 1241
1054 /// 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.
@@ -1057,12 +1245,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1057 if (core == OnlyChangeMask) { 1245 if (core == OnlyChangeMask) {
1058 core = thread->GetIdealCore(); 1246 core = thread->GetIdealCore();
1059 } 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)) {
1060 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 1248 return ERR_INVALID_PROCESSOR_ID;
1061 } 1249 }
1062 1250
1063 // 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.
1064 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { 1252 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
1065 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1253 return ERR_INVALID_COMBINATION;
1066 } 1254 }
1067 1255
1068 thread->ChangeCore(core, mask); 1256 thread->ChangeCore(core, mask);
@@ -1151,7 +1339,7 @@ struct FunctionDef {
1151static const FunctionDef SVC_Table[] = { 1339static const FunctionDef SVC_Table[] = {
1152 {0x00, nullptr, "Unknown"}, 1340 {0x00, nullptr, "Unknown"},
1153 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"}, 1341 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
1154 {0x02, nullptr, "SetMemoryPermission"}, 1342 {0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
1155 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"}, 1343 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
1156 {0x04, SvcWrap<MapMemory>, "MapMemory"}, 1344 {0x04, SvcWrap<MapMemory>, "MapMemory"},
1157 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"}, 1345 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 554a5e328..b06aac4ec 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -24,37 +24,6 @@ struct PageInfo {
24 u64 flags; 24 u64 flags;
25}; 25};
26 26
27/// Values accepted by svcGetInfo
28enum class GetInfoType : u64 {
29 // 1.0.0+
30 AllowedCpuIdBitmask = 0,
31 AllowedThreadPrioBitmask = 1,
32 MapRegionBaseAddr = 2,
33 MapRegionSize = 3,
34 HeapRegionBaseAddr = 4,
35 HeapRegionSize = 5,
36 TotalMemoryUsage = 6,
37 TotalHeapUsage = 7,
38 IsCurrentProcessBeingDebugged = 8,
39 ResourceHandleLimit = 9,
40 IdleTickCount = 10,
41 RandomEntropy = 11,
42 PerformanceCounter = 0xF0000002,
43 // 2.0.0+
44 ASLRRegionBaseAddr = 12,
45 ASLRRegionSize = 13,
46 NewMapRegionBaseAddr = 14,
47 NewMapRegionSize = 15,
48 // 3.0.0+
49 IsVirtualAddressMemoryEnabled = 16,
50 PersonalMmHeapUsage = 17,
51 TitleId = 18,
52 // 4.0.0+
53 PrivilegedProcessId = 19,
54 // 5.0.0+
55 UserExceptionContextAddr = 20,
56};
57
58void CallSVC(u32 immediate); 27void CallSVC(u32 immediate);
59 28
60} // namespace Kernel 29} // namespace Kernel
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 59bc9e0af..4ffb76818 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -4,9 +4,9 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes> 6#include <cinttypes>
7#include <optional>
7#include <vector> 8#include <vector>
8 9
9#include <boost/optional.hpp>
10#include <boost/range/algorithm_ext/erase.hpp> 10#include <boost/range/algorithm_ext/erase.hpp>
11 11
12#include "common/assert.h" 12#include "common/assert.h"
@@ -94,7 +94,7 @@ void Thread::CancelWakeupTimer() {
94 CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle); 94 CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
95} 95}
96 96
97static boost::optional<s32> GetNextProcessorId(u64 mask) { 97static std::optional<s32> GetNextProcessorId(u64 mask) {
98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { 98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
99 if (mask & (1ULL << index)) { 99 if (mask & (1ULL << index)) {
100 if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) { 100 if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
142 142
143 status = ThreadStatus::Ready; 143 status = ThreadStatus::Ready;
144 144
145 boost::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
372 boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; 346 auto& system = Core::System::GetInstance();
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 f4d7bd235..d384d50db 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -258,6 +258,14 @@ public:
258 return last_running_ticks; 258 return last_running_ticks;
259 } 259 }
260 260
261 u64 GetTotalCPUTimeTicks() const {
262 return total_cpu_time_ticks;
263 }
264
265 void UpdateCPUTimeTicks(u64 ticks) {
266 total_cpu_time_ticks += ticks;
267 }
268
261 s32 GetProcessorID() const { 269 s32 GetProcessorID() const {
262 return processor_id; 270 return processor_id;
263 } 271 }
@@ -366,6 +374,8 @@ private:
366 explicit Thread(KernelCore& kernel); 374 explicit Thread(KernelCore& kernel);
367 ~Thread() override; 375 ~Thread() override;
368 376
377 void ChangeScheduler();
378
369 Core::ARM_Interface::ThreadContext context{}; 379 Core::ARM_Interface::ThreadContext context{};
370 380
371 u32 thread_id = 0; 381 u32 thread_id = 0;
@@ -378,7 +388,8 @@ private:
378 u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application 388 u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
379 u32 current_priority = 0; ///< Current thread priority, can be temporarily changed 389 u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
380 390
381 u64 last_running_ticks = 0; ///< CPU tick when thread was last running 391 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
392 u64 last_running_ticks = 0; ///< CPU tick when thread was last running
382 393
383 s32 processor_id = 0; 394 s32 processor_id = 0;
384 395
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e1a34eef1..100f8f6bf 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
143 return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); 143 return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
144} 144}
145 145
146ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
147 // Find the first Free VMA.
148 const VAddr base = GetASLRRegionBaseAddress();
149 const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
150 if (vma.second.type != VMAType::Free)
151 return false;
152
153 const VAddr vma_end = vma.second.base + vma.second.size;
154 return vma_end > base && vma_end >= base + size;
155 });
156
157 if (vma_handle == vma_map.end()) {
158 // TODO(Subv): Find the correct error code here.
159 return ResultCode(-1);
160 }
161
162 const VAddr target = std::max(base, vma_handle->second.base);
163 return MakeResult<VAddr>(target);
164}
165
146ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, 166ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
147 MemoryState state, 167 MemoryState state,
148 Memory::MemoryHookPointer mmio_handler) { 168 Memory::MemoryHookPointer mmio_handler) {
@@ -223,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
223 return RESULT_SUCCESS; 243 return RESULT_SUCCESS;
224} 244}
225 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
226void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { 325void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
227 // 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
228 // specify a specific range of addresses to limit the scan to. 327 // specify a specific range of addresses to limit the scan to.
@@ -475,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const {
475} 574}
476 575
477u64 VMManager::GetTotalHeapUsage() const { 576u64 VMManager::GetTotalHeapUsage() const {
478 LOG_WARNING(Kernel, "(STUBBED) called"); 577 return heap_used;
479 return 0x0;
480} 578}
481 579
482VAddr 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 84c890224..d522404fe 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -158,6 +158,14 @@ public:
158 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); 158 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
159 159
160 /** 160 /**
161 * Finds the first free address that can hold a region of the desired size.
162 *
163 * @param size Size of the desired region.
164 * @return The found free address.
165 */
166 ResultVal<VAddr> FindFreeRegion(u64 size) const;
167
168 /**
161 * Maps a memory-mapped IO region at a given address. 169 * Maps a memory-mapped IO region at a given address.
162 * 170 *
163 * @param target The guest address to start the mapping at. 171 * @param target The guest address to start the mapping at.
@@ -178,6 +186,12 @@ public:
178 /// 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.
179 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); 187 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
180 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
181 /** 195 /**
182 * 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
183 * 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.
@@ -335,5 +349,15 @@ private:
335 349
336 VAddr tls_io_region_base = 0; 350 VAddr tls_io_region_base = 0;
337 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;
338}; 362};
339} // 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 e61748ca3..c629f9357 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -2,9 +2,13 @@
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 <algorithm>
5#include <array> 6#include <array>
7#include "common/common_paths.h"
6#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/file_util.h"
7#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/string_util.h"
8#include "common/swap.h" 12#include "common/swap.h"
9#include "core/core_timing.h" 13#include "core/core_timing.h"
10#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
@@ -16,6 +20,7 @@
16#include "core/hle/service/acc/profile_manager.h" 20#include "core/hle/service/acc/profile_manager.h"
17 21
18namespace Service::Account { 22namespace Service::Account {
23
19// TODO: RE this structure 24// TODO: RE this structure
20struct UserData { 25struct UserData {
21 INSERT_PADDING_WORDS(1); 26 INSERT_PADDING_WORDS(1);
@@ -27,6 +32,29 @@ struct UserData {
27}; 32};
28static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); 33static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
29 34
35// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
36// used as a backup should the one on disk not exist
37constexpr u32 backup_jpeg_size = 107;
38constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
39 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
40 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
41 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
42 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
43 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
44 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
45 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
46}};
47
48static std::string GetImagePath(UUID uuid) {
49 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
50 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
51}
52
53static constexpr u32 SanitizeJPEGSize(std::size_t size) {
54 constexpr std::size_t max_jpeg_image_size = 0x20000;
55 return static_cast<u32>(std::min(size, max_jpeg_image_size));
56}
57
30class IProfile final : public ServiceFramework<IProfile> { 58class IProfile final : public ServiceFramework<IProfile> {
31public: 59public:
32 explicit IProfile(UUID user_id, ProfileManager& profile_manager) 60 explicit IProfile(UUID user_id, ProfileManager& profile_manager)
@@ -73,32 +101,42 @@ private:
73 } 101 }
74 102
75 void LoadImage(Kernel::HLERequestContext& ctx) { 103 void LoadImage(Kernel::HLERequestContext& ctx) {
76 LOG_WARNING(Service_ACC, "(STUBBED) called"); 104 LOG_DEBUG(Service_ACC, "called");
77 // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg 105
78 // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
79 constexpr u32 jpeg_size = 107;
80 static constexpr std::array<u8, jpeg_size> jpeg{
81 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
82 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
83 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
84 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
85 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
86 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
87 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
88 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
89 };
90 ctx.WriteBuffer(jpeg);
91 IPC::ResponseBuilder rb{ctx, 3}; 106 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(RESULT_SUCCESS); 107 rb.Push(RESULT_SUCCESS);
93 rb.Push<u32>(jpeg_size); 108
109 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
110 if (!image.IsOpen()) {
111 LOG_WARNING(Service_ACC,
112 "Failed to load user provided image! Falling back to built-in backup...");
113 ctx.WriteBuffer(backup_jpeg);
114 rb.Push<u32>(backup_jpeg_size);
115 return;
116 }
117
118 const u32 size = SanitizeJPEGSize(image.GetSize());
119 std::vector<u8> buffer(size);
120 image.ReadBytes(buffer.data(), buffer.size());
121
122 ctx.WriteBuffer(buffer.data(), buffer.size());
123 rb.Push<u32>(size);
94 } 124 }
95 125
96 void GetImageSize(Kernel::HLERequestContext& ctx) { 126 void GetImageSize(Kernel::HLERequestContext& ctx) {
97 LOG_WARNING(Service_ACC, "(STUBBED) called"); 127 LOG_DEBUG(Service_ACC, "called");
98 constexpr u32 jpeg_size = 107;
99 IPC::ResponseBuilder rb{ctx, 3}; 128 IPC::ResponseBuilder rb{ctx, 3};
100 rb.Push(RESULT_SUCCESS); 129 rb.Push(RESULT_SUCCESS);
101 rb.Push<u32>(jpeg_size); 130
131 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
132
133 if (!image.IsOpen()) {
134 LOG_WARNING(Service_ACC,
135 "Failed to load user provided image! Falling back to built-in backup...");
136 rb.Push<u32>(backup_jpeg_size);
137 } else {
138 rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
139 }
102 } 140 }
103 141
104 const ProfileManager& profile_manager; 142 const ProfileManager& profile_manager;
@@ -204,6 +242,30 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
204 LOG_DEBUG(Service_ACC, "called"); 242 LOG_DEBUG(Service_ACC, "called");
205} 243}
206 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
207Module::Interface::Interface(std::shared_ptr<Module> module, 269Module::Interface::Interface(std::shared_ptr<Module> module,
208 std::shared_ptr<ProfileManager> profile_manager, const char* name) 270 std::shared_ptr<ProfileManager> profile_manager, const char* name)
209 : 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 bcb3475db..968263846 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -2,42 +2,83 @@
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#include <boost/optional.hpp> 7
8#include <fmt/format.h>
9
10#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
8#include "core/settings.h" 12#include "core/settings.h"
9 13
10namespace Service::Account { 14namespace Service::Account {
15
16struct UserRaw {
17 UUID uuid;
18 UUID uuid2;
19 u64 timestamp;
20 ProfileUsername username;
21 INSERT_PADDING_BYTES(0x80);
22};
23static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
24
25struct ProfileDataRaw {
26 INSERT_PADDING_BYTES(0x10);
27 std::array<UserRaw, MAX_USERS> users;
28};
29static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
30
11// TODO(ogniK): Get actual error codes 31// TODO(ogniK): Get actual error codes
12constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); 32constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
13constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); 33constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
14constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); 34constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
15 35
16const UUID& UUID::Generate() { 36constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
37
38UUID UUID::Generate() {
17 std::random_device device; 39 std::random_device device;
18 std::mt19937 gen(device()); 40 std::mt19937 gen(device());
19 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 41 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
20 uuid[0] = distribution(gen); 42 return UUID{distribution(gen), distribution(gen)};
21 uuid[1] = distribution(gen); 43}
22 return *this; 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]);
23} 56}
24 57
25ProfileManager::ProfileManager() { 58ProfileManager::ProfileManager() {
26 // TODO(ogniK): Create the default user we have for now until loading/saving users is added 59 ParseUserSaveFile();
27 auto user_uuid = UUID{1, 0}; 60
28 ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); 61 if (user_count == 0)
29 OpenUser(user_uuid); 62 CreateNewUser(UUID::Generate(), "yuzu");
63
64 auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
65 if (UserExistsIndex(current))
66 current = 0;
67
68 OpenUser(*GetUser(current));
30} 69}
31 70
32ProfileManager::~ProfileManager() = default; 71ProfileManager::~ProfileManager() {
72 WriteUserSaveFile();
73}
33 74
34/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the 75/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
35/// internal management of the users profiles 76/// internal management of the users profiles
36boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { 77std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
37 if (user_count >= MAX_USERS) { 78 if (user_count >= MAX_USERS) {
38 return boost::none; 79 return {};
39 } 80 }
40 profiles[user_count] = user; 81 profiles[user_count] = profile;
41 return user_count++; 82 return user_count++;
42} 83}
43 84
@@ -56,7 +97,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
56 97
57/// Helper function to register a user to the system 98/// Helper function to register a user to the system
58ResultCode ProfileManager::AddUser(const ProfileInfo& user) { 99ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
59 if (AddToProfiles(user) == boost::none) { 100 if (!AddToProfiles(user)) {
60 return ERROR_TOO_MANY_USERS; 101 return ERROR_TOO_MANY_USERS;
61 } 102 }
62 return RESULT_SUCCESS; 103 return RESULT_SUCCESS;
@@ -101,31 +142,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
101 return CreateNewUser(uuid, username_output); 142 return CreateNewUser(uuid, username_output);
102} 143}
103 144
145std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
146 if (index >= MAX_USERS) {
147 return {};
148 }
149
150 return profiles[index].user_uuid;
151}
152
104/// Returns a users profile index based on their user id. 153/// Returns a users profile index based on their user id.
105boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 154std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
106 if (!uuid) { 155 if (!uuid) {
107 return boost::none; 156 return {};
108 } 157 }
109 auto iter = std::find_if(profiles.begin(), profiles.end(), 158
110 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); 159 const auto iter = std::find_if(profiles.begin(), profiles.end(),
160 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
111 if (iter == profiles.end()) { 161 if (iter == profiles.end()) {
112 return boost::none; 162 return {};
113 } 163 }
164
114 return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); 165 return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
115} 166}
116 167
117/// Returns a users profile index based on their profile 168/// Returns a users profile index based on their profile
118boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { 169std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
119 return GetUserIndex(user.user_uuid); 170 return GetUserIndex(user.user_uuid);
120} 171}
121 172
122/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 173/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
123bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, 174bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
124 ProfileBase& profile) const { 175 if (!index || index >= MAX_USERS) {
125 if (index == boost::none || index >= MAX_USERS) {
126 return false; 176 return false;
127 } 177 }
128 const auto& prof_info = profiles[index.get()]; 178 const auto& prof_info = profiles[*index];
129 profile.user_uuid = prof_info.user_uuid; 179 profile.user_uuid = prof_info.user_uuid;
130 profile.username = prof_info.username; 180 profile.username = prof_info.username;
131 profile.timestamp = prof_info.creation_time; 181 profile.timestamp = prof_info.creation_time;
@@ -134,7 +184,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
134 184
135/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 185/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
136bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { 186bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
137 auto idx = GetUserIndex(uuid); 187 const auto idx = GetUserIndex(uuid);
138 return GetProfileBase(idx, profile); 188 return GetProfileBase(idx, profile);
139} 189}
140 190
@@ -161,26 +211,34 @@ std::size_t ProfileManager::GetOpenUserCount() const {
161 211
162/// Checks if a user id exists in our profile manager 212/// Checks if a user id exists in our profile manager
163bool ProfileManager::UserExists(UUID uuid) const { 213bool ProfileManager::UserExists(UUID uuid) const {
164 return (GetUserIndex(uuid) != boost::none); 214 return GetUserIndex(uuid).has_value();
215}
216
217bool ProfileManager::UserExistsIndex(std::size_t index) const {
218 if (index >= MAX_USERS)
219 return false;
220 return profiles[index].user_uuid.uuid != INVALID_UUID;
165} 221}
166 222
167/// Opens a specific user 223/// Opens a specific user
168void ProfileManager::OpenUser(UUID uuid) { 224void ProfileManager::OpenUser(UUID uuid) {
169 auto idx = GetUserIndex(uuid); 225 const auto idx = GetUserIndex(uuid);
170 if (idx == boost::none) { 226 if (!idx) {
171 return; 227 return;
172 } 228 }
173 profiles[idx.get()].is_open = true; 229
230 profiles[*idx].is_open = true;
174 last_opened_user = uuid; 231 last_opened_user = uuid;
175} 232}
176 233
177/// Closes a specific user 234/// Closes a specific user
178void ProfileManager::CloseUser(UUID uuid) { 235void ProfileManager::CloseUser(UUID uuid) {
179 auto idx = GetUserIndex(uuid); 236 const auto idx = GetUserIndex(uuid);
180 if (idx == boost::none) { 237 if (!idx) {
181 return; 238 return;
182 } 239 }
183 profiles[idx.get()].is_open = false; 240
241 profiles[*idx].is_open = false;
184} 242}
185 243
186/// Gets all valid user ids on the system 244/// Gets all valid user ids on the system
@@ -210,10 +268,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
210} 268}
211 269
212/// Return the users profile base and the unknown arbitary data. 270/// Return the users profile base and the unknown arbitary data.
213bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, 271bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
214 ProfileData& data) const { 272 ProfileData& data) const {
215 if (GetProfileBase(index, profile)) { 273 if (GetProfileBase(index, profile)) {
216 data = profiles[index.get()].data; 274 data = profiles[*index].data;
217 return true; 275 return true;
218 } 276 }
219 return false; 277 return false;
@@ -222,7 +280,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P
222/// Return the users profile base and the unknown arbitary data. 280/// Return the users profile base and the unknown arbitary data.
223bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, 281bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
224 ProfileData& data) const { 282 ProfileData& data) const {
225 auto idx = GetUserIndex(uuid); 283 const auto idx = GetUserIndex(uuid);
226 return GetProfileBaseAndData(idx, profile, data); 284 return GetProfileBaseAndData(idx, profile, data);
227} 285}
228 286
@@ -239,4 +297,97 @@ bool ProfileManager::CanSystemRegisterUser() const {
239 // emulate qlaunch. Update this to dynamically change. 297 // emulate qlaunch. Update this to dynamically change.
240} 298}
241 299
300bool ProfileManager::RemoveUser(UUID uuid) {
301 const auto index = GetUserIndex(uuid);
302 if (!index) {
303 return false;
304 }
305
306 profiles[*index] = ProfileInfo{};
307 std::stable_partition(profiles.begin(), profiles.end(),
308 [](const ProfileInfo& profile) { return profile.user_uuid; });
309 return true;
310}
311
312bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
313 const auto index = GetUserIndex(uuid);
314 if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
315 return false;
316 }
317
318 auto& profile = profiles[*index];
319 profile.user_uuid = profile_new.user_uuid;
320 profile.username = profile_new.username;
321 profile.creation_time = profile_new.timestamp;
322
323 return true;
324}
325
326void ProfileManager::ParseUserSaveFile() {
327 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
328 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
329 "rb");
330
331 if (!save.IsOpen()) {
332 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
333 "user 'yuzu' with random UUID.");
334 return;
335 }
336
337 ProfileDataRaw data;
338 if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
339 LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
340 "'yuzu' with random UUID.");
341 return;
342 }
343
344 for (const auto& user : data.users) {
345 if (user.uuid == UUID(INVALID_UUID)) {
346 continue;
347 }
348
349 AddUser({user.uuid, user.username, user.timestamp, {}, false});
350 }
351
352 std::stable_partition(profiles.begin(), profiles.end(),
353 [](const ProfileInfo& profile) { return profile.user_uuid; });
354}
355
356void ProfileManager::WriteUserSaveFile() {
357 ProfileDataRaw raw{};
358
359 for (std::size_t i = 0; i < MAX_USERS; ++i) {
360 raw.users[i].username = profiles[i].username;
361 raw.users[i].uuid2 = profiles[i].user_uuid;
362 raw.users[i].uuid = profiles[i].user_uuid;
363 raw.users[i].timestamp = profiles[i].creation_time;
364 }
365
366 const auto raw_path =
367 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
368 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
369 FileUtil::Delete(raw_path);
370
371 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
372 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
373
374 if (!FileUtil::CreateFullPath(path)) {
375 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
376 "nand/system/save/8000000000000010/su/avators to mitigate this "
377 "issue.");
378 return;
379 }
380
381 FileUtil::IOFile save(path, "wb");
382
383 if (!save.IsOpen()) {
384 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
385 "made in current session will be saved.");
386 return;
387 }
388
389 save.Resize(sizeof(ProfileDataRaw));
390 save.WriteBytes(&raw, sizeof(ProfileDataRaw));
391}
392
242}; // namespace Service::Account 393}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index bffd4cf4d..d2d8e6c6b 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -5,8 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8 9
9#include "boost/optional.hpp"
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/hle/result.h" 12#include "core/hle/result.h"
@@ -36,19 +36,20 @@ struct UUID {
36 } 36 }
37 37
38 // TODO(ogniK): Properly generate uuids based on RFC-4122 38 // TODO(ogniK): Properly generate uuids based on RFC-4122
39 const UUID& Generate(); 39 static UUID Generate();
40 40
41 // Set the UUID to {0,0} to be considered an invalid user 41 // Set the UUID to {0,0} to be considered an invalid user
42 void Invalidate() { 42 void Invalidate() {
43 uuid = INVALID_UUID; 43 uuid = INVALID_UUID;
44 } 44 }
45 std::string Format() const { 45
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); 46 std::string Format() const;
47 } 47 std::string FormatSwitch() const;
48}; 48};
49static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 49static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
50 50
51using ProfileUsername = std::array<u8, 0x20>; 51constexpr std::size_t profile_username_size = 32;
52using ProfileUsername = std::array<u8, profile_username_size>;
52using ProfileData = std::array<u8, MAX_DATA>; 53using ProfileData = std::array<u8, MAX_DATA>;
53using UserIDArray = std::array<UUID, MAX_USERS>; 54using UserIDArray = std::array<UUID, MAX_USERS>;
54 55
@@ -81,18 +82,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
81/// objects 82/// objects
82class ProfileManager { 83class ProfileManager {
83public: 84public:
84 ProfileManager(); // TODO(ogniK): Load from system save 85 ProfileManager();
85 ~ProfileManager(); 86 ~ProfileManager();
86 87
87 ResultCode AddUser(const ProfileInfo& user); 88 ResultCode AddUser(const ProfileInfo& user);
88 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); 89 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
89 ResultCode CreateNewUser(UUID uuid, const std::string& username); 90 ResultCode CreateNewUser(UUID uuid, const std::string& username);
90 boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; 91 std::optional<UUID> GetUser(std::size_t index) const;
91 boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; 92 std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
92 bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; 93 std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
94 bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
93 bool GetProfileBase(UUID uuid, ProfileBase& profile) const; 95 bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
94 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; 96 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
95 bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, 97 bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
96 ProfileData& data) const; 98 ProfileData& data) const;
97 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; 99 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
98 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, 100 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
@@ -100,6 +102,7 @@ public:
100 std::size_t GetUserCount() const; 102 std::size_t GetUserCount() const;
101 std::size_t GetOpenUserCount() const; 103 std::size_t GetOpenUserCount() const;
102 bool UserExists(UUID uuid) const; 104 bool UserExists(UUID uuid) const;
105 bool UserExistsIndex(std::size_t index) const;
103 void OpenUser(UUID uuid); 106 void OpenUser(UUID uuid);
104 void CloseUser(UUID uuid); 107 void CloseUser(UUID uuid);
105 UserIDArray GetOpenUsers() const; 108 UserIDArray GetOpenUsers() const;
@@ -108,11 +111,17 @@ public:
108 111
109 bool CanSystemRegisterUser() const; 112 bool CanSystemRegisterUser() const;
110 113
114 bool RemoveUser(UUID uuid);
115 bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
116
111private: 117private:
118 void ParseUserSaveFile();
119 void WriteUserSaveFile();
120 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
121 bool RemoveProfileAtIndex(std::size_t index);
122
112 std::array<ProfileInfo, MAX_USERS> profiles{}; 123 std::array<ProfileInfo, MAX_USERS> profiles{};
113 std::size_t user_count = 0; 124 std::size_t user_count = 0;
114 boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
115 bool RemoveProfileAtIndex(std::size_t index);
116 UUID last_opened_user{INVALID_UUID}; 125 UUID last_opened_user{INVALID_UUID};
117}; 126};
118 127
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index ecf72ae24..3758ecae1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -4,11 +4,13 @@
4 4
5#include <array> 5#include <array>
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring>
7#include <stack> 8#include <stack>
8#include "core/core.h" 9#include "core/core.h"
9#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
13#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/am/am.h" 14#include "core/hle/service/am/am.h"
13#include "core/hle/service/am/applet_ae.h" 15#include "core/hle/service/am/applet_ae.h"
14#include "core/hle/service/am/applet_oe.h" 16#include "core/hle/service/am/applet_oe.h"
@@ -26,6 +28,16 @@
26 28
27namespace Service::AM { 29namespace Service::AM {
28 30
31constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
32
33struct LaunchParameters {
34 u32_le magic;
35 u32_le is_account_selected;
36 u128 current_user;
37 INSERT_PADDING_BYTES(0x70);
38};
39static_assert(sizeof(LaunchParameters) == 0x88);
40
29IWindowController::IWindowController() : ServiceFramework("IWindowController") { 41IWindowController::IWindowController() : ServiceFramework("IWindowController") {
30 // clang-format off 42 // clang-format off
31 static const FunctionInfo functions[] = { 43 static const FunctionInfo functions[] = {
@@ -191,8 +203,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
191ISelfController::~ISelfController() = default; 203ISelfController::~ISelfController() = default;
192 204
193void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
194 // 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
195 // bool flags. No output. 207 // u8, these are bool flags. No output.
196 208
197 IPC::RequestParser rp{ctx}; 209 IPC::RequestParser rp{ctx};
198 210
@@ -246,8 +258,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
246} 258}
247 259
248void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
249 // 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
250 // bool flags. No output. 262 // u8, these are bool flags. No output.
251 IPC::RequestParser rp{ctx}; 263 IPC::RequestParser rp{ctx};
252 264
253 bool enabled = rp.Pop<bool>(); 265 bool enabled = rp.Pop<bool>();
@@ -290,8 +302,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
290} 302}
291 303
292void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
293 // 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
294 // in the Default display. 306 // create the layer in the Default display.
295 u64 display_id = nvflinger->OpenDisplay("Default"); 307 u64 display_id = nvflinger->OpenDisplay("Default");
296 u64 layer_id = nvflinger->CreateLayer(display_id); 308 u64 layer_id = nvflinger->CreateLayer(display_id);
297 309
@@ -326,7 +338,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
326 LOG_WARNING(Service_AM, "(STUBBED) called"); 338 LOG_WARNING(Service_AM, "(STUBBED) called");
327} 339}
328 340
329ICommonStateGetter::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)) {
330 // clang-format off 389 // clang-format off
331 static const FunctionInfo functions[] = { 390 static const FunctionInfo functions[] = {
332 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, 391 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
@@ -376,21 +435,19 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
376} 435}
377 436
378void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { 437void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
379 event->Signal();
380
381 IPC::ResponseBuilder rb{ctx, 2, 1}; 438 IPC::ResponseBuilder rb{ctx, 2, 1};
382 rb.Push(RESULT_SUCCESS); 439 rb.Push(RESULT_SUCCESS);
383 rb.PushCopyObjects(event); 440 rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
384 441
385 LOG_WARNING(Service_AM, "(STUBBED) called"); 442 LOG_DEBUG(Service_AM, "called");
386} 443}
387 444
388void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { 445void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
389 IPC::ResponseBuilder rb{ctx, 3}; 446 IPC::ResponseBuilder rb{ctx, 3};
390 rb.Push(RESULT_SUCCESS); 447 rb.Push(RESULT_SUCCESS);
391 rb.Push<u32>(15); 448 rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
392 449
393 LOG_WARNING(Service_AM, "(STUBBED) called"); 450 LOG_DEBUG(Service_AM, "called");
394} 451}
395 452
396void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { 453void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
@@ -402,13 +459,11 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
402} 459}
403 460
404void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { 461void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
405 event->Signal();
406
407 IPC::ResponseBuilder rb{ctx, 2, 1}; 462 IPC::ResponseBuilder rb{ctx, 2, 1};
408 rb.Push(RESULT_SUCCESS); 463 rb.Push(RESULT_SUCCESS);
409 rb.PushCopyObjects(event); 464 rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
410 465
411 LOG_WARNING(Service_AM, "(STUBBED) called"); 466 LOG_DEBUG(Service_AM, "called");
412} 467}
413 468
414void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { 469void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
@@ -432,7 +487,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
432 rb.Push(RESULT_SUCCESS); 487 rb.Push(RESULT_SUCCESS);
433 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));
434 489
435 LOG_WARNING(Service_AM, "(STUBBED) called"); 490 LOG_DEBUG(Service_AM, "called");
436} 491}
437 492
438void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { 493void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
@@ -442,7 +497,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
442 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked 497 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
443 : APM::PerformanceMode::Handheld)); 498 : APM::PerformanceMode::Handheld));
444 499
445 LOG_WARNING(Service_AM, "(STUBBED) called"); 500 LOG_DEBUG(Service_AM, "called");
446} 501}
447 502
448class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 503class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
@@ -678,7 +733,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
678 {70, nullptr, "RequestToShutdown"}, 733 {70, nullptr, "RequestToShutdown"},
679 {71, nullptr, "RequestToReboot"}, 734 {71, nullptr, "RequestToReboot"},
680 {80, nullptr, "ExitAndRequestToShowThanksMessage"}, 735 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
681 {90, nullptr, "EnableApplicationCrashReport"}, 736 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
682 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, 737 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
683 {101, nullptr, "SetApplicationCopyrightImage"}, 738 {101, nullptr, "SetApplicationCopyrightImage"},
684 {102, nullptr, "SetApplicationCopyrightVisibility"}, 739 {102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -697,6 +752,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
697 752
698IApplicationFunctions::~IApplicationFunctions() = default; 753IApplicationFunctions::~IApplicationFunctions() = default;
699 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
700void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( 761void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
701 Kernel::HLERequestContext& ctx) { 762 Kernel::HLERequestContext& ctx) {
702 IPC::ResponseBuilder rb{ctx, 2}; 763 IPC::ResponseBuilder rb{ctx, 2};
@@ -724,20 +785,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
724} 785}
725 786
726void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 787void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
727 constexpr std::array<u8, 0x88> data{{ 788 LaunchParameters params{};
728 0xca, 0x97, 0x94, 0xc7, // Magic 789
729 1, 0, 0, 0, // IsAccountSelected (bool) 790 params.magic = POP_LAUNCH_PARAMETER_MAGIC;
730 1, 0, 0, 0, // User Id (word 0) 791 params.is_account_selected = 1;
731 0, 0, 0, 0, // User Id (word 1)
732 0, 0, 0, 0, // User Id (word 2)
733 0, 0, 0, 0 // User Id (word 3)
734 }};
735 792
736 std::vector<u8> buffer(data.begin(), data.end()); 793 Account::ProfileManager profile_manager{};
794 const auto uuid = profile_manager.GetUser(Settings::values.current_user);
795 ASSERT(uuid);
796 params.current_user = uuid->uuid;
737 797
738 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 798 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
739 799
740 rb.Push(RESULT_SUCCESS); 800 rb.Push(RESULT_SUCCESS);
801
802 std::vector<u8> buffer(sizeof(LaunchParameters));
803 std::memcpy(buffer.data(), &params, buffer.size());
804
741 rb.PushIpcInterface<AM::IStorage>(buffer); 805 rb.PushIpcInterface<AM::IStorage>(buffer);
742 806
743 LOG_DEBUG(Service_AM, "called"); 807 LOG_DEBUG(Service_AM, "called");
@@ -763,7 +827,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
763 827
764void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 828void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
765 // Takes an input u32 Result, no output. 829 // Takes an input u32 Result, no output.
766 // 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.
767 832
768 IPC::RequestParser rp{ctx}; 833 IPC::RequestParser rp{ctx};
769 u32 result = rp.Pop<u32>(); 834 u32 result = rp.Pop<u32>();
@@ -825,8 +890,12 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
825 890
826void InstallInterfaces(SM::ServiceManager& service_manager, 891void InstallInterfaces(SM::ServiceManager& service_manager,
827 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 892 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
828 std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); 893 auto message_queue = std::make_shared<AppletMessageQueue>();
829 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);
830 std::make_shared<IdleSys>()->InstallAsService(service_manager); 899 std::make_shared<IdleSys>()->InstallAsService(service_manager);
831 std::make_shared<OMM>()->InstallAsService(service_manager); 900 std::make_shared<OMM>()->InstallAsService(service_manager);
832 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/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 428069df2..54305cf05 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -24,8 +24,8 @@ namespace Service::AOC {
24constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 24constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
25constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; 25constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
26 26
27static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 27static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
28 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 28 return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
29} 29}
30 30
31static std::vector<u64> AccumulateAOCTitleIDs() { 31static std::vector<u64> AccumulateAOCTitleIDs() {
@@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
74 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 74 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
75 rb.Push<u32>(static_cast<u32>( 75 rb.Push<u32>(static_cast<u32>(
76 std::count_if(add_on_content.begin(), add_on_content.end(), 76 std::count_if(add_on_content.begin(), add_on_content.end(),
77 [&current](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); 77 [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
78} 78}
79 79
80void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { 80void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
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 7168c6a10..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
@@ -161,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
161 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 161 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
162 162
163 std::size_t worker_sz = WorkerBufferSize(channel_count); 163 std::size_t worker_sz = WorkerBufferSize(channel_count);
164 ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); 164 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
165 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ 165 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
166 static_cast<OpusDecoder*>(operator new(worker_sz))}; 166 static_cast<OpusDecoder*>(operator new(worker_sz))};
167 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 167 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
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 d5dced429..038dc80b1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -11,12 +11,14 @@
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"
17#include "core/file_sys/errors.h" 18#include "core/file_sys/errors.h"
18#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
19#include "core/file_sys/nca_metadata.h" 20#include "core/file_sys/nca_metadata.h"
21#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/savedata_factory.h" 22#include "core/file_sys/savedata_factory.h"
21#include "core/file_sys/vfs.h" 23#include "core/file_sys/vfs.h"
22#include "core/hle/ipc_helpers.h" 24#include "core/hle/ipc_helpers.h"
@@ -61,12 +63,12 @@ private:
61 // Error checking 63 // Error checking
62 if (length < 0) { 64 if (length < 0) {
63 IPC::ResponseBuilder rb{ctx, 2}; 65 IPC::ResponseBuilder rb{ctx, 2};
64 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 66 rb.Push(FileSys::ERROR_INVALID_SIZE);
65 return; 67 return;
66 } 68 }
67 if (offset < 0) { 69 if (offset < 0) {
68 IPC::ResponseBuilder rb{ctx, 2}; 70 IPC::ResponseBuilder rb{ctx, 2};
69 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 71 rb.Push(FileSys::ERROR_INVALID_OFFSET);
70 return; 72 return;
71 } 73 }
72 74
@@ -106,12 +108,12 @@ private:
106 // Error checking 108 // Error checking
107 if (length < 0) { 109 if (length < 0) {
108 IPC::ResponseBuilder rb{ctx, 2}; 110 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 111 rb.Push(FileSys::ERROR_INVALID_SIZE);
110 return; 112 return;
111 } 113 }
112 if (offset < 0) { 114 if (offset < 0) {
113 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
114 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 116 rb.Push(FileSys::ERROR_INVALID_OFFSET);
115 return; 117 return;
116 } 118 }
117 119
@@ -137,12 +139,12 @@ private:
137 // Error checking 139 // Error checking
138 if (length < 0) { 140 if (length < 0) {
139 IPC::ResponseBuilder rb{ctx, 2}; 141 IPC::ResponseBuilder rb{ctx, 2};
140 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 142 rb.Push(FileSys::ERROR_INVALID_SIZE);
141 return; 143 return;
142 } 144 }
143 if (offset < 0) { 145 if (offset < 0) {
144 IPC::ResponseBuilder rb{ctx, 2}; 146 IPC::ResponseBuilder rb{ctx, 2};
145 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 147 rb.Push(FileSys::ERROR_INVALID_OFFSET);
146 return; 148 return;
147 } 149 }
148 150
@@ -272,8 +274,8 @@ public:
272 {0, &IFileSystem::CreateFile, "CreateFile"}, 274 {0, &IFileSystem::CreateFile, "CreateFile"},
273 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 275 {1, &IFileSystem::DeleteFile, "DeleteFile"},
274 {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, 276 {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
275 {3, nullptr, "DeleteDirectory"}, 277 {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
276 {4, nullptr, "DeleteDirectoryRecursively"}, 278 {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
277 {5, &IFileSystem::RenameFile, "RenameFile"}, 279 {5, &IFileSystem::RenameFile, "RenameFile"},
278 {6, nullptr, "RenameDirectory"}, 280 {6, nullptr, "RenameDirectory"},
279 {7, &IFileSystem::GetEntryType, "GetEntryType"}, 281 {7, &IFileSystem::GetEntryType, "GetEntryType"},
@@ -328,6 +330,30 @@ public:
328 rb.Push(backend.CreateDirectory(name)); 330 rb.Push(backend.CreateDirectory(name));
329 } 331 }
330 332
333 void DeleteDirectory(Kernel::HLERequestContext& ctx) {
334 const IPC::RequestParser rp{ctx};
335
336 const auto file_buffer = ctx.ReadBuffer();
337 std::string name = Common::StringFromBuffer(file_buffer);
338
339 LOG_DEBUG(Service_FS, "called directory {}", name);
340
341 IPC::ResponseBuilder rb{ctx, 2};
342 rb.Push(backend.DeleteDirectory(name));
343 }
344
345 void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
346 const IPC::RequestParser rp{ctx};
347
348 const auto file_buffer = ctx.ReadBuffer();
349 std::string name = Common::StringFromBuffer(file_buffer);
350
351 LOG_DEBUG(Service_FS, "called directory {}", name);
352
353 IPC::ResponseBuilder rb{ctx, 2};
354 rb.Push(backend.DeleteDirectoryRecursively(name));
355 }
356
331 void RenameFile(Kernel::HLERequestContext& ctx) { 357 void RenameFile(Kernel::HLERequestContext& ctx) {
332 IPC::RequestParser rp{ctx}; 358 IPC::RequestParser rp{ctx};
333 359
@@ -426,7 +452,147 @@ private:
426 VfsDirectoryServiceWrapper backend; 452 VfsDirectoryServiceWrapper backend;
427}; 453};
428 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
429FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { 594FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
595 // clang-format off
430 static const FunctionInfo functions[] = { 596 static const FunctionInfo functions[] = {
431 {0, nullptr, "MountContent"}, 597 {0, nullptr, "MountContent"},
432 {1, &FSP_SRV::Initialize, "Initialize"}, 598 {1, &FSP_SRV::Initialize, "Initialize"},
@@ -460,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
460 {58, nullptr, "ReadSaveDataFileSystemExtraData"}, 626 {58, nullptr, "ReadSaveDataFileSystemExtraData"},
461 {59, nullptr, "WriteSaveDataFileSystemExtraData"}, 627 {59, nullptr, "WriteSaveDataFileSystemExtraData"},
462 {60, nullptr, "OpenSaveDataInfoReader"}, 628 {60, nullptr, "OpenSaveDataInfoReader"},
463 {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, 629 {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
464 {62, nullptr, "OpenCacheStorageList"}, 630 {62, nullptr, "OpenCacheStorageList"},
465 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, 631 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
466 {65, nullptr, "UpdateSaveDataMacForDebug"}, 632 {65, nullptr, "UpdateSaveDataMacForDebug"},
@@ -519,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
519 {1009, nullptr, "GetAndClearMemoryReportInfo"}, 685 {1009, nullptr, "GetAndClearMemoryReportInfo"},
520 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 686 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
521 }; 687 };
688 // clang-format on
522 RegisterHandlers(functions); 689 RegisterHandlers(functions);
523} 690}
524 691
@@ -577,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
577 744
578 if (dir.Failed()) { 745 if (dir.Failed()) {
579 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 746 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
580 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 747 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
581 return; 748 return;
582 } 749 }
583 750
@@ -593,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
593 MountSaveData(ctx); 760 MountSaveData(ctx);
594} 761}
595 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
596void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 772void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
597 LOG_WARNING(Service_FS, "(STUBBED) called"); 773 LOG_WARNING(Service_FS, "(STUBBED) called");
598 774
@@ -630,6 +806,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
630 static_cast<u8>(storage_id), unknown, title_id); 806 static_cast<u8>(storage_id), unknown, title_id);
631 807
632 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 808 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
809
633 if (data.Failed()) { 810 if (data.Failed()) {
634 // TODO(DarkLordZach): Find the right error code to use here 811 // TODO(DarkLordZach): Find the right error code to use here
635 LOG_ERROR(Service_FS, 812 LOG_ERROR(Service_FS,
@@ -640,7 +817,9 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
640 return; 817 return;
641 } 818 }
642 819
643 IStorage storage(std::move(data.Unwrap())); 820 FileSys::PatchManager pm{title_id};
821
822 IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
644 823
645 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 824 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
646 rb.Push(RESULT_SUCCESS); 825 rb.Push(RESULT_SUCCESS);
@@ -657,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
657 static_cast<u8>(storage_id), title_id); 836 static_cast<u8>(storage_id), title_id);
658 837
659 IPC::ResponseBuilder rb{ctx, 2}; 838 IPC::ResponseBuilder rb{ctx, 2};
660 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 839 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
661} 840}
662 841
663} // 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 b06e65a77..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
@@ -108,9 +131,10 @@ void Controller_NPad::OnInit() {
108 styleset_changed_event = 131 styleset_changed_event =
109 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); 132 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
110 133
111 if (!IsControllerActivated()) 134 if (!IsControllerActivated()) {
112 return; 135 return;
113 std::size_t controller{}; 136 }
137
114 if (style.raw == 0) { 138 if (style.raw == 0) {
115 // We want to support all controllers 139 // We want to support all controllers
116 style.handheld.Assign(1); 140 style.handheld.Assign(1);
@@ -287,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
287 switch (controller_type) { 311 switch (controller_type) {
288 case NPadControllerType::Handheld: 312 case NPadControllerType::Handheld:
289 handheld_entry.connection_status.raw = 0; 313 handheld_entry.connection_status.raw = 0;
290 handheld_entry.connection_status.IsConnected.Assign(1); 314 handheld_entry.connection_status.IsWired.Assign(1);
291 if (!Settings::values.use_docked_mode) { 315 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
292 handheld_entry.connection_status.IsWired.Assign(1); 316 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
293 } 317 handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
318 handheld_entry.connection_status.IsRightJoyWired.Assign(1);
294 handheld_entry.pad_states.raw = pad_state.raw; 319 handheld_entry.pad_states.raw = pad_state.raw;
295 handheld_entry.l_stick = lstick_entry; 320 handheld_entry.l_stick = lstick_entry;
296 handheld_entry.r_stick = rstick_entry; 321 handheld_entry.r_stick = rstick_entry;
@@ -309,6 +334,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
309 dual_entry.pad_states.raw = pad_state.raw; 334 dual_entry.pad_states.raw = pad_state.raw;
310 dual_entry.l_stick = lstick_entry; 335 dual_entry.l_stick = lstick_entry;
311 dual_entry.r_stick = rstick_entry; 336 dual_entry.r_stick = rstick_entry;
337 break;
312 case NPadControllerType::JoyLeft: 338 case NPadControllerType::JoyLeft:
313 left_entry.connection_status.raw = 0; 339 left_entry.connection_status.raw = 0;
314 340
@@ -369,16 +395,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
369 supported_npad_id_types.clear(); 395 supported_npad_id_types.clear();
370 supported_npad_id_types.resize(length / sizeof(u32)); 396 supported_npad_id_types.resize(length / sizeof(u32));
371 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;
372 for (std::size_t i = 0; i < connected_controllers.size(); i++) { 399 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
373 auto& controller = connected_controllers[i]; 400 auto& controller = connected_controllers[i];
374 if (!controller.is_connected) { 401 if (!controller.is_connected) {
375 continue; 402 continue;
376 } 403 }
377 if (!IsControllerSupported(PREFERRED_CONTROLLER)) { 404 if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
378 controller.type = DecideBestController(PREFERRED_CONTROLLER); 405 const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
379 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;
380 } 417 }
381 } 418 }
419 if (had_controller_update) {
420 styleset_changed_event->Signal();
421 }
382} 422}
383 423
384void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 424void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -391,8 +431,10 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
391} 431}
392 432
393void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 433void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
434 styleset_changed_event->Signal();
394 hold_type = joy_hold_type; 435 hold_type = joy_hold_type;
395} 436}
437
396Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { 438Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
397 return hold_type; 439 return hold_type;
398} 440}
@@ -426,6 +468,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
426} 468}
427 469
428Kernel::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();
429 return styleset_changed_event; 474 return styleset_changed_event;
430} 475}
431 476
@@ -451,15 +496,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
451} 496}
452 497
453void Controller_NPad::ConnectNPad(u32 npad_id) { 498void Controller_NPad::ConnectNPad(u32 npad_id) {
454 if (npad_id >= connected_controllers.size()) 499 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
455 return;
456 connected_controllers[npad_id].is_connected = true;
457} 500}
458 501
459void Controller_NPad::DisconnectNPad(u32 npad_id) { 502void Controller_NPad::DisconnectNPad(u32 npad_id) {
460 if (npad_id >= connected_controllers.size()) 503 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
461 return;
462 connected_controllers[npad_id].is_connected = false;
463} 504}
464 505
465Controller_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 ec32faf15..b43f1f054 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -3,12 +3,51 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <fmt/format.h>
7#include <mbedtls/sha256.h>
6 8
9#include "common/alignment.h"
10#include "common/hex_util.h"
11#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/process.h"
7#include "core/hle/service/ldr/ldr.h" 13#include "core/hle/service/ldr/ldr.h"
8#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
15#include "core/loader/nro.h"
9 16
10namespace Service::LDR { 17namespace Service::LDR {
11 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
12class DebugMonitor final : public ServiceFramework<DebugMonitor> { 51class DebugMonitor final : public ServiceFramework<DebugMonitor> {
13public: 52public:
14 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { 53 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -59,16 +98,392 @@ public:
59 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { 98 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
60 // clang-format off 99 // clang-format off
61 static const FunctionInfo functions[] = { 100 static const FunctionInfo functions[] = {
62 {0, nullptr, "LoadNro"}, 101 {0, &RelocatableObject::LoadNro, "LoadNro"},
63 {1, nullptr, "UnloadNro"}, 102 {1, &RelocatableObject::UnloadNro, "UnloadNro"},
64 {2, nullptr, "LoadNrr"}, 103 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
65 {3, nullptr, "UnloadNrr"}, 104 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
66 {4, nullptr, "Initialize"}, 105 {4, &RelocatableObject::Initialize, "Initialize"},
67 }; 106 };
68 // clang-format on 107 // clang-format on
69 108
70 RegisterHandlers(functions); 109 RegisterHandlers(functions);
71 } 110 }
111
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);
228 IPC::ResponseBuilder rb{ctx, 2};
229 rb.Push(RESULT_SUCCESS);
230 }
231
232 void LoadNro(Kernel::HLERequestContext& ctx) {
233 IPC::RequestParser rp{ctx};
234 rp.Skip(2, false);
235 const VAddr nro_addr{rp.Pop<VAddr>()};
236 const u64 nro_size{rp.Pop<u64>()};
237 const VAddr bss_addr{rp.Pop<VAddr>()};
238 const u64 bss_size{rp.Pop<u64>()};
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
279 // Read NRO data from memory
280 std::vector<u8> nro_data(nro_size);
281 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
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
316 // Load NRO as new executable module
317 auto* process = Core::CurrentProcess();
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);
347
348 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
349 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
350 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
351 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
352
353 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
354
355 IPC::ResponseBuilder rb{ctx, 4};
356 rb.Push(RESULT_SUCCESS);
357 rb.Push(*map_address);
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);
410 }
411
412 void Initialize(Kernel::HLERequestContext& ctx) {
413 initialized = true;
414
415 IPC::ResponseBuilder rb{ctx, 2};
416 rb.Push(RESULT_SUCCESS);
417 LOG_WARNING(Service_LDR, "(STUBBED) called");
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 }
72}; 487};
73 488
74void InstallInterfaces(SM::ServiceManager& sm) { 489void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 8fec97db8..30e542542 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -10,12 +10,13 @@
10#include "core/hle/service/nfc/nfc.h" 10#include "core/hle/service/nfc/nfc.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h" 12#include "core/hle/service/sm/sm.h"
13#include "core/settings.h"
13 14
14namespace Service::NFC { 15namespace Service::NFC {
15 16
16class IAm final : public ServiceFramework<IAm> { 17class IAm final : public ServiceFramework<IAm> {
17public: 18public:
18 explicit IAm() : ServiceFramework{"IAm"} { 19 explicit IAm() : ServiceFramework{"NFC::IAm"} {
19 // clang-format off 20 // clang-format off
20 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"}, 22 {0, nullptr, "Initialize"},
@@ -52,7 +53,7 @@ private:
52 53
53class MFIUser final : public ServiceFramework<MFIUser> { 54class MFIUser final : public ServiceFramework<MFIUser> {
54public: 55public:
55 explicit MFIUser() : ServiceFramework{"IUser"} { 56 explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} {
56 // clang-format off 57 // clang-format off
57 static const FunctionInfo functions[] = { 58 static const FunctionInfo functions[] = {
58 {0, nullptr, "Initialize"}, 59 {0, nullptr, "Initialize"},
@@ -100,13 +101,13 @@ private:
100 101
101class IUser final : public ServiceFramework<IUser> { 102class IUser final : public ServiceFramework<IUser> {
102public: 103public:
103 explicit IUser() : ServiceFramework{"IUser"} { 104 explicit IUser() : ServiceFramework{"NFC::IUser"} {
104 // clang-format off 105 // clang-format off
105 static const FunctionInfo functions[] = { 106 static const FunctionInfo functions[] = {
106 {0, nullptr, "Initialize"}, 107 {0, &IUser::InitializeOld, "InitializeOld"},
107 {1, nullptr, "Finalize"}, 108 {1, &IUser::FinalizeOld, "FinalizeOld"},
108 {2, nullptr, "GetState"}, 109 {2, &IUser::GetStateOld, "GetStateOld"},
109 {3, nullptr, "IsNfcEnabled"}, 110 {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
110 {400, nullptr, "Initialize"}, 111 {400, nullptr, "Initialize"},
111 {401, nullptr, "Finalize"}, 112 {401, nullptr, "Finalize"},
112 {402, nullptr, "GetState"}, 113 {402, nullptr, "GetState"},
@@ -130,11 +131,47 @@ public:
130 131
131 RegisterHandlers(functions); 132 RegisterHandlers(functions);
132 } 133 }
134
135private:
136 enum class NfcStates : u32 {
137 Finalized = 6,
138 };
139
140 void InitializeOld(Kernel::HLERequestContext& ctx) {
141 IPC::ResponseBuilder rb{ctx, 2, 0};
142 rb.Push(RESULT_SUCCESS);
143
144 // We don't deal with hardware initialization so we can just stub this.
145 LOG_DEBUG(Service_NFC, "called");
146 }
147
148 void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
149 IPC::ResponseBuilder rb{ctx, 3};
150 rb.Push(RESULT_SUCCESS);
151 rb.PushRaw<u8>(Settings::values.enable_nfc);
152
153 LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
154 }
155
156 void GetStateOld(Kernel::HLERequestContext& ctx) {
157 LOG_WARNING(Service_NFC, "(STUBBED) called");
158
159 IPC::ResponseBuilder rb{ctx, 3};
160 rb.Push(RESULT_SUCCESS);
161 rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
162 }
163
164 void FinalizeOld(Kernel::HLERequestContext& ctx) {
165 LOG_WARNING(Service_NFC, "(STUBBED) called");
166
167 IPC::ResponseBuilder rb{ctx, 2};
168 rb.Push(RESULT_SUCCESS);
169 }
133}; 170};
134 171
135class NFC_U final : public ServiceFramework<NFC_U> { 172class NFC_U final : public ServiceFramework<NFC_U> {
136public: 173public:
137 explicit NFC_U() : ServiceFramework{"nfc:u"} { 174 explicit NFC_U() : ServiceFramework{"nfc:user"} {
138 // clang-format off 175 // clang-format off
139 static const FunctionInfo functions[] = { 176 static const FunctionInfo functions[] = {
140 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, 177 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 39c0c1e63..1d6e7756f 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,56 +2,67 @@
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 <atomic>
6
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/lock.h"
9#include "core/hle/service/hid/hid.h" 12#include "core/hle/service/hid/hid.h"
10#include "core/hle/service/nfp/nfp.h" 13#include "core/hle/service/nfp/nfp.h"
11#include "core/hle/service/nfp/nfp_user.h" 14#include "core/hle/service/nfp/nfp_user.h"
12 15
13namespace Service::NFP { 16namespace Service::NFP {
14 17
18namespace ErrCodes {
19constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
20 -1); // TODO(ogniK): Find the actual error code
21}
22
15Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 23Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
16 : ServiceFramework(name), module(std::move(module)) {} 24 : ServiceFramework(name), module(std::move(module)) {
25 auto& kernel = Core::System::GetInstance().Kernel();
26 nfc_tag_load =
27 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected");
28}
17 29
18Module::Interface::~Interface() = default; 30Module::Interface::~Interface() = default;
19 31
20class IUser final : public ServiceFramework<IUser> { 32class IUser final : public ServiceFramework<IUser> {
21public: 33public:
22 IUser() : ServiceFramework("IUser") { 34 IUser(Module::Interface& nfp_interface)
35 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
23 static const FunctionInfo functions[] = { 36 static const FunctionInfo functions[] = {
24 {0, &IUser::Initialize, "Initialize"}, 37 {0, &IUser::Initialize, "Initialize"},
25 {1, nullptr, "Finalize"}, 38 {1, &IUser::Finalize, "Finalize"},
26 {2, &IUser::ListDevices, "ListDevices"}, 39 {2, &IUser::ListDevices, "ListDevices"},
27 {3, nullptr, "StartDetection"}, 40 {3, &IUser::StartDetection, "StartDetection"},
28 {4, nullptr, "StopDetection"}, 41 {4, &IUser::StopDetection, "StopDetection"},
29 {5, nullptr, "Mount"}, 42 {5, &IUser::Mount, "Mount"},
30 {6, nullptr, "Unmount"}, 43 {6, &IUser::Unmount, "Unmount"},
31 {7, nullptr, "OpenApplicationArea"}, 44 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
32 {8, nullptr, "GetApplicationArea"}, 45 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
33 {9, nullptr, "SetApplicationArea"}, 46 {9, nullptr, "SetApplicationArea"},
34 {10, nullptr, "Flush"}, 47 {10, nullptr, "Flush"},
35 {11, nullptr, "Restore"}, 48 {11, nullptr, "Restore"},
36 {12, nullptr, "CreateApplicationArea"}, 49 {12, nullptr, "CreateApplicationArea"},
37 {13, nullptr, "GetTagInfo"}, 50 {13, &IUser::GetTagInfo, "GetTagInfo"},
38 {14, nullptr, "GetRegisterInfo"}, 51 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
39 {15, nullptr, "GetCommonInfo"}, 52 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
40 {16, nullptr, "GetModelInfo"}, 53 {16, &IUser::GetModelInfo, "GetModelInfo"},
41 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, 54 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
42 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, 55 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
43 {19, &IUser::GetState, "GetState"}, 56 {19, &IUser::GetState, "GetState"},
44 {20, &IUser::GetDeviceState, "GetDeviceState"}, 57 {20, &IUser::GetDeviceState, "GetDeviceState"},
45 {21, &IUser::GetNpadId, "GetNpadId"}, 58 {21, &IUser::GetNpadId, "GetNpadId"},
46 {22, nullptr, "GetApplicationArea2"}, 59 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
47 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, 60 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
48 {24, nullptr, "RecreateApplicationArea"}, 61 {24, nullptr, "RecreateApplicationArea"},
49 }; 62 };
50 RegisterHandlers(functions); 63 RegisterHandlers(functions);
51 64
52 auto& kernel = Core::System::GetInstance().Kernel(); 65 auto& kernel = Core::System::GetInstance().Kernel();
53 activate_event =
54 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");
55 deactivate_event = 66 deactivate_event =
56 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); 67 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
57 availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, 68 availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
@@ -59,6 +70,17 @@ public:
59 } 70 }
60 71
61private: 72private:
73 struct TagInfo {
74 std::array<u8, 10> uuid;
75 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
76 // mean something else
77 INSERT_PADDING_BYTES(0x15);
78 u32_le protocol;
79 u32_le tag_type;
80 INSERT_PADDING_BYTES(0x2c);
81 };
82 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
83
62 enum class State : u32 { 84 enum class State : u32 {
63 NonInitialized = 0, 85 NonInitialized = 0,
64 Initialized = 1, 86 Initialized = 1,
@@ -66,15 +88,40 @@ private:
66 88
67 enum class DeviceState : u32 { 89 enum class DeviceState : u32 {
68 Initialized = 0, 90 Initialized = 0,
91 SearchingForTag = 1,
92 TagFound = 2,
93 TagRemoved = 3,
94 TagNearby = 4,
95 Unknown5 = 5,
96 Finalized = 6
97 };
98
99 struct CommonInfo {
100 u16_be last_write_year;
101 u8 last_write_month;
102 u8 last_write_day;
103 u16_be write_counter;
104 u16_be version;
105 u32_be application_area_size;
106 INSERT_PADDING_BYTES(0x34);
69 }; 107 };
108 static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
70 109
71 void Initialize(Kernel::HLERequestContext& ctx) { 110 void Initialize(Kernel::HLERequestContext& ctx) {
72 LOG_WARNING(Service_NFP, "(STUBBED) called"); 111 IPC::ResponseBuilder rb{ctx, 2, 0};
112 rb.Push(RESULT_SUCCESS);
73 113
74 state = State::Initialized; 114 state = State::Initialized;
75 115
76 IPC::ResponseBuilder rb{ctx, 2}; 116 LOG_DEBUG(Service_NFC, "called");
117 }
118
119 void GetState(Kernel::HLERequestContext& ctx) {
120 IPC::ResponseBuilder rb{ctx, 3, 0};
77 rb.Push(RESULT_SUCCESS); 121 rb.Push(RESULT_SUCCESS);
122 rb.PushRaw<u32>(static_cast<u32>(state));
123
124 LOG_DEBUG(Service_NFC, "called");
78 } 125 }
79 126
80 void ListDevices(Kernel::HLERequestContext& ctx) { 127 void ListDevices(Kernel::HLERequestContext& ctx) {
@@ -83,80 +130,219 @@ private:
83 130
84 ctx.WriteBuffer(&device_handle, sizeof(device_handle)); 131 ctx.WriteBuffer(&device_handle, sizeof(device_handle));
85 132
86 LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); 133 LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
87 134
88 IPC::ResponseBuilder rb{ctx, 3}; 135 IPC::ResponseBuilder rb{ctx, 3};
89 rb.Push(RESULT_SUCCESS); 136 rb.Push(RESULT_SUCCESS);
90 rb.Push<u32>(0); 137 rb.Push<u32>(1);
91 } 138 }
92 139
93 void AttachActivateEvent(Kernel::HLERequestContext& ctx) { 140 void GetNpadId(Kernel::HLERequestContext& ctx) {
94 IPC::RequestParser rp{ctx}; 141 IPC::RequestParser rp{ctx};
95 const u64 dev_handle = rp.Pop<u64>(); 142 const u64 dev_handle = rp.Pop<u64>();
96 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 143 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
144 IPC::ResponseBuilder rb{ctx, 3};
145 rb.Push(RESULT_SUCCESS);
146 rb.Push<u32>(npad_id);
147 }
97 148
149 void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
150 IPC::RequestParser rp{ctx};
151 const u64 dev_handle = rp.Pop<u64>();
152 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
98 IPC::ResponseBuilder rb{ctx, 2, 1}; 153 IPC::ResponseBuilder rb{ctx, 2, 1};
99 rb.Push(RESULT_SUCCESS); 154 rb.Push(RESULT_SUCCESS);
100 rb.PushCopyObjects(activate_event); 155 rb.PushCopyObjects(nfp_interface.GetNFCEvent());
156 has_attached_handle = true;
101 } 157 }
102 158
103 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { 159 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
104 IPC::RequestParser rp{ctx}; 160 IPC::RequestParser rp{ctx};
105 const u64 dev_handle = rp.Pop<u64>(); 161 const u64 dev_handle = rp.Pop<u64>();
106 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 162 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
107 163
108 IPC::ResponseBuilder rb{ctx, 2, 1}; 164 IPC::ResponseBuilder rb{ctx, 2, 1};
109 rb.Push(RESULT_SUCCESS); 165 rb.Push(RESULT_SUCCESS);
110 rb.PushCopyObjects(deactivate_event); 166 rb.PushCopyObjects(deactivate_event);
111 } 167 }
112 168
113 void GetState(Kernel::HLERequestContext& ctx) { 169 void StopDetection(Kernel::HLERequestContext& ctx) {
114 LOG_WARNING(Service_NFP, "(STUBBED) called"); 170 LOG_DEBUG(Service_NFP, "called");
115 IPC::ResponseBuilder rb{ctx, 3}; 171 switch (device_state) {
172 case DeviceState::TagFound:
173 case DeviceState::TagNearby:
174 deactivate_event->Signal();
175 device_state = DeviceState::Initialized;
176 break;
177 case DeviceState::SearchingForTag:
178 case DeviceState::TagRemoved:
179 device_state = DeviceState::Initialized;
180 break;
181 }
182 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(RESULT_SUCCESS); 183 rb.Push(RESULT_SUCCESS);
117 rb.Push<u32>(static_cast<u32>(state));
118 } 184 }
119 185
120 void GetDeviceState(Kernel::HLERequestContext& ctx) { 186 void GetDeviceState(Kernel::HLERequestContext& ctx) {
121 LOG_WARNING(Service_NFP, "(STUBBED) called"); 187 LOG_DEBUG(Service_NFP, "called");
188 auto nfc_event = nfp_interface.GetNFCEvent();
189 if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) {
190 device_state = DeviceState::TagFound;
191 nfc_event->Clear();
192 }
193
122 IPC::ResponseBuilder rb{ctx, 3}; 194 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(RESULT_SUCCESS); 195 rb.Push(RESULT_SUCCESS);
124 rb.Push<u32>(static_cast<u32>(device_state)); 196 rb.Push<u32>(static_cast<u32>(device_state));
125 } 197 }
126 198
127 void GetNpadId(Kernel::HLERequestContext& ctx) { 199 void StartDetection(Kernel::HLERequestContext& ctx) {
128 IPC::RequestParser rp{ctx}; 200 LOG_DEBUG(Service_NFP, "called");
129 const u64 dev_handle = rp.Pop<u64>(); 201
130 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 202 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
131 IPC::ResponseBuilder rb{ctx, 3}; 203 device_state = DeviceState::SearchingForTag;
204 }
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(RESULT_SUCCESS);
207 }
208
209 void GetTagInfo(Kernel::HLERequestContext& ctx) {
210 LOG_DEBUG(Service_NFP, "called");
211
212 IPC::ResponseBuilder rb{ctx, 2};
213 auto amiibo = nfp_interface.GetAmiiboBuffer();
214 TagInfo tag_info{};
215 tag_info.uuid = amiibo.uuid;
216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
217
218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
219 tag_info.tag_type = 2;
220 ctx.WriteBuffer(&tag_info, sizeof(TagInfo));
221 rb.Push(RESULT_SUCCESS);
222 }
223
224 void Mount(Kernel::HLERequestContext& ctx) {
225 LOG_DEBUG(Service_NFP, "called");
226
227 device_state = DeviceState::TagNearby;
228 IPC::ResponseBuilder rb{ctx, 2};
229 rb.Push(RESULT_SUCCESS);
230 }
231
232 void GetModelInfo(Kernel::HLERequestContext& ctx) {
233 LOG_DEBUG(Service_NFP, "called");
234
235 IPC::ResponseBuilder rb{ctx, 2};
236 auto amiibo = nfp_interface.GetAmiiboBuffer();
237 ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
238 rb.Push(RESULT_SUCCESS);
239 }
240
241 void Unmount(Kernel::HLERequestContext& ctx) {
242 LOG_DEBUG(Service_NFP, "called");
243
244 device_state = DeviceState::TagFound;
245
246 IPC::ResponseBuilder rb{ctx, 2};
247 rb.Push(RESULT_SUCCESS);
248 }
249
250 void Finalize(Kernel::HLERequestContext& ctx) {
251 LOG_DEBUG(Service_NFP, "called");
252
253 device_state = DeviceState::Finalized;
254
255 IPC::ResponseBuilder rb{ctx, 2};
132 rb.Push(RESULT_SUCCESS); 256 rb.Push(RESULT_SUCCESS);
133 rb.Push<u32>(npad_id);
134 } 257 }
135 258
136 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { 259 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
137 IPC::RequestParser rp{ctx}; 260 LOG_WARNING(Service_NFP, "(STUBBED) called");
138 const u64 dev_handle = rp.Pop<u64>();
139 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
140 261
141 IPC::ResponseBuilder rb{ctx, 2, 1}; 262 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS); 263 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(availability_change_event); 264 rb.PushCopyObjects(availability_change_event);
144 } 265 }
145 266
146 const u64 device_handle{0xDEAD}; 267 void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
147 const u32 npad_id{0}; // This is the first player controller id 268 LOG_WARNING(Service_NFP, "(STUBBED) called");
269
270 // TODO(ogniK): Pull Mii and owner data from amiibo
271
272 IPC::ResponseBuilder rb{ctx, 2};
273 rb.Push(RESULT_SUCCESS);
274 }
275
276 void GetCommonInfo(Kernel::HLERequestContext& ctx) {
277 LOG_WARNING(Service_NFP, "(STUBBED) called");
278
279 // TODO(ogniK): Pull common information from amiibo
280
281 CommonInfo common_info{};
282 common_info.application_area_size = 0;
283 ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
284
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(RESULT_SUCCESS);
287 }
288
289 void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
290 LOG_DEBUG(Service_NFP, "called");
291 // We don't need to worry about this since we can just open the file
292 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(RESULT_SUCCESS);
294 }
295
296 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
297 LOG_WARNING(Service_NFP, "(STUBBED) called");
298 // We don't need to worry about this since we can just open the file
299 IPC::ResponseBuilder rb{ctx, 3};
300 rb.Push(RESULT_SUCCESS);
301 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
302 }
303
304 void GetApplicationArea(Kernel::HLERequestContext& ctx) {
305 LOG_WARNING(Service_NFP, "(STUBBED) called");
306
307 // TODO(ogniK): Pull application area from amiibo
308
309 IPC::ResponseBuilder rb{ctx, 3};
310 rb.Push(RESULT_SUCCESS);
311 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
312 }
313
314 bool has_attached_handle{};
315 const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')};
316 const u32 npad_id{0}; // Player 1 controller
148 State state{State::NonInitialized}; 317 State state{State::NonInitialized};
149 DeviceState device_state{DeviceState::Initialized}; 318 DeviceState device_state{DeviceState::Initialized};
150 Kernel::SharedPtr<Kernel::Event> activate_event;
151 Kernel::SharedPtr<Kernel::Event> deactivate_event; 319 Kernel::SharedPtr<Kernel::Event> deactivate_event;
152 Kernel::SharedPtr<Kernel::Event> availability_change_event; 320 Kernel::SharedPtr<Kernel::Event> availability_change_event;
321 const Module::Interface& nfp_interface;
153}; 322};
154 323
155void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 324void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
156 LOG_DEBUG(Service_NFP, "called"); 325 LOG_DEBUG(Service_NFP, "called");
157 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 326 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
158 rb.Push(RESULT_SUCCESS); 327 rb.Push(RESULT_SUCCESS);
159 rb.PushIpcInterface<IUser>(); 328 rb.PushIpcInterface<IUser>(*this);
329}
330
331bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
332 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
333 if (buffer.size() < sizeof(AmiiboFile)) {
334 return false;
335 }
336
337 std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
338 nfc_tag_load->Signal();
339 return true;
340}
341const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
342 return nfc_tag_load;
343}
344const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
345 return amiibo;
160} 346}
161 347
162void InstallInterfaces(SM::ServiceManager& service_manager) { 348void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 77df343c4..5c0ae8a54 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector>
9#include "core/hle/kernel/event.h"
7#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
8 11
9namespace Service::NFP { 12namespace Service::NFP {
@@ -15,7 +18,27 @@ public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 18 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override; 19 ~Interface() override;
17 20
21 struct ModelInfo {
22 std::array<u8, 0x8> amiibo_identification_block;
23 INSERT_PADDING_BYTES(0x38);
24 };
25 static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
26
27 struct AmiiboFile {
28 std::array<u8, 10> uuid;
29 INSERT_PADDING_BYTES(0x4a);
30 ModelInfo model_info;
31 };
32 static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
33
18 void CreateUserInterface(Kernel::HLERequestContext& ctx); 34 void CreateUserInterface(Kernel::HLERequestContext& ctx);
35 bool LoadAmiibo(const std::vector<u8>& buffer);
36 const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
37 const AmiiboFile& GetAmiiboBuffer() const;
38
39 private:
40 Kernel::SharedPtr<Kernel::Event> nfc_tag_load{};
41 AmiiboFile amiibo{};
19 42
20 protected: 43 protected:
21 std::shared_ptr<Module> module; 44 std::shared_ptr<Module> module;
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.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index fd98d541d..630ebbfc7 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -31,7 +31,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
31 buffer_wait_event->Signal(); 31 buffer_wait_event->Signal();
32} 32}
33 33
34boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { 34std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
35 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { 35 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
36 // Only consider free buffers. Buffers become free once again after they've been Acquired 36 // Only consider free buffers. Buffers become free once again after they've been Acquired
37 // and Released by the compositor, see the NVFlinger::Compose method. 37 // and Released by the compositor, see the NVFlinger::Compose method.
@@ -44,7 +44,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
44 }); 44 });
45 45
46 if (itr == queue.end()) { 46 if (itr == queue.end()) {
47 return boost::none; 47 return {};
48 } 48 }
49 49
50 itr->status = Buffer::Status::Dequeued; 50 itr->status = Buffer::Status::Dequeued;
@@ -70,12 +70,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
70 itr->crop_rect = crop_rect; 70 itr->crop_rect = crop_rect;
71} 71}
72 72
73boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { 73std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
74 auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { 74 auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
75 return buffer.status == Buffer::Status::Queued; 75 return buffer.status == Buffer::Status::Queued;
76 }); 76 });
77 if (itr == queue.end()) 77 if (itr == queue.end())
78 return boost::none; 78 return {};
79 itr->status = Buffer::Status::Acquired; 79 itr->status = Buffer::Status::Acquired;
80 return *itr; 80 return *itr;
81} 81}
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 50b767732..8cff5eb71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,8 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include <vector> 8#include <vector>
8#include <boost/optional.hpp> 9
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/math_util.h" 11#include "common/math_util.h"
11#include "common/swap.h" 12#include "common/swap.h"
@@ -57,9 +58,9 @@ public:
57 /// Rotate source image 90 degrees clockwise 58 /// Rotate source image 90 degrees clockwise
58 Rotate90 = 0x04, 59 Rotate90 = 0x04,
59 /// Rotate source image 180 degrees 60 /// Rotate source image 180 degrees
60 Roate180 = 0x03, 61 Rotate180 = 0x03,
61 /// Rotate source image 270 degrees clockwise 62 /// Rotate source image 270 degrees clockwise
62 Roate270 = 0x07, 63 Rotate270 = 0x07,
63 }; 64 };
64 65
65 struct Buffer { 66 struct Buffer {
@@ -73,11 +74,11 @@ public:
73 }; 74 };
74 75
75 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); 76 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
76 boost::optional<u32> DequeueBuffer(u32 width, u32 height); 77 std::optional<u32> DequeueBuffer(u32 width, u32 height);
77 const IGBPBuffer& RequestBuffer(u32 slot) const; 78 const IGBPBuffer& RequestBuffer(u32 slot) const;
78 void QueueBuffer(u32 slot, BufferTransformFlags transform, 79 void QueueBuffer(u32 slot, BufferTransformFlags transform,
79 const MathUtil::Rectangle<int>& crop_rect); 80 const MathUtil::Rectangle<int>& crop_rect);
80 boost::optional<const Buffer&> AcquireBuffer(); 81 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
81 void ReleaseBuffer(u32 slot); 82 void ReleaseBuffer(u32 slot);
82 u32 Query(QueryType type); 83 u32 Query(QueryType type);
83 84
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index d47b6f659..214e6d1b3 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -3,7 +3,7 @@
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 <boost/optional.hpp> 6#include <optional>
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/assert.h" 9#include "common/assert.h"
@@ -134,7 +134,7 @@ void NVFlinger::Compose() {
134 134
135 MicroProfileFlip(); 135 MicroProfileFlip();
136 136
137 if (buffer == boost::none) { 137 if (!buffer) {
138 auto& system_instance = Core::System::GetInstance(); 138 auto& system_instance = Core::System::GetInstance();
139 139
140 // There was no queued buffer to draw, render previous frame 140 // There was no queued buffer to draw, render previous frame
@@ -143,7 +143,7 @@ void NVFlinger::Compose() {
143 continue; 143 continue;
144 } 144 }
145 145
146 auto& igbp_buffer = buffer->igbp_buffer; 146 auto& igbp_buffer = buffer->get().igbp_buffer;
147 147
148 // Now send the buffer to the GPU for drawing. 148 // Now send the buffer to the GPU for drawing.
149 // TODO(Subv): Support more than just disp0. The display device selection is probably based 149 // TODO(Subv): Support more than just disp0. The display device selection is probably based
@@ -152,10 +152,10 @@ void NVFlinger::Compose() {
152 ASSERT(nvdisp); 152 ASSERT(nvdisp);
153 153
154 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, 154 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
155 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform, 155 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
156 buffer->crop_rect); 156 buffer->get().transform, buffer->get().crop_rect);
157 157
158 buffer_queue->ReleaseBuffer(buffer->slot); 158 buffer_queue->ReleaseBuffer(buffer->get().slot);
159 } 159 }
160} 160}
161 161
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/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index e7fb5a419..f0a831d45 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -67,15 +67,15 @@ public:
67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { 67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
68 // clang-format off 68 // clang-format off
69 static const FunctionInfo functions[] = { 69 static const FunctionInfo functions[] = {
70 {0, nullptr, "Unknown1"}, 70 {0, nullptr, "Open"},
71 {1, nullptr, "Unknown2"}, 71 {1, nullptr, "Close"},
72 {2, nullptr, "Unknown3"}, 72 {2, nullptr, "Unknown1"},
73 {3, nullptr, "Unknown4"}, 73 {3, nullptr, "Populate"},
74 {4, nullptr, "PostBufferAsync"}, 74 {4, nullptr, "PostBufferAsync"},
75 {5, nullptr, "Unknown5"}, 75 {5, nullptr, "GetXferReport"},
76 {6, nullptr, "Unknown6"}, 76 {6, nullptr, "Unknown2"},
77 {7, nullptr, "Unknown7"}, 77 {7, nullptr, "Unknown3"},
78 {8, nullptr, "Unknown8"}, 78 {8, nullptr, "Unknown4"},
79 }; 79 };
80 // clang-format on 80 // clang-format on
81 81
@@ -89,15 +89,15 @@ public:
89 // clang-format off 89 // clang-format off
90 static const FunctionInfo functions[] = { 90 static const FunctionInfo functions[] = {
91 {0, nullptr, "Unknown1"}, 91 {0, nullptr, "Unknown1"},
92 {1, nullptr, "Unknown2"}, 92 {1, nullptr, "SetInterface"},
93 {2, nullptr, "Unknown3"}, 93 {2, nullptr, "GetInterface"},
94 {3, nullptr, "Unknown4"}, 94 {3, nullptr, "GetAlternateInterface"},
95 {4, nullptr, "Unknown5"}, 95 {4, nullptr, "GetCurrentFrame"},
96 {5, nullptr, "CtrlXferAsync"}, 96 {5, nullptr, "CtrlXferAsync"},
97 {6, nullptr, "Unknown6"}, 97 {6, nullptr, "Unknown2"},
98 {7, nullptr, "GetCtrlXferReport"}, 98 {7, nullptr, "GetCtrlXferReport"},
99 {8, nullptr, "Unknown7"}, 99 {8, nullptr, "ResetDevice"},
100 {9, nullptr, "GetClientEpSession"}, 100 {9, nullptr, "OpenUsbEp"},
101 }; 101 };
102 // clang-format on 102 // clang-format on
103 103
@@ -111,13 +111,14 @@ public:
111 // clang-format off 111 // clang-format off
112 static const FunctionInfo functions[] = { 112 static const FunctionInfo functions[] = {
113 {0, nullptr, "BindClientProcess"}, 113 {0, nullptr, "BindClientProcess"},
114 {1, nullptr, "Unknown1"}, 114 {1, nullptr, "QueryAllInterfaces"},
115 {2, nullptr, "Unknown2"}, 115 {2, nullptr, "QueryAvailableInterfaces"},
116 {3, nullptr, "Unknown3"}, 116 {3, nullptr, "QueryAcquiredInterfaces"},
117 {4, nullptr, "Unknown4"}, 117 {4, nullptr, "CreateInterfaceAvailableEvent"},
118 {5, nullptr, "Unknown5"}, 118 {5, nullptr, "DestroyInterfaceAvailableEvent"},
119 {6, nullptr, "GetInterfaceStateChangeEvent"}, 119 {6, nullptr, "GetInterfaceStateChangeEvent"},
120 {7, nullptr, "GetClientIfSession"}, 120 {7, nullptr, "AcquireUsbIf"},
121 {8, nullptr, "Unknown1"},
121 }; 122 };
122 // clang-format on 123 // clang-format on
123 124
@@ -131,11 +132,11 @@ public:
131 // clang-format off 132 // clang-format off
132 static const FunctionInfo functions[] = { 133 static const FunctionInfo functions[] = {
133 {0, nullptr, "BindNoticeEvent"}, 134 {0, nullptr, "BindNoticeEvent"},
134 {1, nullptr, "Unknown1"}, 135 {1, nullptr, "UnbindNoticeEvent"},
135 {2, nullptr, "GetStatus"}, 136 {2, nullptr, "GetStatus"},
136 {3, nullptr, "GetNotice"}, 137 {3, nullptr, "GetNotice"},
137 {4, nullptr, "Unknown2"}, 138 {4, nullptr, "EnablePowerRequestNotice"},
138 {5, nullptr, "Unknown3"}, 139 {5, nullptr, "DisablePowerRequestNotice"},
139 {6, nullptr, "ReplyPowerRequest"}, 140 {6, nullptr, "ReplyPowerRequest"},
140 }; 141 };
141 // clang-format on 142 // clang-format on
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 184537daa..d25fdb1fe 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -6,9 +6,10 @@
6#include <array> 6#include <array>
7#include <cstring> 7#include <cstring>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <type_traits> 10#include <type_traits>
10#include <utility> 11#include <utility>
11#include <boost/optional.hpp> 12
12#include "common/alignment.h" 13#include "common/alignment.h"
13#include "common/assert.h" 14#include "common/assert.h"
14#include "common/common_funcs.h" 15#include "common/common_funcs.h"
@@ -236,6 +237,22 @@ private:
236 Data data{}; 237 Data data{};
237}; 238};
238 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
239class IGBPSetPreallocatedBufferRequestParcel : public Parcel { 256class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
240public: 257public:
241 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) 258 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -506,9 +523,9 @@ private:
506 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; 523 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
507 const u32 width{request.data.width}; 524 const u32 width{request.data.width};
508 const u32 height{request.data.height}; 525 const u32 height{request.data.height};
509 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 526 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
510 527
511 if (slot != boost::none) { 528 if (slot) {
512 // Buffer is available 529 // Buffer is available
513 IGBPDequeueBufferResponseParcel response{*slot}; 530 IGBPDequeueBufferResponseParcel response{*slot};
514 ctx.WriteBuffer(response.Serialize()); 531 ctx.WriteBuffer(response.Serialize());
@@ -520,7 +537,7 @@ private:
520 Kernel::ThreadWakeupReason reason) { 537 Kernel::ThreadWakeupReason reason) {
521 // Repeat TransactParcel DequeueBuffer when a buffer is available 538 // Repeat TransactParcel DequeueBuffer when a buffer is available
522 auto buffer_queue = nv_flinger->GetBufferQueue(id); 539 auto buffer_queue = nv_flinger->GetBufferQueue(id);
523 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 540 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
524 IGBPDequeueBufferResponseParcel response{*slot}; 541 IGBPDequeueBufferResponseParcel response{*slot};
525 ctx.WriteBuffer(response.Serialize()); 542 ctx.WriteBuffer(response.Serialize());
526 IPC::ResponseBuilder rb{ctx, 2}; 543 IPC::ResponseBuilder rb{ctx, 2};
@@ -553,6 +570,12 @@ private:
553 ctx.WriteBuffer(response.Serialize()); 570 ctx.WriteBuffer(response.Serialize());
554 } else if (transaction == TransactionId::CancelBuffer) { 571 } else if (transaction == TransactionId::CancelBuffer) {
555 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());
556 } else { 579 } else {
557 ASSERT_MSG(false, "Unimplemented"); 580 ASSERT_MSG(false, "Unimplemented");
558 } 581 }
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index e562b3a04..7686634bf 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -6,10 +6,11 @@
6 6
7#include <iosfwd> 7#include <iosfwd>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10#include <utility> 11#include <utility>
11#include <vector> 12#include <vector>
12#include <boost/optional.hpp> 13
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "core/file_sys/vfs.h" 15#include "core/file_sys/vfs.h"
15 16
@@ -145,7 +146,7 @@ public:
145 * information. 146 * information.
146 * @returns A pair with the optional system mode, and and the status. 147 * @returns A pair with the optional system mode, and and the status.
147 */ 148 */
148 virtual std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() { 149 virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
149 // 96MB allocated to the application. 150 // 96MB allocated to the application.
150 return std::make_pair(2, ResultStatus::Success); 151 return std::make_pair(2, ResultStatus::Success);
151 } 152 }
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 243b499f2..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"
@@ -127,18 +129,23 @@ static constexpr u32 PageAlignSize(u32 size) {
127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 129 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
128} 130}
129 131
130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { 132/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name,
131 // Read NSO header 133 VAddr load_base) {
132 NroHeader nro_header{}; 134
133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { 135 if (data.size() < sizeof(NroHeader)) {
134 return {}; 136 return {};
135 } 137 }
138
139 // Read NSO header
140 NroHeader nro_header{};
141 std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 142 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
137 return {}; 143 return {};
138 } 144 }
139 145
140 // Build program image 146 // Build program image
141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); 147 std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
148 std::memcpy(program_image.data(), data.data(), program_image.size());
142 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 149 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
143 return {}; 150 return {};
144 } 151 }
@@ -163,17 +170,20 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
163 arg_data.size()); 170 arg_data.size());
164 } 171 }
165 172
166 // Read MOD header
167 ModHeader mod_header{};
168 // 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
169 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{};
170 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,
171 sizeof(ModHeader)); 179 sizeof(ModHeader));
180
172 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')};
173 if (has_mod_header) { 182 if (has_mod_header) {
174 // 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
175 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);
176 } 185 }
186
177 codeset.DataSegment().size += bss_size; 187 codeset.DataSegment().size += bss_size;
178 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 188 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
179 189
@@ -182,11 +192,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 192 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
183 193
184 // Register module with GDBStub 194 // Register module with GDBStub
185 GDBStub::RegisterModule(file.GetName(), load_base, load_base); 195 GDBStub::RegisterModule(name, load_base, load_base);
186 196
187 return true; 197 return true;
188} 198}
189 199
200bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
201 return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base);
202}
203
190ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { 204ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
191 if (is_loaded) { 205 if (is_loaded) {
192 return ResultStatus::ErrorAlreadyLoaded; 206 return ResultStatus::ErrorAlreadyLoaded;
@@ -199,6 +213,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
199 return ResultStatus::ErrorLoadingNRO; 213 return ResultStatus::ErrorLoadingNRO;
200 } 214 }
201 215
216 if (romfs != nullptr)
217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
218
202 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 219 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
203 220
204 is_loaded = true; 221 is_loaded = true;
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 50ee5a78a..3e6959302 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
10#include "core/loader/loader.h" 11#include "core/loader/loader.h"
@@ -40,6 +41,8 @@ public:
40 ResultStatus ReadTitle(std::string& title) override; 41 ResultStatus ReadTitle(std::string& title) override;
41 bool IsRomFSUpdatable() const override; 42 bool IsRomFSUpdatable() const override;
42 43
44 static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base);
45
43private: 46private:
44 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); 47 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 48
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/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 13e57848d..080d89904 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -36,6 +36,16 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
36 36
37 std::tie(nacp_file, icon_file) = 37 std::tie(nacp_file, icon_file) =
38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); 38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
39
40 if (nsp->IsExtractedType()) {
41 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
42 } else {
43 if (title_id == 0)
44 return;
45
46 secondary_loader = std::make_unique<AppLoader_NCA>(
47 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
48 }
39} 49}
40 50
41AppLoader_NSP::~AppLoader_NSP() = default; 51AppLoader_NSP::~AppLoader_NSP() = default;
@@ -67,26 +77,19 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
67 return ResultStatus::ErrorAlreadyLoaded; 77 return ResultStatus::ErrorAlreadyLoaded;
68 } 78 }
69 79
70 if (nsp->IsExtractedType()) { 80 if (title_id == 0)
71 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); 81 return ResultStatus::ErrorNSPMissingProgramNCA;
72 } else {
73 if (title_id == 0)
74 return ResultStatus::ErrorNSPMissingProgramNCA;
75
76 secondary_loader = std::make_unique<AppLoader_NCA>(
77 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
78 82
79 if (nsp->GetStatus() != ResultStatus::Success) 83 if (nsp->GetStatus() != ResultStatus::Success)
80 return nsp->GetStatus(); 84 return nsp->GetStatus();
81 85
82 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) 86 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
83 return nsp->GetProgramStatus(title_id); 87 return nsp->GetProgramStatus(title_id);
84 88
85 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { 89 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
86 if (!Core::Crypto::KeyManager::KeyFileExists(false)) 90 if (!Core::Crypto::KeyManager::KeyFileExists(false))
87 return ResultStatus::ErrorMissingProductionKeyFile; 91 return ResultStatus::ErrorMissingProductionKeyFile;
88 return ResultStatus::ErrorNSPMissingProgramNCA; 92 return ResultStatus::ErrorNSPMissingProgramNCA;
89 }
90 } 93 }
91 94
92 const auto result = secondary_loader->Load(process); 95 const auto result = secondary_loader->Load(process);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 014298ed6..70abd856a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,9 +4,9 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <optional>
7#include <utility> 8#include <utility>
8 9
9#include <boost/optional.hpp>
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h
index 0269c7ff1..940777107 100644
--- a/src/core/memory_hook.h
+++ b/src/core/memory_hook.h
@@ -5,7 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <boost/optional.hpp> 8#include <optional>
9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
11namespace Memory { 12namespace Memory {
@@ -18,19 +19,19 @@ namespace Memory {
18 * 19 *
19 * A hook may be mapped to multiple regions of memory. 20 * A hook may be mapped to multiple regions of memory.
20 * 21 *
21 * If a boost::none or false is returned from a function, the read/write request is passed through 22 * If a std::nullopt or false is returned from a function, the read/write request is passed through
22 * to the underlying memory region. 23 * to the underlying memory region.
23 */ 24 */
24class MemoryHook { 25class MemoryHook {
25public: 26public:
26 virtual ~MemoryHook(); 27 virtual ~MemoryHook();
27 28
28 virtual boost::optional<bool> IsValidAddress(VAddr addr) = 0; 29 virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
29 30
30 virtual boost::optional<u8> Read8(VAddr addr) = 0; 31 virtual std::optional<u8> Read8(VAddr addr) = 0;
31 virtual boost::optional<u16> Read16(VAddr addr) = 0; 32 virtual std::optional<u16> Read16(VAddr addr) = 0;
32 virtual boost::optional<u32> Read32(VAddr addr) = 0; 33 virtual std::optional<u32> Read32(VAddr addr) = 0;
33 virtual boost::optional<u64> Read64(VAddr addr) = 0; 34 virtual std::optional<u64> Read64(VAddr addr) = 0;
34 35
35 virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; 36 virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
36 37
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 7d95816fe..c716a462b 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() {
74} 74}
75 75
76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { 76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
77 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
78 // values increase the time needed to recover and limit framerate again after spikes.
79 constexpr microseconds MAX_LAG_TIME_US = 25000us;
80
81 if (!Settings::values.use_frame_limit) { 77 if (!Settings::values.use_frame_limit) {
82 return; 78 return;
83 } 79 }
diff --git a/src/core/settings.h b/src/core/settings.h
index 8f2da01c8..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
@@ -113,8 +114,10 @@ static const std::array<const char*, NumAnalogs> mapping = {{
113struct Values { 114struct Values {
114 // System 115 // System
115 bool use_docked_mode; 116 bool use_docked_mode;
116 std::string username; 117 bool enable_nfc;
117 int language_index; 118 std::optional<u32> rng_seed;
119 s32 current_user;
120 s32 language_index;
118 121
119 // Controls 122 // Controls
120 std::array<std::string, NativeButton::NumButtons> buttons; 123 std::array<std::string, NativeButton::NumButtons> buttons;
@@ -156,6 +159,7 @@ struct Values {
156 bool use_gdbstub; 159 bool use_gdbstub;
157 u16 gdbstub_port; 160 u16 gdbstub_port;
158 std::string program_args; 161 std::string program_args;
162 bool dump_nso;
159 163
160 // WebService 164 // WebService
161 bool enable_telemetry; 165 bool enable_telemetry;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 0de13edd3..a3b08c740 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -184,4 +184,13 @@ TelemetrySession::~TelemetrySession() {
184 backend = nullptr; 184 backend = nullptr;
185} 185}
186 186
187bool TelemetrySession::SubmitTestcase() {
188#ifdef ENABLE_WEB_SERVICE
189 field_collection.Accept(*backend);
190 return backend->SubmitTestcase();
191#else
192 return false;
193#endif
194}
195
187} // namespace Core 196} // namespace Core
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 2a4845797..023612b79 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -31,6 +31,12 @@ public:
31 field_collection.AddField(type, name, std::move(value)); 31 field_collection.AddField(type, name, std::move(value));
32 } 32 }
33 33
34 /**
35 * Submits a Testcase.
36 * @returns A bool indicating whether the submission succeeded
37 */
38 bool SubmitTestcase();
39
34private: 40private:
35 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session 41 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
36 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields 42 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 37e15bad0..9b8a44fa1 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -64,11 +64,11 @@ void TestEnvironment::ClearWriteRecords() {
64 64
65TestEnvironment::TestMemory::~TestMemory() {} 65TestEnvironment::TestMemory::~TestMemory() {}
66 66
67boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) { 67std::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
68 return true; 68 return true;
69} 69}
70 70
71boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { 71std::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
72 const auto iter = data.find(addr); 72 const auto iter = data.find(addr);
73 73
74 if (iter == data.end()) { 74 if (iter == data.end()) {
@@ -79,15 +79,15 @@ boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
79 return iter->second; 79 return iter->second;
80} 80}
81 81
82boost::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) { 82std::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) {
83 return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8; 83 return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8;
84} 84}
85 85
86boost::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) { 86std::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) {
87 return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16; 87 return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16;
88} 88}
89 89
90boost::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { 90std::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) {
91 return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; 91 return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32;
92} 92}
93 93
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index 5de8dab4e..0b7539601 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -64,12 +64,12 @@ private:
64 64
65 ~TestMemory() override; 65 ~TestMemory() override;
66 66
67 boost::optional<bool> IsValidAddress(VAddr addr) override; 67 std::optional<bool> IsValidAddress(VAddr addr) override;
68 68
69 boost::optional<u8> Read8(VAddr addr) override; 69 std::optional<u8> Read8(VAddr addr) override;
70 boost::optional<u16> Read16(VAddr addr) override; 70 std::optional<u16> Read16(VAddr addr) override;
71 boost::optional<u32> Read32(VAddr addr) override; 71 std::optional<u32> Read32(VAddr addr) override;
72 boost::optional<u64> Read64(VAddr addr) override; 72 std::optional<u64> Read64(VAddr addr) override;
73 73
74 bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override; 74 bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override;
75 75
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 09ecc5bad..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
@@ -51,6 +53,10 @@ add_library(video_core STATIC
51 renderer_opengl/maxwell_to_gl.h 53 renderer_opengl/maxwell_to_gl.h
52 renderer_opengl/renderer_opengl.cpp 54 renderer_opengl/renderer_opengl.cpp
53 renderer_opengl/renderer_opengl.h 55 renderer_opengl/renderer_opengl.h
56 renderer_opengl/utils.cpp
57 renderer_opengl/utils.h
58 surface.cpp
59 surface.h
54 textures/astc.cpp 60 textures/astc.cpp
55 textures/astc.h 61 textures/astc.h
56 textures/decoders.cpp 62 textures/decoders.cpp
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index f1aa6091b..28e8c13aa 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -81,7 +81,7 @@ void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) {
81 for (auto entry : commands) { 81 for (auto entry : commands) {
82 Tegra::GPUVAddr address = entry.Address(); 82 Tegra::GPUVAddr address = entry.Address();
83 u32 size = entry.sz; 83 u32 size = entry.sz;
84 const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); 84 const std::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address);
85 VAddr current_addr = *head_address; 85 VAddr current_addr = *head_address;
86 while (current_addr < *head_address + size * sizeof(CommandHeader)) { 86 while (current_addr < *head_address + size * sizeof(CommandHeader)) {
87 const CommandHeader header = {Memory::Read32(current_addr)}; 87 const CommandHeader header = {Memory::Read32(current_addr)};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index bca014a4a..6de07ea56 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <cstring>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/core_timing.h" 9#include "core/core_timing.h"
@@ -19,21 +20,69 @@ namespace Tegra::Engines {
19constexpr u32 MacroRegistersStart = 0xE00; 20constexpr u32 MacroRegistersStart = 0xE00;
20 21
21Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
22 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} 23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {
24 InitializeRegisterDefaults();
25}
26
27void Maxwell3D::InitializeRegisterDefaults() {
28 // Initializes registers to their default values - what games expect them to be at boot. This is
29 // for certain registers that may not be explicitly set by games.
30
31 // Reset all registers to zero
32 std::memset(&regs, 0, sizeof(regs));
33
34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
35 // needed for ARMS.
36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
37 regs.viewport[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f;
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;
69}
23 70
24void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 71void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
25 // Reset the current macro. 72 // Reset the current macro.
26 executing_macro = 0; 73 executing_macro = 0;
27 74
28 // The requested macro must have been uploaded already. 75 // Lookup the macro offset
29 auto macro_code = uploaded_macros.find(method); 76 const u32 entry{(method - MacroRegistersStart) >> 1};
30 if (macro_code == uploaded_macros.end()) { 77 const auto& search{macro_offsets.find(entry)};
31 LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method); 78 if (search == macro_offsets.end()) {
79 LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method);
80 UNREACHABLE();
32 return; 81 return;
33 } 82 }
34 83
35 // Execute the current macro. 84 // Execute the current macro.
36 macro_interpreter.Execute(macro_code->second, std::move(parameters)); 85 macro_interpreter.Execute(search->second, std::move(parameters));
37} 86}
38 87
39void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { 88void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
@@ -72,13 +121,23 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
72 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); 121 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
73 } 122 }
74 123
75 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 }
76 131
77 switch (method) { 132 switch (method) {
78 case MAXWELL3D_REG_INDEX(macros.data): { 133 case MAXWELL3D_REG_INDEX(macros.data): {
79 ProcessMacroUpload(value); 134 ProcessMacroUpload(value);
80 break; 135 break;
81 } 136 }
137 case MAXWELL3D_REG_INDEX(macros.bind): {
138 ProcessMacroBind(value);
139 break;
140 }
82 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): 141 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
83 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): 142 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
84 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): 143 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
@@ -140,22 +199,25 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
140} 199}
141 200
142void Maxwell3D::ProcessMacroUpload(u32 data) { 201void Maxwell3D::ProcessMacroUpload(u32 data) {
143 // Store the uploaded macro code to interpret them when they're called. 202 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
144 auto& macro = uploaded_macros[regs.macros.entry * 2 + MacroRegistersStart]; 203 "upload_address exceeded macro_memory size!");
145 macro.push_back(data); 204 macro_memory[regs.macros.upload_address++] = data;
205}
206
207void Maxwell3D::ProcessMacroBind(u32 data) {
208 macro_offsets[regs.macros.entry] = data;
146} 209}
147 210
148void Maxwell3D::ProcessQueryGet() { 211void Maxwell3D::ProcessQueryGet() {
149 GPUVAddr sequence_address = regs.query.QueryAddress(); 212 GPUVAddr sequence_address = regs.query.QueryAddress();
150 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application 213 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application
151 // VAddr before writing. 214 // VAddr before writing.
152 boost::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address); 215 std::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address);
153 216
154 // TODO(Subv): Support the other query units. 217 // TODO(Subv): Support the other query units.
155 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 218 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
156 "Units other than CROP are unimplemented"); 219 "Units other than CROP are unimplemented");
157 220
158 u32 value = Memory::Read32(*address);
159 u64 result = 0; 221 u64 result = 0;
160 222
161 // TODO(Subv): Support the other query variables 223 // TODO(Subv): Support the other query variables
@@ -268,7 +330,7 @@ void Maxwell3D::ProcessCBData(u32 value) {
268 // Don't allow writing past the end of the buffer. 330 // Don't allow writing past the end of the buffer.
269 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size); 331 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
270 332
271 boost::optional<VAddr> address = 333 std::optional<VAddr> address =
272 memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); 334 memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
273 335
274 Memory::Write32(*address, value); 336 Memory::Write32(*address, value);
@@ -281,7 +343,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
281 GPUVAddr tic_base_address = regs.tic.TICAddress(); 343 GPUVAddr tic_base_address = regs.tic.TICAddress();
282 344
283 GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry); 345 GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
284 boost::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu); 346 std::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
285 347
286 Texture::TICEntry tic_entry; 348 Texture::TICEntry tic_entry;
287 Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); 349 Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
@@ -305,7 +367,7 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
305 GPUVAddr tsc_base_address = regs.tsc.TSCAddress(); 367 GPUVAddr tsc_base_address = regs.tsc.TSCAddress();
306 368
307 GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry); 369 GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
308 boost::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu); 370 std::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
309 371
310 Texture::TSCEntry tsc_entry; 372 Texture::TSCEntry tsc_entry;
311 Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry)); 373 Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry));
@@ -369,7 +431,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
369 431
370 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); 432 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
371 433
372 boost::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); 434 std::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
373 Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)}; 435 Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
374 436
375 Texture::FullTextureInfo tex_info{}; 437 Texture::FullTextureInfo tex_info{};
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 0e09a7ee5..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)) {
@@ -475,12 +493,13 @@ public:
475 INSERT_PADDING_WORDS(0x45); 493 INSERT_PADDING_WORDS(0x45);
476 494
477 struct { 495 struct {
478 INSERT_PADDING_WORDS(1); 496 u32 upload_address;
479 u32 data; 497 u32 data;
480 u32 entry; 498 u32 entry;
499 u32 bind;
481 } macros; 500 } macros;
482 501
483 INSERT_PADDING_WORDS(0x189); 502 INSERT_PADDING_WORDS(0x188);
484 503
485 u32 tfb_enabled; 504 u32 tfb_enabled;
486 505
@@ -570,7 +589,11 @@ public:
570 u32 stencil_back_mask; 589 u32 stencil_back_mask;
571 u32 stencil_back_func_mask; 590 u32 stencil_back_func_mask;
572 591
573 INSERT_PADDING_WORDS(0x13); 592 INSERT_PADDING_WORDS(0xC);
593
594 u32 color_mask_common;
595
596 INSERT_PADDING_WORDS(0x6);
574 597
575 u32 rt_separate_frag_data; 598 u32 rt_separate_frag_data;
576 599
@@ -645,8 +668,14 @@ public:
645 ComparisonOp depth_test_func; 668 ComparisonOp depth_test_func;
646 float alpha_test_ref; 669 float alpha_test_ref;
647 ComparisonOp alpha_test_func; 670 ComparisonOp alpha_test_func;
648 671 u32 draw_tfb_stride;
649 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);
650 679
651 struct { 680 struct {
652 u32 separate_alpha; 681 u32 separate_alpha;
@@ -723,7 +752,11 @@ public:
723 StencilOp stencil_back_op_zpass; 752 StencilOp stencil_back_op_zpass;
724 ComparisonOp stencil_back_func_func; 753 ComparisonOp stencil_back_func_func;
725 754
726 INSERT_PADDING_WORDS(0x17); 755 INSERT_PADDING_WORDS(0x4);
756
757 u32 framebuffer_srgb;
758
759 INSERT_PADDING_WORDS(0x12);
727 760
728 union { 761 union {
729 BitField<2, 1, u32> coord_origin; 762 BitField<2, 1, u32> coord_origin;
@@ -751,7 +784,14 @@ public:
751 }; 784 };
752 } draw; 785 } draw;
753 786
754 INSERT_PADDING_WORDS(0x6B); 787 INSERT_PADDING_WORDS(0xA);
788
789 struct {
790 u32 enabled;
791 u32 index;
792 } primitive_restart;
793
794 INSERT_PADDING_WORDS(0x5F);
755 795
756 struct { 796 struct {
757 u32 start_addr_high; 797 u32 start_addr_high;
@@ -829,8 +869,9 @@ public:
829 BitField<6, 4, u32> RT; 869 BitField<6, 4, u32> RT;
830 BitField<10, 11, u32> layer; 870 BitField<10, 11, u32> layer;
831 } clear_buffers; 871 } clear_buffers;
832 872 INSERT_PADDING_WORDS(0xB);
833 INSERT_PADDING_WORDS(0x4B); 873 std::array<ColorMask, NumRenderTargets> color_mask;
874 INSERT_PADDING_WORDS(0x38);
834 875
835 struct { 876 struct {
836 u32 query_address_high; 877 u32 query_address_high;
@@ -971,6 +1012,12 @@ public:
971 State state{}; 1012 State state{};
972 MemoryManager& memory_manager; 1013 MemoryManager& memory_manager;
973 1014
1015 struct DirtyFlags {
1016 bool vertex_attrib_format = true;
1017 };
1018
1019 DirtyFlags dirty_flags;
1020
974 /// Reads a register value located at the input method address 1021 /// Reads a register value located at the input method address
975 u32 GetRegisterValue(u32 method) const; 1022 u32 GetRegisterValue(u32 method) const;
976 1023
@@ -983,10 +1030,25 @@ public:
983 /// Returns the texture information for a specific texture in a specific shader stage. 1030 /// Returns the texture information for a specific texture in a specific shader stage.
984 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; 1031 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
985 1032
1033 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
1034 /// we've seen used.
1035 using MacroMemory = std::array<u32, 0x40000>;
1036
1037 /// Gets a reference to macro memory.
1038 const MacroMemory& GetMacroMemory() const {
1039 return macro_memory;
1040 }
1041
986private: 1042private:
1043 void InitializeRegisterDefaults();
1044
987 VideoCore::RasterizerInterface& rasterizer; 1045 VideoCore::RasterizerInterface& rasterizer;
988 1046
989 std::unordered_map<u32, std::vector<u32>> uploaded_macros; 1047 /// Start offsets of each macro in macro_memory
1048 std::unordered_map<u32, u32> macro_offsets;
1049
1050 /// Memory for macro code
1051 MacroMemory macro_memory;
990 1052
991 /// Macro method that is currently being executed / being fed parameters. 1053 /// Macro method that is currently being executed / being fed parameters.
992 u32 executing_macro = 0; 1054 u32 executing_macro = 0;
@@ -1009,9 +1071,12 @@ private:
1009 */ 1071 */
1010 void CallMacroMethod(u32 method, std::vector<u32> parameters); 1072 void CallMacroMethod(u32 method, std::vector<u32> parameters);
1011 1073
1012 /// Handles writes to the macro uploading registers. 1074 /// Handles writes to the macro uploading register.
1013 void ProcessMacroUpload(u32 data); 1075 void ProcessMacroUpload(u32 data);
1014 1076
1077 /// Handles writes to the macro bind register.
1078 void ProcessMacroBind(u32 data);
1079
1015 /// Handles a write to the CLEAR_BUFFERS register. 1080 /// Handles a write to the CLEAR_BUFFERS register.
1016 void ProcessClearBuffers(); 1081 void ProcessClearBuffers();
1017 1082
@@ -1045,6 +1110,7 @@ ASSERT_REG_POSITION(scissor_test, 0x380);
1045ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 1110ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
1046ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 1111ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
1047ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 1112ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
1113ASSERT_REG_POSITION(color_mask_common, 0x3E4);
1048ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); 1114ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
1049ASSERT_REG_POSITION(zeta, 0x3F8); 1115ASSERT_REG_POSITION(zeta, 0x3F8);
1050ASSERT_REG_POSITION(vertex_attrib_format, 0x458); 1116ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
@@ -1057,6 +1123,10 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
1057ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); 1123ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
1058ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); 1124ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
1059ASSERT_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);
1060ASSERT_REG_POSITION(blend, 0x4CF); 1130ASSERT_REG_POSITION(blend, 0x4CF);
1061ASSERT_REG_POSITION(stencil_enable, 0x4E0); 1131ASSERT_REG_POSITION(stencil_enable, 0x4E0);
1062ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1); 1132ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1);
@@ -1077,14 +1147,17 @@ ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
1077ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); 1147ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
1078ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); 1148ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
1079ASSERT_REG_POSITION(stencil_back_func_func, 0x569); 1149ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
1150ASSERT_REG_POSITION(framebuffer_srgb, 0x56E);
1080ASSERT_REG_POSITION(point_coord_replace, 0x581); 1151ASSERT_REG_POSITION(point_coord_replace, 0x581);
1081ASSERT_REG_POSITION(code_address, 0x582); 1152ASSERT_REG_POSITION(code_address, 0x582);
1082ASSERT_REG_POSITION(draw, 0x585); 1153ASSERT_REG_POSITION(draw, 0x585);
1154ASSERT_REG_POSITION(primitive_restart, 0x591);
1083ASSERT_REG_POSITION(index_array, 0x5F2); 1155ASSERT_REG_POSITION(index_array, 0x5F2);
1084ASSERT_REG_POSITION(instanced_arrays, 0x620); 1156ASSERT_REG_POSITION(instanced_arrays, 0x620);
1085ASSERT_REG_POSITION(cull, 0x646); 1157ASSERT_REG_POSITION(cull, 0x646);
1086ASSERT_REG_POSITION(logic_op, 0x671); 1158ASSERT_REG_POSITION(logic_op, 0x671);
1087ASSERT_REG_POSITION(clear_buffers, 0x674); 1159ASSERT_REG_POSITION(clear_buffers, 0x674);
1160ASSERT_REG_POSITION(color_mask, 0x680);
1088ASSERT_REG_POSITION(query, 0x6C0); 1161ASSERT_REG_POSITION(query, 0x6C0);
1089ASSERT_REG_POSITION(vertex_array[0], 0x700); 1162ASSERT_REG_POSITION(vertex_array[0], 0x700);
1090ASSERT_REG_POSITION(independent_blend, 0x780); 1163ASSERT_REG_POSITION(independent_blend, 0x780);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 6cd08d28b..83a6fd875 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -5,12 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <bitset> 7#include <bitset>
8#include <optional>
8#include <string> 9#include <string>
9#include <tuple> 10#include <tuple>
10#include <vector> 11#include <vector>
11 12
12#include <boost/optional.hpp>
13
14#include "common/assert.h" 13#include "common/assert.h"
15#include "common/bit_field.h" 14#include "common/bit_field.h"
16#include "common/common_types.h" 15#include "common/common_types.h"
@@ -79,6 +78,7 @@ union Attribute {
79 constexpr explicit Attribute(u64 value) : value(value) {} 78 constexpr explicit Attribute(u64 value) : value(value) {}
80 79
81 enum class Index : u64 { 80 enum class Index : u64 {
81 PointSize = 6,
82 Position = 7, 82 Position = 7,
83 Attribute_0 = 8, 83 Attribute_0 = 8,
84 Attribute_31 = 39, 84 Attribute_31 = 39,
@@ -207,6 +207,16 @@ enum class UniformType : u64 {
207 Double = 5, 207 Double = 5,
208}; 208};
209 209
210enum class StoreType : u64 {
211 Unsigned8 = 0,
212 Signed8 = 1,
213 Unsigned16 = 2,
214 Signed16 = 3,
215 Bytes32 = 4,
216 Bytes64 = 5,
217 Bytes128 = 6,
218};
219
210enum class IMinMaxExchange : u64 { 220enum class IMinMaxExchange : u64 {
211 None = 0, 221 None = 0,
212 XLo = 1, 222 XLo = 1,
@@ -568,6 +578,10 @@ union Instruction {
568 } fmul32; 578 } fmul32;
569 579
570 union { 580 union {
581 BitField<52, 1, u64> generates_cc;
582 } op_32;
583
584 union {
571 BitField<48, 1, u64> is_signed; 585 BitField<48, 1, u64> is_signed;
572 } shift; 586 } shift;
573 587
@@ -747,6 +761,18 @@ union Instruction {
747 } ld_c; 761 } ld_c;
748 762
749 union { 763 union {
764 BitField<48, 3, StoreType> type;
765 } ldst_sl;
766
767 union {
768 BitField<44, 2, u64> unknown;
769 } ld_l;
770
771 union {
772 BitField<44, 2, u64> unknown;
773 } st_l;
774
775 union {
750 BitField<0, 3, u64> pred0; 776 BitField<0, 3, u64> pred0;
751 BitField<3, 3, u64> pred3; 777 BitField<3, 3, u64> pred3;
752 BitField<7, 1, u64> abs_a; 778 BitField<7, 1, u64> abs_a;
@@ -1208,6 +1234,8 @@ union Instruction {
1208 BitField<61, 1, u64> is_b_imm; 1234 BitField<61, 1, u64> is_b_imm;
1209 BitField<60, 1, u64> is_b_gpr; 1235 BitField<60, 1, u64> is_b_gpr;
1210 BitField<59, 1, u64> is_c_gpr; 1236 BitField<59, 1, u64> is_c_gpr;
1237 BitField<20, 24, s64> smem_imm;
1238 BitField<0, 5, ControlCode> flow_control_code;
1211 1239
1212 Attribute attribute; 1240 Attribute attribute;
1213 Sampler sampler; 1241 Sampler sampler;
@@ -1231,8 +1259,12 @@ public:
1231 BRA, 1259 BRA,
1232 PBK, 1260 PBK,
1233 LD_A, 1261 LD_A,
1262 LD_L,
1263 LD_S,
1234 LD_C, 1264 LD_C,
1235 ST_A, 1265 ST_A,
1266 ST_L,
1267 ST_S,
1236 LDG, // Load from global memory 1268 LDG, // Load from global memory
1237 STG, // Store in global memory 1269 STG, // Store in global memory
1238 TEX, 1270 TEX,
@@ -1428,7 +1460,7 @@ public:
1428 Type type; 1460 Type type;
1429 }; 1461 };
1430 1462
1431 static boost::optional<const Matcher&> Decode(Instruction instr) { 1463 static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) {
1432 static const auto table{GetDecodeTable()}; 1464 static const auto table{GetDecodeTable()};
1433 1465
1434 const auto matches_instruction = [instr](const auto& matcher) { 1466 const auto matches_instruction = [instr](const auto& matcher) {
@@ -1436,7 +1468,8 @@ public:
1436 }; 1468 };
1437 1469
1438 auto iter = std::find_if(table.begin(), table.end(), matches_instruction); 1470 auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
1439 return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none; 1471 return iter != table.end() ? std::optional<std::reference_wrapper<const Matcher>>(*iter)
1472 : std::nullopt;
1440 } 1473 }
1441 1474
1442private: 1475private:
@@ -1489,8 +1522,12 @@ private:
1489 INST("111000110100---", Id::BRK, Type::Flow, "BRK"), 1522 INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
1490 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), 1523 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
1491 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 1524 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
1525 INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
1526 INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
1492 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 1527 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
1493 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 1528 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
1529 INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"),
1530 INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
1494 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), 1531 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
1495 INST("1110111011011---", Id::STG, Type::Memory, "STG"), 1532 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
1496 INST("110000----111---", Id::TEX, Type::Memory, "TEX"), 1533 INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
@@ -1626,4 +1663,4 @@ private:
1626 } 1663 }
1627}; 1664};
1628 1665
1629} // namespace Tegra::Shader \ No newline at end of file 1666} // namespace Tegra::Shader
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h
index a885ee3cf..a0e015c4b 100644
--- a/src/video_core/engines/shader_header.h
+++ b/src/video_core/engines/shader_header.h
@@ -96,6 +96,11 @@ struct Header {
96 } 96 }
97 } ps; 97 } ps;
98 }; 98 };
99
100 u64 GetLocalMemorySize() {
101 return (common1.shader_local_memory_low_size |
102 (common2.shader_local_memory_high_size << 24));
103 }
99}; 104};
100 105
101static_assert(sizeof(Header) == 0x50, "Incorrect structure size"); 106static_assert(sizeof(Header) == 0x50, "Incorrect structure size");
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 377bd66ab..335a8d407 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -11,7 +11,7 @@ namespace Tegra {
11 11
12MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} 12MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
13 13
14void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> parameters) { 14void MacroInterpreter::Execute(u32 offset, std::vector<u32> parameters) {
15 Reset(); 15 Reset();
16 registers[1] = parameters[0]; 16 registers[1] = parameters[0];
17 this->parameters = std::move(parameters); 17 this->parameters = std::move(parameters);
@@ -19,7 +19,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa
19 // Execute the code until we hit an exit condition. 19 // Execute the code until we hit an exit condition.
20 bool keep_executing = true; 20 bool keep_executing = true;
21 while (keep_executing) { 21 while (keep_executing) {
22 keep_executing = Step(code, false); 22 keep_executing = Step(offset, false);
23 } 23 }
24 24
25 // Assert the the macro used all the input parameters 25 // Assert the the macro used all the input parameters
@@ -29,7 +29,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa
29void MacroInterpreter::Reset() { 29void MacroInterpreter::Reset() {
30 registers = {}; 30 registers = {};
31 pc = 0; 31 pc = 0;
32 delayed_pc = boost::none; 32 delayed_pc = {};
33 method_address.raw = 0; 33 method_address.raw = 0;
34 parameters.clear(); 34 parameters.clear();
35 // The next parameter index starts at 1, because $r1 already has the value of the first 35 // The next parameter index starts at 1, because $r1 already has the value of the first
@@ -37,17 +37,17 @@ void MacroInterpreter::Reset() {
37 next_parameter_index = 1; 37 next_parameter_index = 1;
38} 38}
39 39
40bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) { 40bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
41 u32 base_address = pc; 41 u32 base_address = pc;
42 42
43 Opcode opcode = GetOpcode(code); 43 Opcode opcode = GetOpcode(offset);
44 pc += 4; 44 pc += 4;
45 45
46 // Update the program counter if we were delayed 46 // Update the program counter if we were delayed
47 if (delayed_pc != boost::none) { 47 if (delayed_pc) {
48 ASSERT(is_delay_slot); 48 ASSERT(is_delay_slot);
49 pc = *delayed_pc; 49 pc = *delayed_pc;
50 delayed_pc = boost::none; 50 delayed_pc = {};
51 } 51 }
52 52
53 switch (opcode.operation) { 53 switch (opcode.operation) {
@@ -108,7 +108,7 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
108 108
109 delayed_pc = base_address + opcode.GetBranchTarget(); 109 delayed_pc = base_address + opcode.GetBranchTarget();
110 // Execute one more instruction due to the delay slot. 110 // Execute one more instruction due to the delay slot.
111 return Step(code, true); 111 return Step(offset, true);
112 } 112 }
113 break; 113 break;
114 } 114 }
@@ -121,17 +121,18 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
121 // Exit has a delay slot, execute the next instruction 121 // Exit has a delay slot, execute the next instruction
122 // Note: Executing an exit during a branch delay slot will cause the instruction at the 122 // Note: Executing an exit during a branch delay slot will cause the instruction at the
123 // branch target to be executed before exiting. 123 // branch target to be executed before exiting.
124 Step(code, true); 124 Step(offset, true);
125 return false; 125 return false;
126 } 126 }
127 127
128 return true; 128 return true;
129} 129}
130 130
131MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector<u32>& code) const { 131MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
132 const auto& macro_memory{maxwell3d.GetMacroMemory()};
132 ASSERT((pc % sizeof(u32)) == 0); 133 ASSERT((pc % sizeof(u32)) == 0);
133 ASSERT(pc < code.size() * sizeof(u32)); 134 ASSERT((pc + offset) < macro_memory.size() * sizeof(u32));
134 return {code[pc / sizeof(u32)]}; 135 return {macro_memory[offset + pc / sizeof(u32)]};
135} 136}
136 137
137u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { 138u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h
index cee0baaf3..62d1ce289 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro_interpreter.h
@@ -5,8 +5,9 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8#include <vector> 9#include <vector>
9#include <boost/optional.hpp> 10
10#include "common/bit_field.h" 11#include "common/bit_field.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
@@ -21,10 +22,10 @@ public:
21 22
22 /** 23 /**
23 * Executes the macro code with the specified input parameters. 24 * Executes the macro code with the specified input parameters.
24 * @param code The macro byte code to execute 25 * @param offset Offset to start execution at.
25 * @param parameters The parameters of the macro 26 * @param parameters The parameters of the macro.
26 */ 27 */
27 void Execute(const std::vector<u32>& code, std::vector<u32> parameters); 28 void Execute(u32 offset, std::vector<u32> parameters);
28 29
29private: 30private:
30 enum class Operation : u32 { 31 enum class Operation : u32 {
@@ -109,11 +110,11 @@ private:
109 /** 110 /**
110 * Executes a single macro instruction located at the current program counter. Returns whether 111 * Executes a single macro instruction located at the current program counter. Returns whether
111 * the interpreter should keep running. 112 * the interpreter should keep running.
112 * @param code The macro code to execute. 113 * @param offset Offset to start execution at.
113 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a 114 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a
114 * previous instruction. 115 * previous instruction.
115 */ 116 */
116 bool Step(const std::vector<u32>& code, bool is_delay_slot); 117 bool Step(u32 offset, bool is_delay_slot);
117 118
118 /// Calculates the result of an ALU operation. src_a OP src_b; 119 /// Calculates the result of an ALU operation. src_a OP src_b;
119 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; 120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
@@ -126,7 +127,7 @@ private:
126 bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; 127 bool EvaluateBranchCondition(BranchCondition cond, u32 value) const;
127 128
128 /// Reads an opcode at the current program counter location. 129 /// Reads an opcode at the current program counter location.
129 Opcode GetOpcode(const std::vector<u32>& code) const; 130 Opcode GetOpcode(u32 offset) const;
130 131
131 /// Returns the specified register's value. Register 0 is hardcoded to always return 0. 132 /// Returns the specified register's value. Register 0 is hardcoded to always return 0.
132 u32 GetRegister(u32 register_id) const; 133 u32 GetRegister(u32 register_id) const;
@@ -149,7 +150,7 @@ private:
149 Engines::Maxwell3D& maxwell3d; 150 Engines::Maxwell3D& maxwell3d;
150 151
151 u32 pc; ///< Current program counter 152 u32 pc; ///< Current program counter
152 boost::optional<u32> 153 std::optional<u32>
153 delayed_pc; ///< Program counter to execute at after the delay slot is executed. 154 delayed_pc; ///< Program counter to execute at after the delay slot is executed.
154 155
155 static constexpr std::size_t NumMacroRegisters = 8; 156 static constexpr std::size_t NumMacroRegisters = 8;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 022d4ab74..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 boost::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 boost::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
100boost::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;
@@ -118,8 +143,8 @@ boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
118 return {}; 143 return {};
119} 144}
120 145
121boost::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 caf80093f..4eb338aa2 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -6,10 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <vector> 10#include <vector>
10 11
11#include <boost/optional.hpp>
12
13#include "common/common_types.h" 12#include "common/common_types.h"
14 13
15namespace Tegra { 14namespace Tegra {
@@ -27,7 +26,7 @@ public:
27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 26 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
29 GPUVAddr GetRegionEnd(GPUVAddr region_start) const; 28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
30 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
31 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; 30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
32 31
33 static constexpr u64 PAGE_BITS = 16; 32 static constexpr u64 PAGE_BITS = 16;
@@ -35,15 +34,15 @@ public:
35 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
36 35
37private: 36private:
38 boost::optional<GPUVAddr> FindFreeBlock(u64 size, u64 align = 1);
39 bool IsPageMapped(GPUVAddr gpu_addr);
40 VAddr& PageSlot(GPUVAddr gpu_addr);
41
42 enum class PageStatus : u64 { 37 enum class PageStatus : u64 {
43 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 38 Unmapped = 0xFFFFFFFFFFFFFFFFULL,
44 Allocated = 0xFFFFFFFFFFFFFFFEULL, 39 Allocated = 0xFFFFFFFFFFFFFFFEULL,
45 }; 40 };
46 41
42 std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align,
43 PageStatus status);
44 VAddr& PageSlot(GPUVAddr gpu_addr);
45
47 static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; 46 static constexpr u64 MAX_ADDRESS{0x10000000000ULL};
48 static constexpr u64 PAGE_TABLE_BITS{10}; 47 static constexpr u64 PAGE_TABLE_BITS{10};
49 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_base.h b/src/video_core/renderer_base.h
index 2cd0738ff..669e26e15 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -6,7 +6,8 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <memory> 8#include <memory>
9#include <boost/optional.hpp> 9#include <optional>
10
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12#include "video_core/rasterizer_interface.h" 13#include "video_core/rasterizer_interface.h"
@@ -28,7 +29,8 @@ public:
28 virtual ~RendererBase(); 29 virtual ~RendererBase();
29 30
30 /// Swap buffers (render frame) 31 /// Swap buffers (render frame)
31 virtual void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) = 0; 32 virtual void SwapBuffers(
33 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
32 34
33 /// Initialize the renderer 35 /// Initialize the renderer
34 virtual bool Init() = 0; 36 virtual bool Init() = 0;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index c142095c5..075192c3f 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -9,15 +9,17 @@
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) {
19 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 21 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
20 const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; 22 const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
21 23
22 // Cache management is a big overhead, so only cache entries with a given size. 24 // Cache management is a big overhead, so only cache entries with a given size.
23 // TODO: Figure out which size is the best for given games. 25 // TODO: Figure out which size is the best for given games.
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 ee1d9601b..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"
@@ -45,7 +46,7 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
45 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); 46 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
46 47
47 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 48 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
48 const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; 49 const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
49 const u8* source{Memory::GetPointer(*cpu_addr)}; 50 const u8* source{Memory::GetPointer(*cpu_addr)};
50 51
51 for (u32 primitive = 0; primitive < count / 4; ++primitive) { 52 for (u32 primitive = 0; primitive < count / 4; ++primitive) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index be51c5215..84bd91eed 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -30,10 +30,11 @@
30namespace OpenGL { 30namespace OpenGL {
31 31
32using Maxwell = Tegra::Engines::Maxwell3D::Regs; 32using Maxwell = Tegra::Engines::Maxwell3D::Regs;
33using PixelFormat = SurfaceParams::PixelFormat; 33using PixelFormat = VideoCore::Surface::PixelFormat;
34using SurfaceType = SurfaceParams::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();
@@ -104,9 +106,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
104 } 106 }
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 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();
@@ -115,8 +115,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
115 state.draw.shader_program = 0; 115 state.draw.shader_program = 0;
116 state.Apply(); 116 state.Apply();
117 117
118 glEnable(GL_BLEND);
119
120 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); 118 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
121 119
122 LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); 120 LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!");
@@ -124,18 +122,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
124 122
125RasterizerOpenGL::~RasterizerOpenGL() {} 123RasterizerOpenGL::~RasterizerOpenGL() {}
126 124
127void RasterizerOpenGL::SetupVertexArrays() { 125void RasterizerOpenGL::SetupVertexFormat() {
128 MICROPROFILE_SCOPE(OpenGL_VAO); 126 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
129 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
130 const auto& regs = gpu.regs; 127 const auto& regs = gpu.regs;
131 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
132 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);
133 auto& VAO = iter->second; 136 auto& VAO = iter->second;
134 137
135 if (is_cache_miss) { 138 if (is_cache_miss) {
136 VAO.Create(); 139 VAO.Create();
137 state.draw.vertex_array = VAO.handle; 140 state.draw.vertex_array = VAO.handle;
138 state.Apply(); 141 state.ApplyVertexBufferState();
139 142
140 // 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
141 // around. 144 // around.
@@ -177,8 +180,13 @@ void RasterizerOpenGL::SetupVertexArrays() {
177 } 180 }
178 } 181 }
179 state.draw.vertex_array = VAO.handle; 182 state.draw.vertex_array = VAO.handle;
180 state.draw.vertex_buffer = buffer_cache.GetHandle(); 183 state.ApplyVertexBufferState();
181 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;
182 190
183 // Upload all guest vertex arrays sequentially to our buffer 191 // Upload all guest vertex arrays sequentially to our buffer
184 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 192 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -205,6 +213,9 @@ void RasterizerOpenGL::SetupVertexArrays() {
205 glVertexBindingDivisor(index, 0); 213 glVertexBindingDivisor(index, 0);
206 } 214 }
207 } 215 }
216
217 // Implicit set by glBindVertexBuffer. Stupid glstate handling...
218 state.draw.vertex_buffer = buffer_cache.GetHandle();
208} 219}
209 220
210DrawParameters RasterizerOpenGL::SetupDraw() { 221DrawParameters RasterizerOpenGL::SetupDraw() {
@@ -329,8 +340,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
329 index++; 340 index++;
330 } 341 }
331 } 342 }
332
333 state.Apply();
334} 343}
335 344
336std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 345std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -399,9 +408,9 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
399 cached_pages.add({pages_interval, delta}); 408 cached_pages.add({pages_interval, delta});
400} 409}
401 410
402void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, 411void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb,
403 bool preserve_contents, 412 bool using_depth_fb, bool preserve_contents,
404 boost::optional<std::size_t> single_color_target) { 413 std::optional<std::size_t> single_color_target) {
405 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 414 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
406 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 415 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
407 416
@@ -416,8 +425,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
416 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented"); 425 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
417 426
418 // Bind the framebuffer surfaces 427 // Bind the framebuffer surfaces
419 state.draw.draw_framebuffer = framebuffer.handle; 428 current_state.draw.draw_framebuffer = framebuffer.handle;
420 state.Apply(); 429 current_state.ApplyFramebufferState();
430 current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
421 431
422 if (using_color_fb) { 432 if (using_color_fb) {
423 if (single_color_target) { 433 if (single_color_target) {
@@ -429,6 +439,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
429 // Assume that a surface will be written to if it is used as a framebuffer, even if 439 // Assume that a surface will be written to if it is used as a framebuffer, even if
430 // the shader doesn't actually write to it. 440 // the shader doesn't actually write to it.
431 color_surface->MarkAsModified(true, res_cache); 441 color_surface->MarkAsModified(true, res_cache);
442 // Workaround for and issue in nvidia drivers
443 // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
444 state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
432 } 445 }
433 446
434 glFramebufferTexture2D( 447 glFramebufferTexture2D(
@@ -446,6 +459,11 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
446 // Assume that a surface will be written to if it is used as a framebuffer, even 459 // Assume that a surface will be written to if it is used as a framebuffer, even
447 // if the shader doesn't actually write to it. 460 // if the shader doesn't actually write to it.
448 color_surface->MarkAsModified(true, res_cache); 461 color_surface->MarkAsModified(true, res_cache);
462 // Enable sRGB only for supported formats
463 // Workaround for and issue in nvidia drivers
464 // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
465 state.framebuffer_srgb.enabled |=
466 color_surface->GetSurfaceParams().srgb_conversion;
449 } 467 }
450 468
451 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 469 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
@@ -487,10 +505,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
487 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,
488 0); 506 0);
489 } 507 }
490 508 SyncViewport(current_state);
491 SyncViewport();
492
493 state.Apply();
494} 509}
495 510
496void RasterizerOpenGL::Clear() { 511void RasterizerOpenGL::Clear() {
@@ -503,22 +518,23 @@ void RasterizerOpenGL::Clear() {
503 bool use_stencil{}; 518 bool use_stencil{};
504 519
505 OpenGLState clear_state; 520 OpenGLState clear_state;
506 clear_state.draw.draw_framebuffer = framebuffer.handle;
507 clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
508 clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
509 clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
510 clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
511
512 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 ||
513 regs.clear_buffers.A) { 522 regs.clear_buffers.A) {
514 use_color = true; 523 use_color = true;
515 } 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 }
516 if (regs.clear_buffers.Z) { 531 if (regs.clear_buffers.Z) {
517 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!");
518 use_depth = true; 533 use_depth = true;
519 534
520 // 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
521 // 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.
522 clear_state.depth.test_enabled = true; 538 clear_state.depth.test_enabled = true;
523 clear_state.depth.test_func = GL_ALWAYS; 539 clear_state.depth.test_func = GL_ALWAYS;
524 } 540 }
@@ -535,9 +551,8 @@ void RasterizerOpenGL::Clear() {
535 551
536 ScopeAcquireGLContext acquire_context{emu_window}; 552 ScopeAcquireGLContext acquire_context{emu_window};
537 553
538 ConfigureFramebuffers(use_color, use_depth || use_stencil, false, 554 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
539 regs.clear_buffers.RT.Value()); 555 regs.clear_buffers.RT.Value());
540
541 clear_state.Apply(); 556 clear_state.Apply();
542 557
543 if (use_color) { 558 if (use_color) {
@@ -563,13 +578,14 @@ void RasterizerOpenGL::DrawArrays() {
563 578
564 ScopeAcquireGLContext acquire_context{emu_window}; 579 ScopeAcquireGLContext acquire_context{emu_window};
565 580
566 ConfigureFramebuffers(); 581 ConfigureFramebuffers(state);
567 582 SyncColorMask();
568 SyncDepthTestState(); 583 SyncDepthTestState();
569 SyncStencilTestState(); 584 SyncStencilTestState();
570 SyncBlendState(); 585 SyncBlendState();
571 SyncLogicOpState(); 586 SyncLogicOpState();
572 SyncCullMode(); 587 SyncCullMode();
588 SyncPrimitiveRestart();
573 SyncScissorTest(); 589 SyncScissorTest();
574 // Alpha Testing is synced on shaders. 590 // Alpha Testing is synced on shaders.
575 SyncTransformFeedback(); 591 SyncTransformFeedback();
@@ -583,7 +599,7 @@ void RasterizerOpenGL::DrawArrays() {
583 const bool is_indexed = accelerate_draw == AccelDraw::Indexed; 599 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
584 600
585 state.draw.vertex_buffer = buffer_cache.GetHandle(); 601 state.draw.vertex_buffer = buffer_cache.GetHandle();
586 state.Apply(); 602 state.ApplyVertexBufferState();
587 603
588 std::size_t buffer_size = CalculateVertexArraysSize(); 604 std::size_t buffer_size = CalculateVertexArraysSize();
589 605
@@ -610,7 +626,8 @@ void RasterizerOpenGL::DrawArrays() {
610 626
611 buffer_cache.Map(buffer_size); 627 buffer_cache.Map(buffer_size);
612 628
613 SetupVertexArrays(); 629 SetupVertexFormat();
630 SetupVertexBuffer();
614 DrawParameters params = SetupDraw(); 631 DrawParameters params = SetupDraw();
615 SetupShaders(params.primitive_mode); 632 SetupShaders(params.primitive_mode);
616 633
@@ -690,7 +707,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
690 707
691 // Verify that the cached surface is the same size and format as the requested framebuffer 708 // Verify that the cached surface is the same size and format as the requested framebuffer
692 const auto& params{surface->GetSurfaceParams()}; 709 const auto& params{surface->GetSurfaceParams()};
693 const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)}; 710 const auto& pixel_format{
711 VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)};
694 ASSERT_MSG(params.width == config.width, "Framebuffer width is different"); 712 ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
695 ASSERT_MSG(params.height == config.height, "Framebuffer height is different"); 713 ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
696 ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different"); 714 ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different");
@@ -713,16 +731,20 @@ void RasterizerOpenGL::SamplerInfo::Create() {
713 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); 731 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
714} 732}
715 733
716void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { 734void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) {
717 const GLuint s = sampler.handle; 735 const GLuint s = sampler.handle;
718 736 const Tegra::Texture::TSCEntry& config = info.tsc;
719 if (mag_filter != config.mag_filter) { 737 if (mag_filter != config.mag_filter) {
720 mag_filter = config.mag_filter; 738 mag_filter = config.mag_filter;
721 glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, MaxwellToGL::TextureFilterMode(mag_filter)); 739 glSamplerParameteri(
740 s, GL_TEXTURE_MAG_FILTER,
741 MaxwellToGL::TextureFilterMode(mag_filter, Tegra::Texture::TextureMipmapFilter::None));
722 } 742 }
723 if (min_filter != config.min_filter) { 743 if (min_filter != config.min_filter || mip_filter != config.mip_filter) {
724 min_filter = config.min_filter; 744 min_filter = config.min_filter;
725 glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, MaxwellToGL::TextureFilterMode(min_filter)); 745 mip_filter = config.mip_filter;
746 glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
747 MaxwellToGL::TextureFilterMode(min_filter, mip_filter));
726 } 748 }
727 749
728 if (wrap_u != config.wrap_u) { 750 if (wrap_u != config.wrap_u) {
@@ -762,6 +784,22 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
762 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); 784 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
763 } 785 }
764 } 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 }
765} 803}
766 804
767u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, 805u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
@@ -859,7 +897,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
859 continue; 897 continue;
860 } 898 }
861 899
862 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); 900 texture_samplers[current_bindpoint].SyncWithConfig(texture);
863 Surface surface = res_cache.GetTextureSurface(texture, entry); 901 Surface surface = res_cache.GetTextureSurface(texture, entry);
864 if (surface != nullptr) { 902 if (surface != nullptr) {
865 state.texture_units[current_bindpoint].texture = surface->Texture().handle; 903 state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -881,14 +919,18 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
881 return current_unit + static_cast<u32>(entries.size()); 919 return current_unit + static_cast<u32>(entries.size());
882} 920}
883 921
884void RasterizerOpenGL::SyncViewport() { 922void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
885 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 923 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
886 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; 924 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
887 925 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
888 state.viewport.x = viewport_rect.left; 926 auto& viewport = current_state.viewports[i];
889 state.viewport.y = viewport_rect.bottom; 927 viewport.x = viewport_rect.left;
890 state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); 928 viewport.y = viewport_rect.bottom;
891 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 }
892} 934}
893 935
894void RasterizerOpenGL::SyncClipEnabled() { 936void RasterizerOpenGL::SyncClipEnabled() {
@@ -923,12 +965,11 @@ void RasterizerOpenGL::SyncCullMode() {
923 } 965 }
924} 966}
925 967
926void RasterizerOpenGL::SyncDepthScale() { 968void RasterizerOpenGL::SyncPrimitiveRestart() {
927 UNREACHABLE(); 969 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
928}
929 970
930void RasterizerOpenGL::SyncDepthOffset() { 971 state.primitive_restart.enabled = regs.primitive_restart.enabled;
931 UNREACHABLE(); 972 state.primitive_restart.index = regs.primitive_restart.index;
932} 973}
933 974
934void RasterizerOpenGL::SyncDepthTestState() { 975void RasterizerOpenGL::SyncDepthTestState() {
@@ -951,9 +992,6 @@ void RasterizerOpenGL::SyncStencilTestState() {
951 return; 992 return;
952 } 993 }
953 994
954 // TODO(bunnei): Verify behavior when this is not set
955 ASSERT(regs.stencil_two_side_enable);
956
957 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);
958 state.stencil.front.test_ref = regs.stencil_front_func_ref; 996 state.stencil.front.test_ref = regs.stencil_front_func_ref;
959 state.stencil.front.test_mask = regs.stencil_front_func_mask; 997 state.stencil.front.test_mask = regs.stencil_front_func_mask;
@@ -961,36 +999,79 @@ void RasterizerOpenGL::SyncStencilTestState() {
961 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);
962 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);
963 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}
964 1020
965 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); 1021void RasterizerOpenGL::SyncColorMask() {
966 state.stencil.back.test_ref = regs.stencil_back_func_ref; 1022 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
967 state.stencil.back.test_mask = regs.stencil_back_func_mask; 1023 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
968 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];
969 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); 1025 auto& dest = state.color_mask[i];
970 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); 1026 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
971 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 }
972} 1031}
973 1032
974void RasterizerOpenGL::SyncBlendState() { 1033void RasterizerOpenGL::SyncBlendState() {
975 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1034 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
976 1035
977 // TODO(Subv): Support more than just render target 0. 1036 state.blend_color.red = regs.blend_color.r;
978 state.blend.enabled = regs.blend.enable[0] != 0; 1037 state.blend_color.green = regs.blend_color.g;
979 1038 state.blend_color.blue = regs.blend_color.b;
980 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 }
981 return; 1057 return;
1058 }
982 1059
983 ASSERT_MSG(regs.logic_op.enable == 0, 1060 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
984 "Blending and logic op can't be enabled at the same time."); 1061 auto& blend = state.blend[i];
985 1062 blend.enabled = regs.blend.enable[i] != 0;
986 ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented"); 1063 if (!blend.enabled)
987 ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented"); 1064 continue;
988 state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb); 1065 blend.separate_alpha = regs.independent_blend[i].separate_alpha;
989 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);
990 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);
991 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);
992 state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_a); 1069 if (blend.separate_alpha) {
993 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 }
994} 1075}
995 1076
996void RasterizerOpenGL::SyncLogicOpState() { 1077void RasterizerOpenGL::SyncLogicOpState() {
@@ -1009,19 +1090,19 @@ void RasterizerOpenGL::SyncLogicOpState() {
1009} 1090}
1010 1091
1011void 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?
1012 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1095 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1013
1014 state.scissor.enabled = (regs.scissor_test.enable != 0); 1096 state.scissor.enabled = (regs.scissor_test.enable != 0);
1015 // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's 1097 if (regs.scissor_test.enable == 0) {
1016 // implemented. 1098 return;
1017 if (regs.scissor_test.enable != 0) {
1018 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1019 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1020 state.scissor.x = regs.scissor_test.min_x;
1021 state.scissor.y = regs.scissor_test.min_y;
1022 state.scissor.width = width;
1023 state.scissor.height = height;
1024 } 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;
1025} 1106}
1026 1107
1027void RasterizerOpenGL::SyncTransformFeedback() { 1108void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1046,9 +1127,8 @@ void RasterizerOpenGL::CheckAlphaTests() {
1046 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1127 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1047 1128
1048 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) { 1129 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
1049 LOG_CRITICAL( 1130 LOG_CRITICAL(Render_OpenGL, "Alpha Testing is enabled with Multiple Render Targets, "
1050 Render_OpenGL, 1131 "this behavior is undefined.");
1051 "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
1052 UNREACHABLE(); 1132 UNREACHABLE();
1053 } 1133 }
1054} 1134}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 0e90a31f5..8ef0f6c12 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -8,12 +8,12 @@
8#include <cstddef> 8#include <cstddef>
9#include <map> 9#include <map>
10#include <memory> 10#include <memory>
11#include <optional>
11#include <tuple> 12#include <tuple>
12#include <utility> 13#include <utility>
13#include <vector> 14#include <vector>
14 15
15#include <boost/icl/interval_map.hpp> 16#include <boost/icl/interval_map.hpp>
16#include <boost/optional.hpp>
17#include <boost/range/iterator_range.hpp> 17#include <boost/range/iterator_range.hpp>
18#include <glad/glad.h> 18#include <glad/glad.h>
19 19
@@ -88,11 +88,12 @@ 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;
95 Tegra::Texture::TextureFilter min_filter; 95 Tegra::Texture::TextureFilter min_filter;
96 Tegra::Texture::TextureMipmapFilter mip_filter;
96 Tegra::Texture::WrapMode wrap_u; 97 Tegra::Texture::WrapMode wrap_u;
97 Tegra::Texture::WrapMode wrap_v; 98 Tegra::Texture::WrapMode wrap_v;
98 Tegra::Texture::WrapMode wrap_p; 99 Tegra::Texture::WrapMode wrap_p;
@@ -108,9 +109,9 @@ private:
108 * @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.
109 * @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.
110 */ 111 */
111 void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, 112 void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true,
112 bool preserve_contents = true, 113 bool using_depth_fb = true, bool preserve_contents = true,
113 boost::optional<std::size_t> single_color_target = {}); 114 std::optional<std::size_t> single_color_target = {});
114 115
115 /* 116 /*
116 * Configures the current constbuffers to use for the draw command. 117 * Configures the current constbuffers to use for the draw command.
@@ -132,8 +133,8 @@ private:
132 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 133 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
133 GLenum primitive_mode, u32 current_unit); 134 GLenum primitive_mode, u32 current_unit);
134 135
135 /// Syncs the viewport to match the guest state 136 /// Syncs the viewport and depth range to match the guest state
136 void SyncViewport(); 137 void SyncViewport(OpenGLState& current_state);
137 138
138 /// Syncs the clip enabled status to match the guest state 139 /// Syncs the clip enabled status to match the guest state
139 void SyncClipEnabled(); 140 void SyncClipEnabled();
@@ -144,11 +145,8 @@ private:
144 /// Syncs the cull mode to match the guest state 145 /// Syncs the cull mode to match the guest state
145 void SyncCullMode(); 146 void SyncCullMode();
146 147
147 /// Syncs the depth scale to match the guest state 148 /// Syncs the primitve restart to match the guest state
148 void SyncDepthScale(); 149 void SyncPrimitiveRestart();
149
150 /// Syncs the depth offset to match the guest state
151 void SyncDepthOffset();
152 150
153 /// Syncs the depth test state to match the guest state 151 /// Syncs the depth test state to match the guest state
154 void SyncDepthTestState(); 152 void SyncDepthTestState();
@@ -171,6 +169,9 @@ private:
171 /// Syncs the point state to match the guest state 169 /// Syncs the point state to match the guest state
172 void SyncPointState(); 170 void SyncPointState();
173 171
172 /// Syncs Color Mask
173 void SyncColorMask();
174
174 /// Check asserts for alpha testing. 175 /// Check asserts for alpha testing.
175 void CheckAlphaTests(); 176 void CheckAlphaTests();
176 177
@@ -206,7 +207,8 @@ private:
206 207
207 std::size_t CalculateIndexBufferSize() const; 208 std::size_t CalculateIndexBufferSize() const;
208 209
209 void SetupVertexArrays(); 210 void SetupVertexFormat();
211 void SetupVertexBuffer();
210 212
211 DrawParameters SetupDraw(); 213 DrawParameters SetupDraw();
212 214
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 9c8925383..9ca82c06c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -15,16 +15,24 @@
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"
21#include "video_core/renderer_opengl/utils.h"
22#include "video_core/surface.h"
19#include "video_core/textures/astc.h" 23#include "video_core/textures/astc.h"
20#include "video_core/textures/decoders.h" 24#include "video_core/textures/decoders.h"
21#include "video_core/utils.h" 25#include "video_core/utils.h"
22 26
23namespace OpenGL { 27namespace OpenGL {
24 28
25using SurfaceType = SurfaceParams::SurfaceType; 29using VideoCore::Surface::ComponentTypeFromDepthFormat;
26using PixelFormat = SurfaceParams::PixelFormat; 30using VideoCore::Surface::ComponentTypeFromRenderTarget;
27using ComponentType = SurfaceParams::ComponentType; 31using VideoCore::Surface::ComponentTypeFromTexture;
32using VideoCore::Surface::PixelFormatFromDepthFormat;
33using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
34using VideoCore::Surface::PixelFormatFromTextureFormat;
35using VideoCore::Surface::SurfaceTargetFromTextureType;
28 36
29struct FormatTuple { 37struct FormatTuple {
30 GLint internal_format; 38 GLint internal_format;
@@ -34,34 +42,6 @@ struct FormatTuple {
34 bool compressed; 42 bool compressed;
35}; 43};
36 44
37static bool IsPixelFormatASTC(PixelFormat format) {
38 switch (format) {
39 case PixelFormat::ASTC_2D_4X4:
40 case PixelFormat::ASTC_2D_5X4:
41 case PixelFormat::ASTC_2D_8X8:
42 case PixelFormat::ASTC_2D_8X5:
43 return true;
44 default:
45 return false;
46 }
47}
48
49static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
50 switch (format) {
51 case PixelFormat::ASTC_2D_4X4:
52 return {4, 4};
53 case PixelFormat::ASTC_2D_5X4:
54 return {5, 4};
55 case PixelFormat::ASTC_2D_8X8:
56 return {8, 8};
57 case PixelFormat::ASTC_2D_8X5:
58 return {8, 5};
59 default:
60 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
61 UNREACHABLE();
62 }
63}
64
65void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { 45void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
66 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 46 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
67 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)}; 47 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
@@ -78,6 +58,36 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
78 } 58 }
79} 59}
80 60
61std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only,
62 bool uncompressed) const {
63 const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
64 const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
65 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
66 u32 m_depth = (layer_only ? 1U : depth);
67 u32 m_width = MipWidth(mip_level);
68 u32 m_height = MipHeight(mip_level);
69 m_width = uncompressed ? m_width : std::max(1U, (m_width + tile_x - 1) / tile_x);
70 m_height = uncompressed ? m_height : std::max(1U, (m_height + tile_y - 1) / tile_y);
71 m_depth = std::max(1U, m_depth >> mip_level);
72 u32 m_block_height = MipBlockHeight(mip_level);
73 u32 m_block_depth = MipBlockDepth(mip_level);
74 return Tegra::Texture::CalculateSize(force_gl ? false : is_tiled, bytes_per_pixel, m_width,
75 m_height, m_depth, m_block_height, m_block_depth);
76}
77
78std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
79 bool uncompressed) const {
80 std::size_t block_size_bytes = Tegra::Texture::GetGOBSize() * block_height * block_depth;
81 std::size_t size = 0;
82 for (u32 i = 0; i < max_mip_level; i++) {
83 size += InnerMipmapMemorySize(i, force_gl, layer_only, uncompressed);
84 }
85 if (!force_gl && is_tiled) {
86 size = Common::AlignUp(size, block_size_bytes);
87 }
88 return size;
89}
90
81/*static*/ SurfaceParams SurfaceParams::CreateForTexture( 91/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
82 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { 92 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
83 SurfaceParams params{}; 93 SurfaceParams params{};
@@ -85,8 +95,9 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
85 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, 95 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
86 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 96 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
87 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, 97 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
88 params.pixel_format = 98 params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
89 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); 99 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
100 params.srgb_conversion);
90 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 101 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
91 params.type = GetFormatType(params.pixel_format); 102 params.type = GetFormatType(params.pixel_format);
92 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); 103 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
@@ -117,6 +128,13 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
117 params.target = SurfaceTarget::Texture2D; 128 params.target = SurfaceTarget::Texture2D;
118 } 129 }
119 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;
120 default: 138 default:
121 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));
122 UNREACHABLE(); 140 UNREACHABLE();
@@ -124,6 +142,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
124 break; 142 break;
125 } 143 }
126 144
145 params.is_layered = SurfaceTargetIsLayered(params.target);
127 params.max_mip_level = config.tic.max_mip_level + 1; 146 params.max_mip_level = config.tic.max_mip_level + 1;
128 params.rt = {}; 147 params.rt = {};
129 148
@@ -142,6 +161,8 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
142 params.block_height = 1 << config.memory_layout.block_height; 161 params.block_height = 1 << config.memory_layout.block_height;
143 params.block_depth = 1 << config.memory_layout.block_depth; 162 params.block_depth = 1 << config.memory_layout.block_depth;
144 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 163 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
164 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
165 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
145 params.component_type = ComponentTypeFromRenderTarget(config.format); 166 params.component_type = ComponentTypeFromRenderTarget(config.format);
146 params.type = GetFormatType(params.pixel_format); 167 params.type = GetFormatType(params.pixel_format);
147 params.width = config.width; 168 params.width = config.width;
@@ -149,7 +170,8 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
149 params.unaligned_height = config.height; 170 params.unaligned_height = config.height;
150 params.target = SurfaceTarget::Texture2D; 171 params.target = SurfaceTarget::Texture2D;
151 params.depth = 1; 172 params.depth = 1;
152 params.max_mip_level = 0; 173 params.max_mip_level = 1;
174 params.is_layered = false;
153 175
154 // Render target specific parameters, not used for caching 176 // Render target specific parameters, not used for caching
155 params.rt.index = static_cast<u32>(index); 177 params.rt.index = static_cast<u32>(index);
@@ -176,12 +198,14 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
176 params.pixel_format = PixelFormatFromDepthFormat(format); 198 params.pixel_format = PixelFormatFromDepthFormat(format);
177 params.component_type = ComponentTypeFromDepthFormat(format); 199 params.component_type = ComponentTypeFromDepthFormat(format);
178 params.type = GetFormatType(params.pixel_format); 200 params.type = GetFormatType(params.pixel_format);
201 params.srgb_conversion = false;
179 params.width = zeta_width; 202 params.width = zeta_width;
180 params.height = zeta_height; 203 params.height = zeta_height;
181 params.unaligned_height = zeta_height; 204 params.unaligned_height = zeta_height;
182 params.target = SurfaceTarget::Texture2D; 205 params.target = SurfaceTarget::Texture2D;
183 params.depth = 1; 206 params.depth = 1;
184 params.max_mip_level = 0; 207 params.max_mip_level = 1;
208 params.is_layered = false;
185 params.rt = {}; 209 params.rt = {};
186 210
187 params.InitCacheParameters(zeta_address); 211 params.InitCacheParameters(zeta_address);
@@ -198,6 +222,8 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
198 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, 222 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
199 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, 223 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
200 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 224 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
225 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
226 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
201 params.component_type = ComponentTypeFromRenderTarget(config.format); 227 params.component_type = ComponentTypeFromRenderTarget(config.format);
202 params.type = GetFormatType(params.pixel_format); 228 params.type = GetFormatType(params.pixel_format);
203 params.width = config.width; 229 params.width = config.width;
@@ -205,7 +231,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
205 params.unaligned_height = config.height; 231 params.unaligned_height = config.height;
206 params.target = SurfaceTarget::Texture2D; 232 params.target = SurfaceTarget::Texture2D;
207 params.depth = 1; 233 params.depth = 1;
208 params.max_mip_level = 0; 234 params.max_mip_level = 1;
209 params.rt = {}; 235 params.rt = {};
210 236
211 params.InitCacheParameters(config.Address()); 237 params.InitCacheParameters(config.Address());
@@ -213,7 +239,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
213 return params; 239 return params;
214} 240}
215 241
216static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ 242static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{
217 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U 243 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U
218 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S 244 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S
219 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI 245 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI
@@ -229,7 +255,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
229 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, 255 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
230 false}, // R11FG11FB10F 256 false}, // R11FG11FB10F
231 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI 257 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
232 {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 258 {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
233 true}, // DXT1 259 true}, // DXT1
234 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 260 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
235 true}, // DXT23 261 true}, // DXT23
@@ -263,14 +289,33 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
263 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I 289 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I
264 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S 290 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S
265 {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F 291 {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F
266 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 292 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm,
267 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U 293 false}, // RGBA8_SRGB
268 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S 294 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U
269 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 295 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
270 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 296 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
271 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 297 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
272 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 298 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
273 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4 299 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
300 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
301 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
302 // Compressed sRGB formats
303 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
304 true}, // DXT1_SRGB
305 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
306 true}, // DXT23_SRGB
307 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
308 true}, // DXT45_SRGB
309 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,
310 ComponentType::UNorm, true}, // BC7U_SRGB
311 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
312 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
313 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_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
274 319
275 // Depth formats 320 // Depth formats
276 {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
@@ -286,20 +331,22 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
286 ComponentType::Float, false}, // Z32FS8 331 ComponentType::Float, false}, // Z32FS8
287}}; 332}};
288 333
289static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) { 334static GLenum SurfaceTargetToGL(SurfaceTarget target) {
290 switch (target) { 335 switch (target) {
291 case SurfaceParams::SurfaceTarget::Texture1D: 336 case SurfaceTarget::Texture1D:
292 return GL_TEXTURE_1D; 337 return GL_TEXTURE_1D;
293 case SurfaceParams::SurfaceTarget::Texture2D: 338 case SurfaceTarget::Texture2D:
294 return GL_TEXTURE_2D; 339 return GL_TEXTURE_2D;
295 case SurfaceParams::SurfaceTarget::Texture3D: 340 case SurfaceTarget::Texture3D:
296 return GL_TEXTURE_3D; 341 return GL_TEXTURE_3D;
297 case SurfaceParams::SurfaceTarget::Texture1DArray: 342 case SurfaceTarget::Texture1DArray:
298 return GL_TEXTURE_1D_ARRAY; 343 return GL_TEXTURE_1D_ARRAY;
299 case SurfaceParams::SurfaceTarget::Texture2DArray: 344 case SurfaceTarget::Texture2DArray:
300 return GL_TEXTURE_2D_ARRAY; 345 return GL_TEXTURE_2D_ARRAY;
301 case SurfaceParams::SurfaceTarget::TextureCubemap: 346 case SurfaceTarget::TextureCubemap:
302 return GL_TEXTURE_CUBE_MAP; 347 return GL_TEXTURE_CUBE_MAP;
348 case SurfaceTarget::TextureCubeArray:
349 return GL_TEXTURE_CUBE_MAP_ARRAY_ARB;
303 } 350 }
304 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); 351 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
305 UNREACHABLE(); 352 UNREACHABLE();
@@ -314,57 +361,41 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
314 return format; 361 return format;
315} 362}
316 363
317MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { 364MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const {
318 u32 actual_height{unaligned_height}; 365 u32 actual_height{std::max(1U, unaligned_height >> mip_level)};
319 if (IsPixelFormatASTC(pixel_format)) { 366 if (IsPixelFormatASTC(pixel_format)) {
320 // ASTC formats must stop at the ATSC block size boundary 367 // ASTC formats must stop at the ATSC block size boundary
321 actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second); 368 actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second);
322 } 369 }
323 return {0, actual_height, width, 0}; 370 return {0, actual_height, MipWidth(mip_level), 0};
324}
325
326/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
327static bool IsFormatBCn(PixelFormat format) {
328 switch (format) {
329 case PixelFormat::DXT1:
330 case PixelFormat::DXT23:
331 case PixelFormat::DXT45:
332 case PixelFormat::DXN1:
333 case PixelFormat::DXN2SNORM:
334 case PixelFormat::DXN2UNORM:
335 case PixelFormat::BC7U:
336 case PixelFormat::BC6H_UF16:
337 case PixelFormat::BC6H_SF16:
338 return true;
339 }
340 return false;
341} 371}
342 372
343template <bool morton_to_gl, PixelFormat format> 373template <bool morton_to_gl, PixelFormat format>
344void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer, 374void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
345 std::size_t gl_buffer_size, VAddr addr) { 375 std::size_t gl_buffer_size, VAddr addr) {
346 constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format); 376 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
347 377
348 // 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
349 // pixel values. 379 // pixel values.
350 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 380 const u32 tile_size_x{GetDefaultBlockWidth(format)};
381 const u32 tile_size_y{GetDefaultBlockHeight(format)};
351 382
352 if (morton_to_gl) { 383 if (morton_to_gl) {
353 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 384 Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
354 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth); 385 stride, height, depth, block_height, block_depth);
355 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
356 memcpy(gl_buffer, data.data(), size_to_copy);
357 } else { 386 } else {
358 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth, 387 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
388 (height + tile_size_y - 1) / tile_size_y, depth,
359 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), 389 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
360 gl_buffer, false, block_height, block_depth); 390 gl_buffer, false, block_height, block_depth);
361 } 391 }
362} 392}
363 393
364static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), 394using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
365 SurfaceParams::MaxPixelFormat> 395 VideoCore::Surface::MaxPixelFormat>;
366 morton_to_gl_fns = { 396
367 // clang-format off 397static constexpr GLConversionArray morton_to_gl_fns = {
398 // clang-format off
368 MortonCopy<true, PixelFormat::ABGR8U>, 399 MortonCopy<true, PixelFormat::ABGR8U>,
369 MortonCopy<true, PixelFormat::ABGR8S>, 400 MortonCopy<true, PixelFormat::ABGR8S>,
370 MortonCopy<true, PixelFormat::ABGR8UI>, 401 MortonCopy<true, PixelFormat::ABGR8UI>,
@@ -405,7 +436,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
405 MortonCopy<true, PixelFormat::RG16I>, 436 MortonCopy<true, PixelFormat::RG16I>,
406 MortonCopy<true, PixelFormat::RG16S>, 437 MortonCopy<true, PixelFormat::RG16S>,
407 MortonCopy<true, PixelFormat::RGB32F>, 438 MortonCopy<true, PixelFormat::RGB32F>,
408 MortonCopy<true, PixelFormat::SRGBA8>, 439 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
409 MortonCopy<true, PixelFormat::RG8U>, 440 MortonCopy<true, PixelFormat::RG8U>,
410 MortonCopy<true, PixelFormat::RG8S>, 441 MortonCopy<true, PixelFormat::RG8S>,
411 MortonCopy<true, PixelFormat::RG32UI>, 442 MortonCopy<true, PixelFormat::RG32UI>,
@@ -413,18 +444,29 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
413 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 444 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
414 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 445 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
415 MortonCopy<true, PixelFormat::ASTC_2D_5X4>, 446 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
447 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
448 MortonCopy<true, PixelFormat::DXT1_SRGB>,
449 MortonCopy<true, PixelFormat::DXT23_SRGB>,
450 MortonCopy<true, PixelFormat::DXT45_SRGB>,
451 MortonCopy<true, PixelFormat::BC7U_SRGB>,
452 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
453 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
454 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
455 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
456 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
457 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
458 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
459 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
416 MortonCopy<true, PixelFormat::Z32F>, 460 MortonCopy<true, PixelFormat::Z32F>,
417 MortonCopy<true, PixelFormat::Z16>, 461 MortonCopy<true, PixelFormat::Z16>,
418 MortonCopy<true, PixelFormat::Z24S8>, 462 MortonCopy<true, PixelFormat::Z24S8>,
419 MortonCopy<true, PixelFormat::S8Z24>, 463 MortonCopy<true, PixelFormat::S8Z24>,
420 MortonCopy<true, PixelFormat::Z32FS8>, 464 MortonCopy<true, PixelFormat::Z32FS8>,
421 // clang-format on 465 // clang-format on
422}; 466};
423 467
424static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), 468static constexpr GLConversionArray gl_to_morton_fns = {
425 SurfaceParams::MaxPixelFormat> 469 // clang-format off
426 gl_to_morton_fns = {
427 // clang-format off
428 MortonCopy<false, PixelFormat::ABGR8U>, 470 MortonCopy<false, PixelFormat::ABGR8U>,
429 MortonCopy<false, PixelFormat::ABGR8S>, 471 MortonCopy<false, PixelFormat::ABGR8S>,
430 MortonCopy<false, PixelFormat::ABGR8UI>, 472 MortonCopy<false, PixelFormat::ABGR8UI>,
@@ -466,7 +508,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
466 MortonCopy<false, PixelFormat::RG16I>, 508 MortonCopy<false, PixelFormat::RG16I>,
467 MortonCopy<false, PixelFormat::RG16S>, 509 MortonCopy<false, PixelFormat::RG16S>,
468 MortonCopy<false, PixelFormat::RGB32F>, 510 MortonCopy<false, PixelFormat::RGB32F>,
469 MortonCopy<false, PixelFormat::SRGBA8>, 511 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
470 MortonCopy<false, PixelFormat::RG8U>, 512 MortonCopy<false, PixelFormat::RG8U>,
471 MortonCopy<false, PixelFormat::RG8S>, 513 MortonCopy<false, PixelFormat::RG8S>,
472 MortonCopy<false, PixelFormat::RG32UI>, 514 MortonCopy<false, PixelFormat::RG32UI>,
@@ -474,17 +516,61 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
474 nullptr, 516 nullptr,
475 nullptr, 517 nullptr,
476 nullptr, 518 nullptr,
519 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
520 MortonCopy<false, PixelFormat::DXT1_SRGB>,
521 MortonCopy<false, PixelFormat::DXT23_SRGB>,
522 MortonCopy<false, PixelFormat::DXT45_SRGB>,
523 MortonCopy<false, PixelFormat::BC7U_SRGB>,
524 nullptr,
525 nullptr,
526 nullptr,
527 nullptr,
528 nullptr,
529 nullptr,
530 nullptr,
531 nullptr,
477 MortonCopy<false, PixelFormat::Z32F>, 532 MortonCopy<false, PixelFormat::Z32F>,
478 MortonCopy<false, PixelFormat::Z16>, 533 MortonCopy<false, PixelFormat::Z16>,
479 MortonCopy<false, PixelFormat::Z24S8>, 534 MortonCopy<false, PixelFormat::Z24S8>,
480 MortonCopy<false, PixelFormat::S8Z24>, 535 MortonCopy<false, PixelFormat::S8Z24>,
481 MortonCopy<false, PixelFormat::Z32FS8>, 536 MortonCopy<false, PixelFormat::Z32FS8>,
482 // clang-format on 537 // clang-format on
483}; 538};
484 539
540void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
541 std::vector<u8>& gl_buffer, u32 mip_level) {
542 u32 depth = params.MipDepth(mip_level);
543 if (params.target == SurfaceTarget::Texture2D) {
544 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
545 depth = 1U;
546 }
547 if (params.is_layered) {
548 u64 offset = params.GetMipmapLevelOffset(mip_level);
549 u64 offset_gl = 0;
550 const u64 layer_size = params.LayerMemorySize();
551 const u64 gl_size = params.LayerSizeGL(mip_level);
552 for (u32 i = 0; i < params.depth; i++) {
553 functions[static_cast<std::size_t>(params.pixel_format)](
554 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
555 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), 1,
556 gl_buffer.data() + offset_gl, gl_size, params.addr + offset);
557 offset += layer_size;
558 offset_gl += gl_size;
559 }
560 } else {
561 const u64 offset = params.GetMipmapLevelOffset(mip_level);
562 functions[static_cast<std::size_t>(params.pixel_format)](
563 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
564 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
565 gl_buffer.size(), params.addr + offset);
566 }
567}
568
569MICROPROFILE_DEFINE(OpenGL_BlitSurface, "OpenGL", "BlitSurface", MP_RGB(128, 192, 64));
485static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, 570static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
486 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, 571 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
487 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 572 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
573 MICROPROFILE_SCOPE(OpenGL_BlitSurface);
488 574
489 const auto& src_params{src_surface->GetSurfaceParams()}; 575 const auto& src_params{src_surface->GetSurfaceParams()};
490 const auto& dst_params{dst_surface->GetSurfaceParams()}; 576 const auto& dst_params{dst_surface->GetSurfaceParams()};
@@ -495,19 +581,21 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
495 OpenGLState state; 581 OpenGLState state;
496 state.draw.read_framebuffer = read_fb_handle; 582 state.draw.read_framebuffer = read_fb_handle;
497 state.draw.draw_framebuffer = draw_fb_handle; 583 state.draw.draw_framebuffer = draw_fb_handle;
498 state.Apply(); 584 // Set sRGB enabled if the destination surfaces need it
585 state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
586 state.ApplyFramebufferState();
499 587
500 u32 buffers{}; 588 u32 buffers{};
501 589
502 if (src_params.type == SurfaceType::ColorTexture) { 590 if (src_params.type == SurfaceType::ColorTexture) {
503 switch (src_params.target) { 591 switch (src_params.target) {
504 case SurfaceParams::SurfaceTarget::Texture2D: 592 case SurfaceTarget::Texture2D:
505 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 593 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
506 GL_TEXTURE_2D, src_surface->Texture().handle, 0); 594 GL_TEXTURE_2D, src_surface->Texture().handle, 0);
507 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 595 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
508 0, 0); 596 0, 0);
509 break; 597 break;
510 case SurfaceParams::SurfaceTarget::TextureCubemap: 598 case SurfaceTarget::TextureCubemap:
511 glFramebufferTexture2D( 599 glFramebufferTexture2D(
512 GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 600 GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
513 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 601 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
@@ -516,12 +604,12 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
516 GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 604 GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
517 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); 605 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
518 break; 606 break;
519 case SurfaceParams::SurfaceTarget::Texture2DArray: 607 case SurfaceTarget::Texture2DArray:
520 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 608 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
521 src_surface->Texture().handle, 0, 0); 609 src_surface->Texture().handle, 0, 0);
522 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); 610 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
523 break; 611 break;
524 case SurfaceParams::SurfaceTarget::Texture3D: 612 case SurfaceTarget::Texture3D:
525 glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 613 glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
526 SurfaceTargetToGL(src_params.target), 614 SurfaceTargetToGL(src_params.target),
527 src_surface->Texture().handle, 0, 0); 615 src_surface->Texture().handle, 0, 0);
@@ -537,13 +625,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
537 } 625 }
538 626
539 switch (dst_params.target) { 627 switch (dst_params.target) {
540 case SurfaceParams::SurfaceTarget::Texture2D: 628 case SurfaceTarget::Texture2D:
541 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 629 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
542 GL_TEXTURE_2D, dst_surface->Texture().handle, 0); 630 GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
543 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 631 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
544 0, 0); 632 0, 0);
545 break; 633 break;
546 case SurfaceParams::SurfaceTarget::TextureCubemap: 634 case SurfaceTarget::TextureCubemap:
547 glFramebufferTexture2D( 635 glFramebufferTexture2D(
548 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 636 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
549 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 637 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
@@ -552,13 +640,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
552 GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 640 GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
553 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); 641 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
554 break; 642 break;
555 case SurfaceParams::SurfaceTarget::Texture2DArray: 643 case SurfaceTarget::Texture2DArray:
556 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 644 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
557 dst_surface->Texture().handle, 0, 0); 645 dst_surface->Texture().handle, 0, 0);
558 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); 646 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
559 break; 647 break;
560 648
561 case SurfaceParams::SurfaceTarget::Texture3D: 649 case SurfaceTarget::Texture3D:
562 glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 650 glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
563 SurfaceTargetToGL(dst_params.target), 651 SurfaceTargetToGL(dst_params.target),
564 dst_surface->Texture().handle, 0, 0); 652 dst_surface->Texture().handle, 0, 0);
@@ -622,18 +710,20 @@ static void FastCopySurface(const Surface& src_surface, const Surface& dst_surfa
622 0, 0, width, height, 1); 710 0, 0, width, height, 1);
623} 711}
624 712
713MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
625static void CopySurface(const Surface& src_surface, const Surface& dst_surface, 714static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
626 GLuint copy_pbo_handle, GLenum src_attachment = 0, 715 const GLuint copy_pbo_handle, const GLenum src_attachment = 0,
627 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 716 const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) {
717 MICROPROFILE_SCOPE(OpenGL_CopySurface);
628 ASSERT_MSG(dst_attachment == 0, "Unimplemented"); 718 ASSERT_MSG(dst_attachment == 0, "Unimplemented");
629 719
630 const auto& src_params{src_surface->GetSurfaceParams()}; 720 const auto& src_params{src_surface->GetSurfaceParams()};
631 const auto& dst_params{dst_surface->GetSurfaceParams()}; 721 const auto& dst_params{dst_surface->GetSurfaceParams()};
632 722
633 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 723 const auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
634 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 724 const auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
635 725
636 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); 726 const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
637 727
638 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 728 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
639 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 729 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
@@ -657,13 +747,10 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
657 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 747 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
658 "reinterpretation but the texture is tiled."); 748 "reinterpretation but the texture is tiled.");
659 } 749 }
660 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; 750 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
661 std::vector<u8> data(remaining_size);
662 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
663 data.size());
664 751
665 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, 752 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
666 data.data()); 753 Memory::GetPointer(dst_params.addr + src_params.size_in_bytes));
667 } 754 }
668 755
669 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 756 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -679,21 +766,22 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
679 UNREACHABLE(); 766 UNREACHABLE();
680 } else { 767 } else {
681 switch (dst_params.target) { 768 switch (dst_params.target) {
682 case SurfaceParams::SurfaceTarget::Texture1D: 769 case SurfaceTarget::Texture1D:
683 glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format, 770 glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format,
684 dest_format.type, nullptr); 771 dest_format.type, nullptr);
685 break; 772 break;
686 case SurfaceParams::SurfaceTarget::Texture2D: 773 case SurfaceTarget::Texture2D:
687 glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height, 774 glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height,
688 dest_format.format, dest_format.type, nullptr); 775 dest_format.format, dest_format.type, nullptr);
689 break; 776 break;
690 case SurfaceParams::SurfaceTarget::Texture3D: 777 case SurfaceTarget::Texture3D:
691 case SurfaceParams::SurfaceTarget::Texture2DArray: 778 case SurfaceTarget::Texture2DArray:
779 case SurfaceTarget::TextureCubeArray:
692 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height, 780 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
693 static_cast<GLsizei>(dst_params.depth), dest_format.format, 781 static_cast<GLsizei>(dst_params.depth), dest_format.format,
694 dest_format.type, nullptr); 782 dest_format.type, nullptr);
695 break; 783 break;
696 case SurfaceParams::SurfaceTarget::TextureCubemap: 784 case SurfaceTarget::TextureCubemap:
697 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 785 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0,
698 static_cast<GLint>(cubemap_face), width, height, 1, 786 static_cast<GLint>(cubemap_face), width, height, 1,
699 dest_format.format, dest_format.type, nullptr); 787 dest_format.format, dest_format.type, nullptr);
@@ -730,35 +818,43 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
730 if (!format_tuple.compressed) { 818 if (!format_tuple.compressed) {
731 // Only pre-create the texture for non-compressed textures. 819 // Only pre-create the texture for non-compressed textures.
732 switch (params.target) { 820 switch (params.target) {
733 case SurfaceParams::SurfaceTarget::Texture1D: 821 case SurfaceTarget::Texture1D:
734 glTexStorage1D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, 822 glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level,
735 rect.GetWidth()); 823 format_tuple.internal_format, rect.GetWidth());
736 break; 824 break;
737 case SurfaceParams::SurfaceTarget::Texture2D: 825 case SurfaceTarget::Texture2D:
738 case SurfaceParams::SurfaceTarget::TextureCubemap: 826 case SurfaceTarget::TextureCubemap:
739 glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, 827 glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level,
740 rect.GetWidth(), rect.GetHeight()); 828 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight());
741 break; 829 break;
742 case SurfaceParams::SurfaceTarget::Texture3D: 830 case SurfaceTarget::Texture3D:
743 case SurfaceParams::SurfaceTarget::Texture2DArray: 831 case SurfaceTarget::Texture2DArray:
744 glTexStorage3D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, 832 case SurfaceTarget::TextureCubeArray:
745 rect.GetWidth(), rect.GetHeight(), params.depth); 833 glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
834 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
835 params.depth);
746 break; 836 break;
747 default: 837 default:
748 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 838 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
749 static_cast<u32>(params.target)); 839 static_cast<u32>(params.target));
750 UNREACHABLE(); 840 UNREACHABLE();
751 glTexStorage2D(GL_TEXTURE_2D, 1, format_tuple.internal_format, rect.GetWidth(), 841 glTexStorage2D(GL_TEXTURE_2D, params.max_mip_level, format_tuple.internal_format,
752 rect.GetHeight()); 842 rect.GetWidth(), rect.GetHeight());
753 } 843 }
754 } 844 }
755 845
756 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); 846 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
847 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
757 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 848 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
758 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 849 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
850 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL,
851 params.max_mip_level - 1);
852 if (params.max_mip_level == 1) {
853 glTexParameterf(SurfaceTargetToGL(params.target), GL_TEXTURE_LOD_BIAS, 1000.0);
854 }
759 855
760 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, 856 LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
761 SurfaceParams::SurfaceTargetName(params.target)); 857 SurfaceParams::SurfaceTargetName(params.target));
762 858
763 // Clamp size to mapped GPU memory region 859 // Clamp size to mapped GPU memory region
764 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000 860 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
@@ -788,7 +884,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo
788 884
789 S8Z24 s8z24_pixel{}; 885 S8Z24 s8z24_pixel{};
790 Z24S8 z24s8_pixel{}; 886 Z24S8 z24s8_pixel{};
791 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)}; 887 constexpr auto bpp{GetBytesPerPixel(PixelFormat::S8Z24)};
792 for (std::size_t y = 0; y < height; ++y) { 888 for (std::size_t y = 0; y < height; ++y) {
793 for (std::size_t x = 0; x < width; ++x) { 889 for (std::size_t x = 0; x < width; ++x) {
794 const std::size_t offset{bpp * (y * width + x)}; 890 const std::size_t offset{bpp * (y * width + x)};
@@ -808,7 +904,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo
808} 904}
809 905
810static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 906static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
811 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)}; 907 constexpr auto bpp{GetBytesPerPixel(PixelFormat::G8R8U)};
812 for (std::size_t y = 0; y < height; ++y) { 908 for (std::size_t y = 0; y < height; ++y) {
813 for (std::size_t x = 0; x < width; ++x) { 909 for (std::size_t x = 0; x < width; ++x) {
814 const std::size_t offset{bpp * (y * width + x)}; 910 const std::size_t offset{bpp * (y * width + x)};
@@ -825,17 +921,26 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
825 * typical desktop GPUs. 921 * typical desktop GPUs.
826 */ 922 */
827static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, 923static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
828 u32 width, u32 height) { 924 u32 width, u32 height, u32 depth) {
829 switch (pixel_format) { 925 switch (pixel_format) {
830 case PixelFormat::ASTC_2D_4X4: 926 case PixelFormat::ASTC_2D_4X4:
831 case PixelFormat::ASTC_2D_8X8: 927 case PixelFormat::ASTC_2D_8X8:
832 case PixelFormat::ASTC_2D_8X5: 928 case PixelFormat::ASTC_2D_8X5:
833 case PixelFormat::ASTC_2D_5X4: { 929 case PixelFormat::ASTC_2D_5X4:
930 case PixelFormat::ASTC_2D_5X5:
931 case PixelFormat::ASTC_2D_4X4_SRGB:
932 case PixelFormat::ASTC_2D_8X8_SRGB:
933 case PixelFormat::ASTC_2D_8X5_SRGB:
934 case PixelFormat::ASTC_2D_5X4_SRGB:
935 case PixelFormat::ASTC_2D_5X5_SRGB:
936 case PixelFormat::ASTC_2D_10X8:
937 case PixelFormat::ASTC_2D_10X8_SRGB: {
834 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 938 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
835 u32 block_width{}; 939 u32 block_width{};
836 u32 block_height{}; 940 u32 block_height{};
837 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format); 941 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
838 data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height); 942 data =
943 Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
839 break; 944 break;
840 } 945 }
841 case PixelFormat::S8Z24: 946 case PixelFormat::S8Z24:
@@ -862,7 +967,13 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
862 case PixelFormat::G8R8U: 967 case PixelFormat::G8R8U:
863 case PixelFormat::G8R8S: 968 case PixelFormat::G8R8S:
864 case PixelFormat::ASTC_2D_4X4: 969 case PixelFormat::ASTC_2D_4X4:
865 case PixelFormat::ASTC_2D_8X8: { 970 case PixelFormat::ASTC_2D_8X8:
971 case PixelFormat::ASTC_2D_4X4_SRGB:
972 case PixelFormat::ASTC_2D_8X8_SRGB:
973 case PixelFormat::ASTC_2D_5X5:
974 case PixelFormat::ASTC_2D_5X5_SRGB:
975 case PixelFormat::ASTC_2D_10X8:
976 case PixelFormat::ASTC_2D_10X8_SRGB: {
866 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", 977 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
867 static_cast<u32>(pixel_format)); 978 static_cast<u32>(pixel_format));
868 UNREACHABLE(); 979 UNREACHABLE();
@@ -875,34 +986,25 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
875 } 986 }
876} 987}
877 988
878MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 989MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
879void CachedSurface::LoadGLBuffer() { 990void CachedSurface::LoadGLBuffer() {
880 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 991 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
881 992 gl_buffer.resize(params.max_mip_level);
882 gl_buffer.resize(params.size_in_bytes_gl); 993 for (u32 i = 0; i < params.max_mip_level; i++)
994 gl_buffer[i].resize(params.GetMipmapSizeGL(i));
883 if (params.is_tiled) { 995 if (params.is_tiled) {
884 u32 depth = params.depth;
885 u32 block_depth = params.block_depth;
886
887 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 996 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
888 params.block_width, static_cast<u32>(params.target)); 997 params.block_width, static_cast<u32>(params.target));
889 998 for (u32 i = 0; i < params.max_mip_level; i++)
890 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { 999 SwizzleFunc(morton_to_gl_fns, params, gl_buffer[i], i);
891 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
892 depth = 1U;
893 block_depth = 1U;
894 }
895
896 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
897 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
898 gl_buffer.size(), params.addr);
899 } else { 1000 } else {
900 const auto texture_src_data{Memory::GetPointer(params.addr)}; 1001 const auto texture_src_data{Memory::GetPointer(params.addr)};
901 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; 1002 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
902 gl_buffer.assign(texture_src_data, texture_src_data_end); 1003 gl_buffer[0].assign(texture_src_data, texture_src_data_end);
903 } 1004 }
904 1005 for (u32 i = 0; i < params.max_mip_level; i++)
905 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); 1006 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
1007 params.MipHeight(i), params.MipDepth(i));
906} 1008}
907 1009
908MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 1010MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
@@ -912,57 +1014,44 @@ void CachedSurface::FlushGLBuffer() {
912 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented"); 1014 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
913 1015
914 // OpenGL temporary buffer needs to be big enough to store raw texture size 1016 // OpenGL temporary buffer needs to be big enough to store raw texture size
915 gl_buffer.resize(GetSizeInBytes()); 1017 gl_buffer.resize(1);
1018 gl_buffer[0].resize(GetSizeInBytes());
916 1019
917 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 1020 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
918 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 1021 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
919 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); 1022 ASSERT(params.width * GetBytesPerPixel(params.pixel_format) % 4 == 0);
920 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); 1023 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
921 ASSERT(!tuple.compressed); 1024 ASSERT(!tuple.compressed);
922 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 1025 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
923 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(), 1026 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type,
924 gl_buffer.data()); 1027 static_cast<GLsizei>(gl_buffer[0].size()), gl_buffer[0].data());
925 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 1028 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
926 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, 1029 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width,
927 params.height); 1030 params.height);
928 ASSERT(params.type != SurfaceType::Fill); 1031 ASSERT(params.type != SurfaceType::Fill);
929 const u8* const texture_src_data = Memory::GetPointer(params.addr); 1032 const u8* const texture_src_data = Memory::GetPointer(params.addr);
930 ASSERT(texture_src_data); 1033 ASSERT(texture_src_data);
931 if (params.is_tiled) { 1034 if (params.is_tiled) {
932 u32 depth = params.depth;
933 u32 block_depth = params.block_depth;
934
935 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 1035 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
936 params.block_width, static_cast<u32>(params.target)); 1036 params.block_width, static_cast<u32>(params.target));
937 1037
938 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { 1038 SwizzleFunc(gl_to_morton_fns, params, gl_buffer[0], 0);
939 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
940 depth = 1U;
941 }
942 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
943 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
944 gl_buffer.size(), GetAddr());
945 } else { 1039 } else {
946 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); 1040 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes());
947 } 1041 }
948} 1042}
949 1043
950MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 1044void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
951void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { 1045 GLuint draw_fb_handle) {
952 if (params.type == SurfaceType::Fill) 1046 const auto& rect{params.GetRect(mip_map)};
953 return;
954
955 MICROPROFILE_SCOPE(OpenGL_TextureUL);
956
957 const auto& rect{params.GetRect()};
958 1047
959 // Load data from memory to the surface 1048 // Load data from memory to the surface
960 const GLint x0 = static_cast<GLint>(rect.left); 1049 const GLint x0 = static_cast<GLint>(rect.left);
961 const GLint y0 = static_cast<GLint>(rect.bottom); 1050 const GLint y0 = static_cast<GLint>(rect.bottom);
962 std::size_t buffer_offset = 1051 std::size_t buffer_offset =
963 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + 1052 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) +
964 static_cast<std::size_t>(x0)) * 1053 static_cast<std::size_t>(x0)) *
965 SurfaceParams::GetBytesPerPixel(params.pixel_format); 1054 GetBytesPerPixel(params.pixel_format);
966 1055
967 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 1056 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
968 const GLuint target_tex = texture.handle; 1057 const GLuint target_tex = texture.handle;
@@ -978,89 +1067,120 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
978 cur_state.Apply(); 1067 cur_state.Apply();
979 1068
980 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 1069 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
981 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); 1070 ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0);
982 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 1071 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map)));
983 1072
1073 GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
984 glActiveTexture(GL_TEXTURE0); 1074 glActiveTexture(GL_TEXTURE0);
985 if (tuple.compressed) { 1075 if (tuple.compressed) {
986 switch (params.target) { 1076 switch (params.target) {
987 case SurfaceParams::SurfaceTarget::Texture2D: 1077 case SurfaceTarget::Texture2D:
988 glCompressedTexImage2D( 1078 glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
989 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 1079 static_cast<GLsizei>(params.MipWidth(mip_map)),
990 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, 1080 static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size,
991 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); 1081 &gl_buffer[mip_map][buffer_offset]);
1082 break;
1083 case SurfaceTarget::Texture3D:
1084 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
1085 static_cast<GLsizei>(params.MipWidth(mip_map)),
1086 static_cast<GLsizei>(params.MipHeight(mip_map)),
1087 static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size,
1088 &gl_buffer[mip_map][buffer_offset]);
992 break; 1089 break;
993 case SurfaceParams::SurfaceTarget::Texture3D: 1090 case SurfaceTarget::Texture2DArray:
994 case SurfaceParams::SurfaceTarget::Texture2DArray: 1091 case SurfaceTarget::TextureCubeArray:
995 glCompressedTexImage3D( 1092 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
996 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 1093 static_cast<GLsizei>(params.MipWidth(mip_map)),
997 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 1094 static_cast<GLsizei>(params.MipHeight(mip_map)),
998 static_cast<GLsizei>(params.depth), 0, 1095 static_cast<GLsizei>(params.depth), 0, image_size,
999 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); 1096 &gl_buffer[mip_map][buffer_offset]);
1000 break; 1097 break;
1001 case SurfaceParams::SurfaceTarget::TextureCubemap: 1098 case SurfaceTarget::TextureCubemap: {
1099 GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map));
1002 for (std::size_t face = 0; face < params.depth; ++face) { 1100 for (std::size_t face = 0; face < params.depth; ++face) {
1003 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 1101 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
1004 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1102 mip_map, tuple.internal_format,
1005 static_cast<GLsizei>(params.height), 0, 1103 static_cast<GLsizei>(params.MipWidth(mip_map)),
1006 static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()), 1104 static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
1007 &gl_buffer[buffer_offset]); 1105 layer_size, &gl_buffer[mip_map][buffer_offset]);
1008 buffer_offset += params.SizeInBytesCubeFace(); 1106 buffer_offset += layer_size;
1009 } 1107 }
1010 break; 1108 break;
1109 }
1011 default: 1110 default:
1012 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1111 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
1013 static_cast<u32>(params.target)); 1112 static_cast<u32>(params.target));
1014 UNREACHABLE(); 1113 UNREACHABLE();
1015 glCompressedTexImage2D( 1114 glCompressedTexImage2D(GL_TEXTURE_2D, mip_map, tuple.internal_format,
1016 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1115 static_cast<GLsizei>(params.MipWidth(mip_map)),
1017 static_cast<GLsizei>(params.height), 0, 1116 static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
1018 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); 1117 static_cast<GLsizei>(params.size_in_bytes_gl),
1118 &gl_buffer[mip_map][buffer_offset]);
1019 } 1119 }
1020 } else { 1120 } else {
1021 1121
1022 switch (params.target) { 1122 switch (params.target) {
1023 case SurfaceParams::SurfaceTarget::Texture1D: 1123 case SurfaceTarget::Texture1D:
1024 glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0, 1124 glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0,
1025 static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, 1125 static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
1026 &gl_buffer[buffer_offset]); 1126 &gl_buffer[mip_map][buffer_offset]);
1027 break; 1127 break;
1028 case SurfaceParams::SurfaceTarget::Texture2D: 1128 case SurfaceTarget::Texture2D:
1029 glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0, 1129 glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0,
1030 static_cast<GLsizei>(rect.GetWidth()), 1130 static_cast<GLsizei>(rect.GetWidth()),
1031 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1131 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
1032 &gl_buffer[buffer_offset]); 1132 &gl_buffer[mip_map][buffer_offset]);
1033 break; 1133 break;
1034 case SurfaceParams::SurfaceTarget::Texture3D: 1134 case SurfaceTarget::Texture3D:
1035 case SurfaceParams::SurfaceTarget::Texture2DArray: 1135 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
1036 glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0, 1136 static_cast<GLsizei>(rect.GetWidth()),
1137 static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map),
1138 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
1139 break;
1140 case SurfaceTarget::Texture2DArray:
1141 case SurfaceTarget::TextureCubeArray:
1142 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
1037 static_cast<GLsizei>(rect.GetWidth()), 1143 static_cast<GLsizei>(rect.GetWidth()),
1038 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, 1144 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
1039 tuple.type, &gl_buffer[buffer_offset]); 1145 tuple.type, &gl_buffer[mip_map][buffer_offset]);
1040 break; 1146 break;
1041 case SurfaceParams::SurfaceTarget::TextureCubemap: 1147 case SurfaceTarget::TextureCubemap: {
1148 std::size_t start = buffer_offset;
1042 for (std::size_t face = 0; face < params.depth; ++face) { 1149 for (std::size_t face = 0; face < params.depth; ++face) {
1043 glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, x0, 1150 glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map,
1044 y0, static_cast<GLsizei>(rect.GetWidth()), 1151 x0, y0, static_cast<GLsizei>(rect.GetWidth()),
1045 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1152 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
1046 &gl_buffer[buffer_offset]); 1153 &gl_buffer[mip_map][buffer_offset]);
1047 buffer_offset += params.SizeInBytesCubeFace(); 1154 buffer_offset += params.LayerSizeGL(mip_map);
1048 } 1155 }
1049 break; 1156 break;
1157 }
1050 default: 1158 default:
1051 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1159 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
1052 static_cast<u32>(params.target)); 1160 static_cast<u32>(params.target));
1053 UNREACHABLE(); 1161 UNREACHABLE();
1054 glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), 1162 glTexSubImage2D(GL_TEXTURE_2D, mip_map, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
1055 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1163 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
1056 &gl_buffer[buffer_offset]); 1164 &gl_buffer[mip_map][buffer_offset]);
1057 } 1165 }
1058 } 1166 }
1059 1167
1060 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 1168 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1061} 1169}
1062 1170
1063RasterizerCacheOpenGL::RasterizerCacheOpenGL() { 1171MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
1172void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
1173 if (params.type == SurfaceType::Fill)
1174 return;
1175
1176 MICROPROFILE_SCOPE(OpenGL_TextureUL);
1177
1178 for (u32 i = 0; i < params.max_mip_level; i++)
1179 UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle);
1180}
1181
1182RasterizerCacheOpenGL::RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer)
1183 : RasterizerCache{rasterizer} {
1064 read_framebuffer.Create(); 1184 read_framebuffer.Create();
1065 draw_framebuffer.Create(); 1185 draw_framebuffer.Create();
1066 copy_pbo.Create(); 1186 copy_pbo.Create();
@@ -1179,7 +1299,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1179 const Surface& dst_surface) { 1299 const Surface& dst_surface) {
1180 const auto& src_params{src_surface->GetSurfaceParams()}; 1300 const auto& src_params{src_surface->GetSurfaceParams()};
1181 const auto& dst_params{dst_surface->GetSurfaceParams()}; 1301 const auto& dst_params{dst_surface->GetSurfaceParams()};
1182 FlushRegion(src_params.addr, dst_params.size_in_bytes); 1302 FlushRegion(src_params.addr, dst_params.MemorySize());
1183 LoadSurface(dst_surface); 1303 LoadSurface(dst_surface);
1184} 1304}
1185 1305
@@ -1200,8 +1320,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1200 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1320 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1201 if (old_params.target == new_params.target && old_params.type == new_params.type && 1321 if (old_params.target == new_params.target && old_params.type == new_params.type &&
1202 old_params.depth == new_params.depth && old_params.depth == 1 && 1322 old_params.depth == new_params.depth && old_params.depth == 1 &&
1203 SurfaceParams::GetFormatBpp(old_params.pixel_format) == 1323 GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) {
1204 SurfaceParams::GetFormatBpp(new_params.pixel_format)) {
1205 FastCopySurface(old_surface, new_surface); 1324 FastCopySurface(old_surface, new_surface);
1206 return new_surface; 1325 return new_surface;
1207 } 1326 }
@@ -1214,51 +1333,19 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1214 const bool is_blit{old_params.pixel_format == new_params.pixel_format}; 1333 const bool is_blit{old_params.pixel_format == new_params.pixel_format};
1215 1334
1216 switch (new_params.target) { 1335 switch (new_params.target) {
1217 case SurfaceParams::SurfaceTarget::Texture2D: 1336 case SurfaceTarget::Texture2D:
1218 if (is_blit) { 1337 if (is_blit) {
1219 BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle); 1338 BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle);
1220 } else { 1339 } else {
1221 CopySurface(old_surface, new_surface, copy_pbo.handle); 1340 CopySurface(old_surface, new_surface, copy_pbo.handle);
1222 } 1341 }
1223 break; 1342 break;
1224 case SurfaceParams::SurfaceTarget::Texture3D: 1343 case SurfaceTarget::TextureCubemap:
1344 case SurfaceTarget::Texture3D:
1345 case SurfaceTarget::Texture2DArray:
1346 case SurfaceTarget::TextureCubeArray:
1225 AccurateCopySurface(old_surface, new_surface); 1347 AccurateCopySurface(old_surface, new_surface);
1226 break; 1348 break;
1227 case SurfaceParams::SurfaceTarget::TextureCubemap: {
1228 if (old_params.rt.array_mode != 1) {
1229 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
1230 // yet (array rendering used as a cubemap texture).
1231 LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode);
1232 UNREACHABLE();
1233 return new_surface;
1234 }
1235
1236 // This seems to be used for render-to-cubemap texture
1237 ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected");
1238 ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected");
1239 ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented");
1240
1241 // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels.
1242 // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild.
1243 const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)};
1244
1245 for (std::size_t index = 0; index < new_params.depth; ++index) {
1246 Surface face_surface{TryGetReservedSurface(old_params)};
1247 ASSERT_MSG(face_surface, "Unexpected");
1248
1249 if (is_blit) {
1250 BlitSurface(face_surface, new_surface, read_framebuffer.handle,
1251 draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index,
1252 new_params.rt.index, index);
1253 } else {
1254 CopySurface(face_surface, new_surface, copy_pbo.handle,
1255 face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index);
1256 }
1257
1258 old_params.addr += byte_stride;
1259 }
1260 break;
1261 }
1262 default: 1349 default:
1263 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1350 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
1264 static_cast<u32>(new_params.target)); 1351 static_cast<u32>(new_params.target));
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0dd0d90a3..494f6b903 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <map> 8#include <map>
9#include <memory> 9#include <memory>
10#include <string>
10#include <vector> 11#include <vector>
11 12
12#include "common/alignment.h" 13#include "common/alignment.h"
@@ -18,6 +19,7 @@
18#include "video_core/rasterizer_cache.h" 19#include "video_core/rasterizer_cache.h"
19#include "video_core/renderer_opengl/gl_resource_manager.h" 20#include "video_core/renderer_opengl/gl_resource_manager.h"
20#include "video_core/renderer_opengl/gl_shader_gen.h" 21#include "video_core/renderer_opengl/gl_shader_gen.h"
22#include "video_core/surface.h"
21#include "video_core/textures/decoders.h" 23#include "video_core/textures/decoders.h"
22#include "video_core/textures/texture.h" 24#include "video_core/textures/texture.h"
23 25
@@ -27,126 +29,12 @@ class CachedSurface;
27using Surface = std::shared_ptr<CachedSurface>; 29using Surface = std::shared_ptr<CachedSurface>;
28using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; 30using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
29 31
30struct SurfaceParams { 32using SurfaceTarget = VideoCore::Surface::SurfaceTarget;
31 enum class PixelFormat { 33using SurfaceType = VideoCore::Surface::SurfaceType;
32 ABGR8U = 0, 34using PixelFormat = VideoCore::Surface::PixelFormat;
33 ABGR8S = 1, 35using ComponentType = VideoCore::Surface::ComponentType;
34 ABGR8UI = 2,
35 B5G6R5U = 3,
36 A2B10G10R10U = 4,
37 A1B5G5R5U = 5,
38 R8U = 6,
39 R8UI = 7,
40 RGBA16F = 8,
41 RGBA16U = 9,
42 RGBA16UI = 10,
43 R11FG11FB10F = 11,
44 RGBA32UI = 12,
45 DXT1 = 13,
46 DXT23 = 14,
47 DXT45 = 15,
48 DXN1 = 16, // This is also known as BC4
49 DXN2UNORM = 17,
50 DXN2SNORM = 18,
51 BC7U = 19,
52 BC6H_UF16 = 20,
53 BC6H_SF16 = 21,
54 ASTC_2D_4X4 = 22,
55 G8R8U = 23,
56 G8R8S = 24,
57 BGRA8 = 25,
58 RGBA32F = 26,
59 RG32F = 27,
60 R32F = 28,
61 R16F = 29,
62 R16U = 30,
63 R16S = 31,
64 R16UI = 32,
65 R16I = 33,
66 RG16 = 34,
67 RG16F = 35,
68 RG16UI = 36,
69 RG16I = 37,
70 RG16S = 38,
71 RGB32F = 39,
72 SRGBA8 = 40,
73 RG8U = 41,
74 RG8S = 42,
75 RG32UI = 43,
76 R32UI = 44,
77 ASTC_2D_8X8 = 45,
78 ASTC_2D_8X5 = 46,
79 ASTC_2D_5X4 = 47,
80
81 MaxColorFormat,
82
83 // Depth formats
84 Z32F = 48,
85 Z16 = 49,
86
87 MaxDepthFormat,
88
89 // DepthStencil formats
90 Z24S8 = 50,
91 S8Z24 = 51,
92 Z32FS8 = 52,
93
94 MaxDepthStencilFormat,
95
96 Max = MaxDepthStencilFormat,
97 Invalid = 255,
98 };
99
100 static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);
101
102 enum class ComponentType {
103 Invalid = 0,
104 SNorm = 1,
105 UNorm = 2,
106 SInt = 3,
107 UInt = 4,
108 Float = 5,
109 };
110
111 enum class SurfaceType {
112 ColorTexture = 0,
113 Depth = 1,
114 DepthStencil = 2,
115 Fill = 3,
116 Invalid = 4,
117 };
118
119 enum class SurfaceTarget {
120 Texture1D,
121 Texture2D,
122 Texture3D,
123 Texture1DArray,
124 Texture2DArray,
125 TextureCubemap,
126 };
127
128 static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
129 switch (texture_type) {
130 case Tegra::Texture::TextureType::Texture1D:
131 return SurfaceTarget::Texture1D;
132 case Tegra::Texture::TextureType::Texture2D:
133 case Tegra::Texture::TextureType::Texture2DNoMipmap:
134 return SurfaceTarget::Texture2D;
135 case Tegra::Texture::TextureType::Texture3D:
136 return SurfaceTarget::Texture3D;
137 case Tegra::Texture::TextureType::TextureCubemap:
138 return SurfaceTarget::TextureCubemap;
139 case Tegra::Texture::TextureType::Texture1DArray:
140 return SurfaceTarget::Texture1DArray;
141 case Tegra::Texture::TextureType::Texture2DArray:
142 return SurfaceTarget::Texture2DArray;
143 default:
144 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
145 UNREACHABLE();
146 return SurfaceTarget::Texture2D;
147 }
148 }
149 36
37struct SurfaceParams {
150 static std::string SurfaceTargetName(SurfaceTarget target) { 38 static std::string SurfaceTargetName(SurfaceTarget target) {
151 switch (target) { 39 switch (target) {
152 case SurfaceTarget::Texture1D: 40 case SurfaceTarget::Texture1D:
@@ -161,6 +49,8 @@ struct SurfaceParams {
161 return "Texture2DArray"; 49 return "Texture2DArray";
162 case SurfaceTarget::TextureCubemap: 50 case SurfaceTarget::TextureCubemap:
163 return "TextureCubemap"; 51 return "TextureCubemap";
52 case SurfaceTarget::TextureCubeArray:
53 return "TextureCubeArray";
164 default: 54 default:
165 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); 55 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
166 UNREACHABLE(); 56 UNREACHABLE();
@@ -168,552 +58,12 @@ struct SurfaceParams {
168 } 58 }
169 } 59 }
170 60
171 /**
172 * Gets the compression factor for the specified PixelFormat. This applies to just the
173 * "compressed width" and "compressed height", not the overall compression factor of a
174 * compressed image. This is used for maintaining proper surface sizes for compressed
175 * texture formats.
176 */
177 static constexpr u32 GetCompressionFactor(PixelFormat format) {
178 if (format == PixelFormat::Invalid)
179 return 0;
180
181 constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
182 1, // ABGR8U
183 1, // ABGR8S
184 1, // ABGR8UI
185 1, // B5G6R5U
186 1, // A2B10G10R10U
187 1, // A1B5G5R5U
188 1, // R8U
189 1, // R8UI
190 1, // RGBA16F
191 1, // RGBA16U
192 1, // RGBA16UI
193 1, // R11FG11FB10F
194 1, // RGBA32UI
195 4, // DXT1
196 4, // DXT23
197 4, // DXT45
198 4, // DXN1
199 4, // DXN2UNORM
200 4, // DXN2SNORM
201 4, // BC7U
202 4, // BC6H_UF16
203 4, // BC6H_SF16
204 4, // ASTC_2D_4X4
205 1, // G8R8U
206 1, // G8R8S
207 1, // BGRA8
208 1, // RGBA32F
209 1, // RG32F
210 1, // R32F
211 1, // R16F
212 1, // R16U
213 1, // R16S
214 1, // R16UI
215 1, // R16I
216 1, // RG16
217 1, // RG16F
218 1, // RG16UI
219 1, // RG16I
220 1, // RG16S
221 1, // RGB32F
222 1, // SRGBA8
223 1, // RG8U
224 1, // RG8S
225 1, // RG32UI
226 1, // R32UI
227 4, // ASTC_2D_8X8
228 4, // ASTC_2D_8X5
229 4, // ASTC_2D_5X4
230 1, // Z32F
231 1, // Z16
232 1, // Z24S8
233 1, // S8Z24
234 1, // Z32FS8
235 }};
236
237 ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
238 return compression_factor_table[static_cast<std::size_t>(format)];
239 }
240
241 static constexpr u32 GetFormatBpp(PixelFormat format) {
242 if (format == PixelFormat::Invalid)
243 return 0;
244
245 constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
246 32, // ABGR8U
247 32, // ABGR8S
248 32, // ABGR8UI
249 16, // B5G6R5U
250 32, // A2B10G10R10U
251 16, // A1B5G5R5U
252 8, // R8U
253 8, // R8UI
254 64, // RGBA16F
255 64, // RGBA16U
256 64, // RGBA16UI
257 32, // R11FG11FB10F
258 128, // RGBA32UI
259 64, // DXT1
260 128, // DXT23
261 128, // DXT45
262 64, // DXN1
263 128, // DXN2UNORM
264 128, // DXN2SNORM
265 128, // BC7U
266 128, // BC6H_UF16
267 128, // BC6H_SF16
268 32, // ASTC_2D_4X4
269 16, // G8R8U
270 16, // G8R8S
271 32, // BGRA8
272 128, // RGBA32F
273 64, // RG32F
274 32, // R32F
275 16, // R16F
276 16, // R16U
277 16, // R16S
278 16, // R16UI
279 16, // R16I
280 32, // RG16
281 32, // RG16F
282 32, // RG16UI
283 32, // RG16I
284 32, // RG16S
285 96, // RGB32F
286 32, // SRGBA8
287 16, // RG8U
288 16, // RG8S
289 64, // RG32UI
290 32, // R32UI
291 16, // ASTC_2D_8X8
292 32, // ASTC_2D_8X5
293 32, // ASTC_2D_5X4
294 32, // Z32F
295 16, // Z16
296 32, // Z24S8
297 32, // S8Z24
298 64, // Z32FS8
299 }};
300
301 ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
302 return bpp_table[static_cast<std::size_t>(format)];
303 }
304
305 u32 GetFormatBpp() const { 61 u32 GetFormatBpp() const {
306 return GetFormatBpp(pixel_format); 62 return VideoCore::Surface::GetFormatBpp(pixel_format);
307 }
308
309 static PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
310 switch (format) {
311 case Tegra::DepthFormat::S8_Z24_UNORM:
312 return PixelFormat::S8Z24;
313 case Tegra::DepthFormat::Z24_S8_UNORM:
314 return PixelFormat::Z24S8;
315 case Tegra::DepthFormat::Z32_FLOAT:
316 return PixelFormat::Z32F;
317 case Tegra::DepthFormat::Z16_UNORM:
318 return PixelFormat::Z16;
319 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
320 return PixelFormat::Z32FS8;
321 default:
322 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
323 UNREACHABLE();
324 }
325 }
326
327 static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
328 switch (format) {
329 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
330 // gamma.
331 case Tegra::RenderTargetFormat::RGBA8_SRGB:
332 case Tegra::RenderTargetFormat::RGBA8_UNORM:
333 return PixelFormat::ABGR8U;
334 case Tegra::RenderTargetFormat::RGBA8_SNORM:
335 return PixelFormat::ABGR8S;
336 case Tegra::RenderTargetFormat::RGBA8_UINT:
337 return PixelFormat::ABGR8UI;
338 case Tegra::RenderTargetFormat::BGRA8_SRGB:
339 case Tegra::RenderTargetFormat::BGRA8_UNORM:
340 return PixelFormat::BGRA8;
341 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
342 return PixelFormat::A2B10G10R10U;
343 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
344 return PixelFormat::RGBA16F;
345 case Tegra::RenderTargetFormat::RGBA16_UNORM:
346 return PixelFormat::RGBA16U;
347 case Tegra::RenderTargetFormat::RGBA16_UINT:
348 return PixelFormat::RGBA16UI;
349 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
350 return PixelFormat::RGBA32F;
351 case Tegra::RenderTargetFormat::RG32_FLOAT:
352 return PixelFormat::RG32F;
353 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
354 return PixelFormat::R11FG11FB10F;
355 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
356 return PixelFormat::B5G6R5U;
357 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
358 return PixelFormat::A1B5G5R5U;
359 case Tegra::RenderTargetFormat::RGBA32_UINT:
360 return PixelFormat::RGBA32UI;
361 case Tegra::RenderTargetFormat::R8_UNORM:
362 return PixelFormat::R8U;
363 case Tegra::RenderTargetFormat::R8_UINT:
364 return PixelFormat::R8UI;
365 case Tegra::RenderTargetFormat::RG16_FLOAT:
366 return PixelFormat::RG16F;
367 case Tegra::RenderTargetFormat::RG16_UINT:
368 return PixelFormat::RG16UI;
369 case Tegra::RenderTargetFormat::RG16_SINT:
370 return PixelFormat::RG16I;
371 case Tegra::RenderTargetFormat::RG16_UNORM:
372 return PixelFormat::RG16;
373 case Tegra::RenderTargetFormat::RG16_SNORM:
374 return PixelFormat::RG16S;
375 case Tegra::RenderTargetFormat::RG8_UNORM:
376 return PixelFormat::RG8U;
377 case Tegra::RenderTargetFormat::RG8_SNORM:
378 return PixelFormat::RG8S;
379 case Tegra::RenderTargetFormat::R16_FLOAT:
380 return PixelFormat::R16F;
381 case Tegra::RenderTargetFormat::R16_UNORM:
382 return PixelFormat::R16U;
383 case Tegra::RenderTargetFormat::R16_SNORM:
384 return PixelFormat::R16S;
385 case Tegra::RenderTargetFormat::R16_UINT:
386 return PixelFormat::R16UI;
387 case Tegra::RenderTargetFormat::R16_SINT:
388 return PixelFormat::R16I;
389 case Tegra::RenderTargetFormat::R32_FLOAT:
390 return PixelFormat::R32F;
391 case Tegra::RenderTargetFormat::R32_UINT:
392 return PixelFormat::R32UI;
393 case Tegra::RenderTargetFormat::RG32_UINT:
394 return PixelFormat::RG32UI;
395 default:
396 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
397 UNREACHABLE();
398 }
399 }
400
401 static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
402 Tegra::Texture::ComponentType component_type) {
403 // TODO(Subv): Properly implement this
404 switch (format) {
405 case Tegra::Texture::TextureFormat::A8R8G8B8:
406 switch (component_type) {
407 case Tegra::Texture::ComponentType::UNORM:
408 return PixelFormat::ABGR8U;
409 case Tegra::Texture::ComponentType::SNORM:
410 return PixelFormat::ABGR8S;
411 case Tegra::Texture::ComponentType::UINT:
412 return PixelFormat::ABGR8UI;
413 }
414 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
415 static_cast<u32>(component_type));
416 UNREACHABLE();
417 case Tegra::Texture::TextureFormat::B5G6R5:
418 switch (component_type) {
419 case Tegra::Texture::ComponentType::UNORM:
420 return PixelFormat::B5G6R5U;
421 }
422 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
423 static_cast<u32>(component_type));
424 UNREACHABLE();
425 case Tegra::Texture::TextureFormat::A2B10G10R10:
426 switch (component_type) {
427 case Tegra::Texture::ComponentType::UNORM:
428 return PixelFormat::A2B10G10R10U;
429 }
430 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
431 static_cast<u32>(component_type));
432 UNREACHABLE();
433 case Tegra::Texture::TextureFormat::A1B5G5R5:
434 switch (component_type) {
435 case Tegra::Texture::ComponentType::UNORM:
436 return PixelFormat::A1B5G5R5U;
437 }
438 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
439 static_cast<u32>(component_type));
440 UNREACHABLE();
441 case Tegra::Texture::TextureFormat::R8:
442 switch (component_type) {
443 case Tegra::Texture::ComponentType::UNORM:
444 return PixelFormat::R8U;
445 case Tegra::Texture::ComponentType::UINT:
446 return PixelFormat::R8UI;
447 }
448 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
449 static_cast<u32>(component_type));
450 UNREACHABLE();
451 case Tegra::Texture::TextureFormat::G8R8:
452 switch (component_type) {
453 case Tegra::Texture::ComponentType::UNORM:
454 return PixelFormat::G8R8U;
455 case Tegra::Texture::ComponentType::SNORM:
456 return PixelFormat::G8R8S;
457 }
458 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
459 static_cast<u32>(component_type));
460 UNREACHABLE();
461 case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
462 switch (component_type) {
463 case Tegra::Texture::ComponentType::UNORM:
464 return PixelFormat::RGBA16U;
465 case Tegra::Texture::ComponentType::FLOAT:
466 return PixelFormat::RGBA16F;
467 }
468 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
469 static_cast<u32>(component_type));
470 UNREACHABLE();
471 case Tegra::Texture::TextureFormat::BF10GF11RF11:
472 switch (component_type) {
473 case Tegra::Texture::ComponentType::FLOAT:
474 return PixelFormat::R11FG11FB10F;
475 }
476 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
477 static_cast<u32>(component_type));
478 UNREACHABLE();
479 case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
480 switch (component_type) {
481 case Tegra::Texture::ComponentType::FLOAT:
482 return PixelFormat::RGBA32F;
483 case Tegra::Texture::ComponentType::UINT:
484 return PixelFormat::RGBA32UI;
485 }
486 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
487 static_cast<u32>(component_type));
488 UNREACHABLE();
489 case Tegra::Texture::TextureFormat::R32_G32:
490 switch (component_type) {
491 case Tegra::Texture::ComponentType::FLOAT:
492 return PixelFormat::RG32F;
493 case Tegra::Texture::ComponentType::UINT:
494 return PixelFormat::RG32UI;
495 }
496 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
497 static_cast<u32>(component_type));
498 UNREACHABLE();
499 case Tegra::Texture::TextureFormat::R32_G32_B32:
500 switch (component_type) {
501 case Tegra::Texture::ComponentType::FLOAT:
502 return PixelFormat::RGB32F;
503 }
504 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
505 static_cast<u32>(component_type));
506 UNREACHABLE();
507 case Tegra::Texture::TextureFormat::R16:
508 switch (component_type) {
509 case Tegra::Texture::ComponentType::FLOAT:
510 return PixelFormat::R16F;
511 case Tegra::Texture::ComponentType::UNORM:
512 return PixelFormat::R16U;
513 case Tegra::Texture::ComponentType::SNORM:
514 return PixelFormat::R16S;
515 case Tegra::Texture::ComponentType::UINT:
516 return PixelFormat::R16UI;
517 case Tegra::Texture::ComponentType::SINT:
518 return PixelFormat::R16I;
519 }
520 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
521 static_cast<u32>(component_type));
522 UNREACHABLE();
523 case Tegra::Texture::TextureFormat::R32:
524 switch (component_type) {
525 case Tegra::Texture::ComponentType::FLOAT:
526 return PixelFormat::R32F;
527 case Tegra::Texture::ComponentType::UINT:
528 return PixelFormat::R32UI;
529 }
530 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
531 static_cast<u32>(component_type));
532 UNREACHABLE();
533 case Tegra::Texture::TextureFormat::ZF32:
534 return PixelFormat::Z32F;
535 case Tegra::Texture::TextureFormat::Z16:
536 return PixelFormat::Z16;
537 case Tegra::Texture::TextureFormat::Z24S8:
538 return PixelFormat::Z24S8;
539 case Tegra::Texture::TextureFormat::DXT1:
540 return PixelFormat::DXT1;
541 case Tegra::Texture::TextureFormat::DXT23:
542 return PixelFormat::DXT23;
543 case Tegra::Texture::TextureFormat::DXT45:
544 return PixelFormat::DXT45;
545 case Tegra::Texture::TextureFormat::DXN1:
546 return PixelFormat::DXN1;
547 case Tegra::Texture::TextureFormat::DXN2:
548 switch (component_type) {
549 case Tegra::Texture::ComponentType::UNORM:
550 return PixelFormat::DXN2UNORM;
551 case Tegra::Texture::ComponentType::SNORM:
552 return PixelFormat::DXN2SNORM;
553 }
554 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
555 static_cast<u32>(component_type));
556 UNREACHABLE();
557 case Tegra::Texture::TextureFormat::BC7U:
558 return PixelFormat::BC7U;
559 case Tegra::Texture::TextureFormat::BC6H_UF16:
560 return PixelFormat::BC6H_UF16;
561 case Tegra::Texture::TextureFormat::BC6H_SF16:
562 return PixelFormat::BC6H_SF16;
563 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
564 return PixelFormat::ASTC_2D_4X4;
565 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
566 return PixelFormat::ASTC_2D_5X4;
567 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
568 return PixelFormat::ASTC_2D_8X8;
569 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
570 return PixelFormat::ASTC_2D_8X5;
571 case Tegra::Texture::TextureFormat::R16_G16:
572 switch (component_type) {
573 case Tegra::Texture::ComponentType::FLOAT:
574 return PixelFormat::RG16F;
575 case Tegra::Texture::ComponentType::UNORM:
576 return PixelFormat::RG16;
577 case Tegra::Texture::ComponentType::SNORM:
578 return PixelFormat::RG16S;
579 case Tegra::Texture::ComponentType::UINT:
580 return PixelFormat::RG16UI;
581 case Tegra::Texture::ComponentType::SINT:
582 return PixelFormat::RG16I;
583 }
584 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
585 static_cast<u32>(component_type));
586 UNREACHABLE();
587 default:
588 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}",
589 static_cast<u32>(format), static_cast<u32>(component_type));
590 UNREACHABLE();
591 }
592 }
593
594 static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
595 // TODO(Subv): Implement more component types
596 switch (type) {
597 case Tegra::Texture::ComponentType::UNORM:
598 return ComponentType::UNorm;
599 case Tegra::Texture::ComponentType::FLOAT:
600 return ComponentType::Float;
601 case Tegra::Texture::ComponentType::SNORM:
602 return ComponentType::SNorm;
603 case Tegra::Texture::ComponentType::UINT:
604 return ComponentType::UInt;
605 case Tegra::Texture::ComponentType::SINT:
606 return ComponentType::SInt;
607 default:
608 LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
609 UNREACHABLE();
610 }
611 }
612
613 static ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
614 // TODO(Subv): Implement more render targets
615 switch (format) {
616 case Tegra::RenderTargetFormat::RGBA8_UNORM:
617 case Tegra::RenderTargetFormat::RGBA8_SRGB:
618 case Tegra::RenderTargetFormat::BGRA8_UNORM:
619 case Tegra::RenderTargetFormat::BGRA8_SRGB:
620 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
621 case Tegra::RenderTargetFormat::R8_UNORM:
622 case Tegra::RenderTargetFormat::RG16_UNORM:
623 case Tegra::RenderTargetFormat::R16_UNORM:
624 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
625 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
626 case Tegra::RenderTargetFormat::RG8_UNORM:
627 case Tegra::RenderTargetFormat::RGBA16_UNORM:
628 return ComponentType::UNorm;
629 case Tegra::RenderTargetFormat::RGBA8_SNORM:
630 case Tegra::RenderTargetFormat::RG16_SNORM:
631 case Tegra::RenderTargetFormat::R16_SNORM:
632 case Tegra::RenderTargetFormat::RG8_SNORM:
633 return ComponentType::SNorm;
634 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
635 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
636 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
637 case Tegra::RenderTargetFormat::RG32_FLOAT:
638 case Tegra::RenderTargetFormat::RG16_FLOAT:
639 case Tegra::RenderTargetFormat::R16_FLOAT:
640 case Tegra::RenderTargetFormat::R32_FLOAT:
641 return ComponentType::Float;
642 case Tegra::RenderTargetFormat::RGBA32_UINT:
643 case Tegra::RenderTargetFormat::RGBA16_UINT:
644 case Tegra::RenderTargetFormat::RG16_UINT:
645 case Tegra::RenderTargetFormat::R8_UINT:
646 case Tegra::RenderTargetFormat::R16_UINT:
647 case Tegra::RenderTargetFormat::RG32_UINT:
648 case Tegra::RenderTargetFormat::R32_UINT:
649 case Tegra::RenderTargetFormat::RGBA8_UINT:
650 return ComponentType::UInt;
651 case Tegra::RenderTargetFormat::RG16_SINT:
652 case Tegra::RenderTargetFormat::R16_SINT:
653 return ComponentType::SInt;
654 default:
655 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
656 UNREACHABLE();
657 }
658 }
659
660 static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
661 switch (format) {
662 case Tegra::FramebufferConfig::PixelFormat::ABGR8:
663 return PixelFormat::ABGR8U;
664 default:
665 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
666 UNREACHABLE();
667 }
668 }
669
670 static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
671 switch (format) {
672 case Tegra::DepthFormat::Z16_UNORM:
673 case Tegra::DepthFormat::S8_Z24_UNORM:
674 case Tegra::DepthFormat::Z24_S8_UNORM:
675 return ComponentType::UNorm;
676 case Tegra::DepthFormat::Z32_FLOAT:
677 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
678 return ComponentType::Float;
679 default:
680 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
681 UNREACHABLE();
682 }
683 }
684
685 static SurfaceType GetFormatType(PixelFormat pixel_format) {
686 if (static_cast<std::size_t>(pixel_format) <
687 static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
688 return SurfaceType::ColorTexture;
689 }
690
691 if (static_cast<std::size_t>(pixel_format) <
692 static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
693 return SurfaceType::Depth;
694 }
695
696 if (static_cast<std::size_t>(pixel_format) <
697 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
698 return SurfaceType::DepthStencil;
699 }
700
701 // TODO(Subv): Implement the other formats
702 ASSERT(false);
703
704 return SurfaceType::Invalid;
705 }
706
707 /// Returns the sizer in bytes of the specified pixel format
708 static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
709 if (pixel_format == SurfaceParams::PixelFormat::Invalid) {
710 return 0;
711 }
712 return GetFormatBpp(pixel_format) / CHAR_BIT;
713 } 63 }
714 64
715 /// Returns the rectangle corresponding to this surface 65 /// Returns the rectangle corresponding to this surface
716 MathUtil::Rectangle<u32> GetRect() const; 66 MathUtil::Rectangle<u32> GetRect(u32 mip_level = 0) const;
717 67
718 /// Returns the total size of this surface in bytes, adjusted for compression 68 /// Returns the total size of this surface in bytes, adjusted for compression
719 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const { 69 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {
@@ -742,6 +92,91 @@ struct SurfaceParams {
742 return size_in_bytes_gl / 6; 92 return size_in_bytes_gl / 6;
743 } 93 }
744 94
95 /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps.
96 std::size_t MemorySize() const {
97 std::size_t size = InnerMemorySize(false, is_layered);
98 if (is_layered)
99 return size * depth;
100 return size;
101 }
102
103 /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including
104 /// mipmaps.
105 std::size_t LayerMemorySize() const {
106 return InnerMemorySize(false, true);
107 }
108
109 /// Returns the size of a layer of this surface in OpenGL.
110 std::size_t LayerSizeGL(u32 mip_level) const {
111 return InnerMipmapMemorySize(mip_level, true, is_layered, false);
112 }
113
114 std::size_t GetMipmapSizeGL(u32 mip_level, bool ignore_compressed = true) const {
115 std::size_t size = InnerMipmapMemorySize(mip_level, true, is_layered, ignore_compressed);
116 if (is_layered)
117 return size * depth;
118 return size;
119 }
120
121 std::size_t GetMipmapLevelOffset(u32 mip_level) const {
122 std::size_t offset = 0;
123 for (u32 i = 0; i < mip_level; i++)
124 offset += InnerMipmapMemorySize(i, false, is_layered);
125 return offset;
126 }
127
128 std::size_t GetMipmapLevelOffsetGL(u32 mip_level) const {
129 std::size_t offset = 0;
130 for (u32 i = 0; i < mip_level; i++)
131 offset += InnerMipmapMemorySize(i, true, is_layered);
132 return offset;
133 }
134
135 u32 MipWidth(u32 mip_level) const {
136 return std::max(1U, width >> mip_level);
137 }
138
139 u32 MipHeight(u32 mip_level) const {
140 return std::max(1U, height >> mip_level);
141 }
142
143 u32 MipDepth(u32 mip_level) const {
144 return is_layered ? depth : std::max(1U, depth >> mip_level);
145 }
146
147 // Auto block resizing algorithm from:
148 // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
149 u32 MipBlockHeight(u32 mip_level) const {
150 if (mip_level == 0)
151 return block_height;
152 u32 alt_height = MipHeight(mip_level);
153 u32 h = GetDefaultBlockHeight(pixel_format);
154 u32 blocks_in_y = (alt_height + h - 1) / h;
155 u32 bh = 16;
156 while (bh > 1 && blocks_in_y <= bh * 4) {
157 bh >>= 1;
158 }
159 return bh;
160 }
161
162 u32 MipBlockDepth(u32 mip_level) const {
163 if (mip_level == 0)
164 return block_depth;
165 if (is_layered)
166 return 1;
167 u32 depth = MipDepth(mip_level);
168 u32 bd = 32;
169 while (bd > 1 && depth * 2 <= bd) {
170 bd >>= 1;
171 }
172 if (bd == 32) {
173 u32 bh = MipBlockHeight(mip_level);
174 if (bh >= 4)
175 return 16;
176 }
177 return bd;
178 }
179
745 /// Creates SurfaceParams from a texture configuration 180 /// Creates SurfaceParams from a texture configuration
746 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, 181 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config,
747 const GLShader::SamplerEntry& entry); 182 const GLShader::SamplerEntry& entry);
@@ -782,7 +217,8 @@ struct SurfaceParams {
782 u32 unaligned_height; 217 u32 unaligned_height;
783 SurfaceTarget target; 218 SurfaceTarget target;
784 u32 max_mip_level; 219 u32 max_mip_level;
785 220 bool is_layered;
221 bool srgb_conversion;
786 // Parameters used for caching 222 // Parameters used for caching
787 VAddr addr; 223 VAddr addr;
788 Tegra::GPUVAddr gpu_addr; 224 Tegra::GPUVAddr gpu_addr;
@@ -797,6 +233,12 @@ struct SurfaceParams {
797 u32 layer_stride; 233 u32 layer_stride;
798 u32 base_layer; 234 u32 base_layer;
799 } rt; 235 } rt;
236
237private:
238 std::size_t InnerMipmapMemorySize(u32 mip_level, bool force_gl = false, bool layer_only = false,
239 bool uncompressed = false) const;
240 std::size_t InnerMemorySize(bool force_gl = false, bool layer_only = false,
241 bool uncompressed = false) const;
800}; 242};
801 243
802}; // namespace OpenGL 244}; // namespace OpenGL
@@ -822,6 +264,8 @@ struct hash<SurfaceReserveKey> {
822 264
823namespace OpenGL { 265namespace OpenGL {
824 266
267class RasterizerOpenGL;
268
825class CachedSurface final : public RasterizerCacheObject { 269class CachedSurface final : public RasterizerCacheObject {
826public: 270public:
827 CachedSurface(const SurfaceParams& params); 271 CachedSurface(const SurfaceParams& params);
@@ -858,8 +302,10 @@ public:
858 void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); 302 void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
859 303
860private: 304private:
305 void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle);
306
861 OGLTexture texture; 307 OGLTexture texture;
862 std::vector<u8> gl_buffer; 308 std::vector<std::vector<u8>> gl_buffer;
863 SurfaceParams params; 309 SurfaceParams params;
864 GLenum gl_target; 310 GLenum gl_target;
865 std::size_t cached_size_in_bytes; 311 std::size_t cached_size_in_bytes;
@@ -867,7 +313,7 @@ private:
867 313
868class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 314class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
869public: 315public:
870 RasterizerCacheOpenGL(); 316 explicit RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer);
871 317
872 /// Get a surface based on the texture configuration 318 /// Get a surface based on the texture configuration
873 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 1a03a677f..a85a7c0c5 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -6,9 +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/utils.h" 12#include "video_core/renderer_opengl/utils.h"
12 13
13namespace OpenGL { 14namespace OpenGL {
14 15
@@ -89,7 +90,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
89 shader.Create(program_result.first.c_str(), gl_type); 90 shader.Create(program_result.first.c_str(), gl_type);
90 program.Create(true, shader.handle); 91 program.Create(true, shader.handle);
91 SetShaderUniformBlockBindings(program.handle); 92 SetShaderUniformBlockBindings(program.handle);
92 VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); 93 LabelGLObject(GL_PROGRAM, program.handle, addr);
93 } else { 94 } else {
94 // Store shader's code to lazily build it on draw 95 // Store shader's code to lazily build it on draw
95 geometry_programs.code = program_result.first; 96 geometry_programs.code = program_result.first;
@@ -120,20 +121,26 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
120} 121}
121 122
122GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, 123GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
123 const std::string& glsl_topology, 124 const std::string& glsl_topology, u32 max_vertices,
124 const std::string& debug_name) { 125 const std::string& debug_name) {
125 if (target_program.handle != 0) { 126 if (target_program.handle != 0) {
126 return target_program.handle; 127 return target_program.handle;
127 } 128 }
128 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
129 OGLShader shader; 134 OGLShader shader;
130 shader.Create(source.c_str(), GL_GEOMETRY_SHADER); 135 shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
131 target_program.Create(true, shader.handle); 136 target_program.Create(true, shader.handle);
132 SetShaderUniformBlockBindings(target_program.handle); 137 SetShaderUniformBlockBindings(target_program.handle);
133 VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name); 138 LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
134 return target_program.handle; 139 return target_program.handle;
135}; 140};
136 141
142ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {}
143
137Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 144Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
138 const VAddr program_addr{GetShaderAddress(program)}; 145 const VAddr program_addr{GetShaderAddress(program)};
139 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 fe4d1bd83..5fde22ad4 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -3,11 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
6#include <optional>
6#include <set> 7#include <set>
7#include <string> 8#include <string>
8#include <string_view> 9#include <string_view>
10#include <unordered_set>
9 11
10#include <boost/optional.hpp>
11#include <fmt/format.h> 12#include <fmt/format.h>
12 13
13#include "common/assert.h" 14#include "common/assert.h"
@@ -143,7 +144,7 @@ private:
143 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { 144 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
144 const Instruction instr = {program_code[offset]}; 145 const Instruction instr = {program_code[offset]};
145 if (const auto opcode = OpCode::Decode(instr)) { 146 if (const auto opcode = OpCode::Decode(instr)) {
146 switch (opcode->GetId()) { 147 switch (opcode->get().GetId()) {
147 case OpCode::Id::EXIT: { 148 case OpCode::Id::EXIT: {
148 // The EXIT instruction can be predicated, which means that the shader can 149 // The EXIT instruction can be predicated, which means that the shader can
149 // conditionally end on this instruction. We have to consider the case where the 150 // conditionally end on this instruction. We have to consider the case where the
@@ -276,7 +277,8 @@ public:
276 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, 277 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
277 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, 278 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
278 const Tegra::Shader::Header& header) 279 const Tegra::Shader::Header& header)
279 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { 280 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header},
281 fixed_pipeline_output_attributes_used{}, local_memory_size{0} {
280 BuildRegisterList(); 282 BuildRegisterList();
281 BuildInputList(); 283 BuildInputList();
282 } 284 }
@@ -339,10 +341,10 @@ public:
339 */ 341 */
340 void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, 342 void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
341 u64 dest_num_components, u64 value_num_components, 343 u64 dest_num_components, u64 value_num_components,
342 bool is_saturated = false, u64 dest_elem = 0) { 344 bool is_saturated = false, u64 dest_elem = 0, bool precise = false) {
343 345
344 SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value, 346 SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
345 dest_num_components, value_num_components, dest_elem); 347 dest_num_components, value_num_components, dest_elem, precise);
346 } 348 }
347 349
348 /** 350 /**
@@ -366,11 +368,12 @@ public:
366 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; 368 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
367 369
368 SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', 370 SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
369 dest_num_components, value_num_components, dest_elem); 371 dest_num_components, value_num_components, dest_elem, false);
370 372
371 if (sets_cc) { 373 if (sets_cc) {
372 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; 374 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
373 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); 375 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
376 LOG_WARNING(HW_GPU, "Control Codes Imcomplete.");
374 } 377 }
375 } 378 }
376 379
@@ -414,7 +417,7 @@ public:
414 } 417 }
415 }(); 418 }();
416 419
417 SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem); 420 SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem, false);
418 } 421 }
419 422
420 /** 423 /**
@@ -428,12 +431,31 @@ public:
428 */ 431 */
429 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, 432 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
430 const Tegra::Shader::IpaMode& input_mode, 433 const Tegra::Shader::IpaMode& input_mode,
431 boost::optional<Register> vertex = {}) { 434 std::optional<Register> vertex = {}) {
432 const std::string dest = GetRegisterAsFloat(reg); 435 const std::string dest = GetRegisterAsFloat(reg);
433 const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem); 436 const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem);
434 shader.AddLine(dest + " = " + src + ';'); 437 shader.AddLine(dest + " = " + src + ';');
435 } 438 }
436 439
440 std::string GetLocalMemoryAsFloat(const std::string& index) {
441 return "lmem[" + index + ']';
442 }
443
444 std::string GetLocalMemoryAsInteger(const std::string& index, bool is_signed = false) {
445 const std::string func{is_signed ? "floatToIntBits" : "floatBitsToUint"};
446 return func + "(lmem[" + index + "])";
447 }
448
449 void SetLocalMemoryAsFloat(const std::string& index, const std::string& value) {
450 shader.AddLine("lmem[" + index + "] = " + value + ';');
451 }
452
453 void SetLocalMemoryAsInteger(const std::string& index, const std::string& value,
454 bool is_signed = false) {
455 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
456 shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");");
457 }
458
437 std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { 459 std::string GetControlCode(const Tegra::Shader::ControlCode cc) const {
438 switch (cc) { 460 switch (cc) {
439 case Tegra::Shader::ControlCode::NEU: 461 case Tegra::Shader::ControlCode::NEU:
@@ -472,15 +494,20 @@ public:
472 // instruction for now. 494 // instruction for now.
473 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { 495 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
474 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry 496 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
475 // 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
476 // drivers from complaining for the out of boundary writes, guard them. 498 // drivers from complaining about out of boundary writes, guard them.
477 const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " + 499 const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
478 std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'}; 500 std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
479 shader.AddLine("amem[" + buf_index + "][" + 501 shader.AddLine("amem[" + buf_index + "][" +
480 std::to_string(static_cast<u32>(attribute)) + ']' + 502 std::to_string(static_cast<u32>(attribute)) + ']' +
481 GetSwizzle(elem) + " = " + src + ';'); 503 GetSwizzle(elem) + " = " + src + ';');
482 } else { 504 } else {
483 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); 505 if (attribute == Attribute::Index::PointSize) {
506 fixed_pipeline_output_attributes_used.insert(attribute);
507 shader.AddLine(dest + " = " + src + ';');
508 } else {
509 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
510 }
484 } 511 }
485 } 512 }
486 } 513 }
@@ -524,7 +551,9 @@ public:
524 551
525 /// Add declarations. 552 /// Add declarations.
526 void GenerateDeclarations(const std::string& suffix) { 553 void GenerateDeclarations(const std::string& suffix) {
554 GenerateVertex();
527 GenerateRegisters(suffix); 555 GenerateRegisters(suffix);
556 GenerateLocalMemory();
528 GenerateInternalFlags(); 557 GenerateInternalFlags();
529 GenerateInputAttrs(); 558 GenerateInputAttrs();
530 GenerateOutputAttrs(); 559 GenerateOutputAttrs();
@@ -570,6 +599,10 @@ public:
570 return entry.GetName(); 599 return entry.GetName();
571 } 600 }
572 601
602 void SetLocalMemory(u64 lmem) {
603 local_memory_size = lmem;
604 }
605
573private: 606private:
574 /// Generates declarations for registers. 607 /// Generates declarations for registers.
575 void GenerateRegisters(const std::string& suffix) { 608 void GenerateRegisters(const std::string& suffix) {
@@ -580,6 +613,15 @@ private:
580 declarations.AddNewLine(); 613 declarations.AddNewLine();
581 } 614 }
582 615
616 /// Generates declarations for local memory.
617 void GenerateLocalMemory() {
618 if (local_memory_size > 0) {
619 declarations.AddLine("float lmem[" + std::to_string((local_memory_size - 1 + 4) / 4) +
620 "];");
621 declarations.AddNewLine();
622 }
623 }
624
583 /// Generates declarations for internal flags. 625 /// Generates declarations for internal flags.
584 void GenerateInternalFlags() { 626 void GenerateInternalFlags() {
585 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { 627 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
@@ -683,6 +725,20 @@ private:
683 declarations.AddNewLine(); 725 declarations.AddNewLine();
684 } 726 }
685 727
728 void GenerateVertex() {
729 if (stage != Maxwell3D::Regs::ShaderStage::Vertex)
730 return;
731 declarations.AddLine("out gl_PerVertex {");
732 ++declarations.scope;
733 declarations.AddLine("vec4 gl_Position;");
734 for (auto& o : fixed_pipeline_output_attributes_used) {
735 if (o == Attribute::Index::PointSize)
736 declarations.AddLine("float gl_PointSize;");
737 }
738 --declarations.scope;
739 declarations.AddLine("};");
740 }
741
686 /// Generates code representing a temporary (GPR) register. 742 /// Generates code representing a temporary (GPR) register.
687 std::string GetRegister(const Register& reg, unsigned elem) { 743 std::string GetRegister(const Register& reg, unsigned elem) {
688 if (reg == Register::ZeroIndex) { 744 if (reg == Register::ZeroIndex) {
@@ -702,7 +758,8 @@ private:
702 * @param dest_elem Optional, the destination element to use for the operation. 758 * @param dest_elem Optional, the destination element to use for the operation.
703 */ 759 */
704 void SetRegister(const Register& reg, u64 elem, const std::string& value, 760 void SetRegister(const Register& reg, u64 elem, const std::string& value,
705 u64 dest_num_components, u64 value_num_components, u64 dest_elem) { 761 u64 dest_num_components, u64 value_num_components, u64 dest_elem,
762 bool precise) {
706 if (reg == Register::ZeroIndex) { 763 if (reg == Register::ZeroIndex) {
707 LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); 764 LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex");
708 UNREACHABLE(); 765 UNREACHABLE();
@@ -719,7 +776,18 @@ private:
719 src += GetSwizzle(elem); 776 src += GetSwizzle(elem);
720 } 777 }
721 778
722 shader.AddLine(dest + " = " + src + ';'); 779 if (precise && stage != Maxwell3D::Regs::ShaderStage::Fragment) {
780 shader.AddLine('{');
781 ++shader.scope;
782 // This avoids optimizations of constant propagation and keeps the code as the original
783 // Sadly using the precise keyword causes "linking" errors on fragment shaders.
784 shader.AddLine("precise float tmp = " + src + ';');
785 shader.AddLine(dest + " = tmp;");
786 --shader.scope;
787 shader.AddLine('}');
788 } else {
789 shader.AddLine(dest + " = " + src + ';');
790 }
723 } 791 }
724 792
725 /// Build the GLSL register list. 793 /// Build the GLSL register list.
@@ -740,10 +808,14 @@ private:
740 /// Generates code representing an input attribute register. 808 /// Generates code representing an input attribute register.
741 std::string GetInputAttribute(Attribute::Index attribute, 809 std::string GetInputAttribute(Attribute::Index attribute,
742 const Tegra::Shader::IpaMode& input_mode, 810 const Tegra::Shader::IpaMode& input_mode,
743 boost::optional<Register> vertex = {}) { 811 std::optional<Register> vertex = {}) {
744 auto GeometryPass = [&](const std::string& name) { 812 auto GeometryPass = [&](const std::string& name) {
745 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) { 813 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
746 return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 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]";
747 } 819 }
748 return name; 820 return name;
749 }; 821 };
@@ -836,6 +908,8 @@ private:
836 /// Generates code representing the declaration name of an output attribute register. 908 /// Generates code representing the declaration name of an output attribute register.
837 std::string GetOutputAttribute(Attribute::Index attribute) { 909 std::string GetOutputAttribute(Attribute::Index attribute) {
838 switch (attribute) { 910 switch (attribute) {
911 case Attribute::Index::PointSize:
912 return "gl_PointSize";
839 case Attribute::Index::Position: 913 case Attribute::Index::Position:
840 return "position"; 914 return "position";
841 default: 915 default:
@@ -870,6 +944,8 @@ private:
870 const Maxwell3D::Regs::ShaderStage& stage; 944 const Maxwell3D::Regs::ShaderStage& stage;
871 const std::string& suffix; 945 const std::string& suffix;
872 const Tegra::Shader::Header& header; 946 const Tegra::Shader::Header& header;
947 std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used;
948 u64 local_memory_size;
873}; 949};
874 950
875class GLSLGenerator { 951class GLSLGenerator {
@@ -879,6 +955,8 @@ public:
879 : subroutines(subroutines), program_code(program_code), main_offset(main_offset), 955 : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
880 stage(stage), suffix(suffix) { 956 stage(stage), suffix(suffix) {
881 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); 957 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
958 local_memory_size = header.GetLocalMemorySize();
959 regs.SetLocalMemory(local_memory_size);
882 Generate(suffix); 960 Generate(suffix);
883 } 961 }
884 962
@@ -1392,7 +1470,7 @@ private:
1392 } 1470 }
1393 1471
1394 shader.AddLine( 1472 shader.AddLine(
1395 fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value)); 1473 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value));
1396 1474
1397 using Tegra::Shader::Pred; 1475 using Tegra::Shader::Pred;
1398 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 1476 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -1400,7 +1478,7 @@ private:
1400 1478
1401 // Some instructions (like SSY) don't have a predicate field, they are always 1479 // Some instructions (like SSY) don't have a predicate field, they are always
1402 // unconditionally executed. 1480 // unconditionally executed.
1403 bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->GetId()); 1481 bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->get().GetId());
1404 1482
1405 if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { 1483 if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
1406 shader.AddLine("if (" + 1484 shader.AddLine("if (" +
@@ -1410,7 +1488,7 @@ private:
1410 ++shader.scope; 1488 ++shader.scope;
1411 } 1489 }
1412 1490
1413 switch (opcode->GetType()) { 1491 switch (opcode->get().GetType()) {
1414 case OpCode::Type::Arithmetic: { 1492 case OpCode::Type::Arithmetic: {
1415 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1493 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1416 1494
@@ -1427,7 +1505,7 @@ private:
1427 } 1505 }
1428 } 1506 }
1429 1507
1430 switch (opcode->GetId()) { 1508 switch (opcode->get().GetId()) {
1431 case OpCode::Id::MOV_C: 1509 case OpCode::Id::MOV_C:
1432 case OpCode::Id::MOV_R: { 1510 case OpCode::Id::MOV_R: {
1433 // MOV does not have neither 'abs' nor 'neg' bits. 1511 // MOV does not have neither 'abs' nor 'neg' bits.
@@ -1449,8 +1527,13 @@ private:
1449 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); 1527 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented");
1450 1528
1451 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); 1529 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
1530
1452 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1531 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
1453 instr.alu.saturate_d); 1532 instr.alu.saturate_d, 0, true);
1533 if (instr.generates_cc) {
1534 LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code");
1535 UNREACHABLE();
1536 }
1454 break; 1537 break;
1455 } 1538 }
1456 case OpCode::Id::FADD_C: 1539 case OpCode::Id::FADD_C:
@@ -1458,8 +1541,13 @@ private:
1458 case OpCode::Id::FADD_IMM: { 1541 case OpCode::Id::FADD_IMM: {
1459 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1542 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
1460 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); 1543 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1544
1461 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, 1545 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
1462 instr.alu.saturate_d); 1546 instr.alu.saturate_d, 0, true);
1547 if (instr.generates_cc) {
1548 LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code");
1549 UNREACHABLE();
1550 }
1463 break; 1551 break;
1464 } 1552 }
1465 case OpCode::Id::MUFU: { 1553 case OpCode::Id::MUFU: {
@@ -1467,31 +1555,31 @@ private:
1467 switch (instr.sub_op) { 1555 switch (instr.sub_op) {
1468 case SubOp::Cos: 1556 case SubOp::Cos:
1469 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, 1557 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
1470 instr.alu.saturate_d); 1558 instr.alu.saturate_d, 0, true);
1471 break; 1559 break;
1472 case SubOp::Sin: 1560 case SubOp::Sin:
1473 regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1, 1561 regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
1474 instr.alu.saturate_d); 1562 instr.alu.saturate_d, 0, true);
1475 break; 1563 break;
1476 case SubOp::Ex2: 1564 case SubOp::Ex2:
1477 regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1, 1565 regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
1478 instr.alu.saturate_d); 1566 instr.alu.saturate_d, 0, true);
1479 break; 1567 break;
1480 case SubOp::Lg2: 1568 case SubOp::Lg2:
1481 regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1, 1569 regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
1482 instr.alu.saturate_d); 1570 instr.alu.saturate_d, 0, true);
1483 break; 1571 break;
1484 case SubOp::Rcp: 1572 case SubOp::Rcp:
1485 regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, 1573 regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
1486 instr.alu.saturate_d); 1574 instr.alu.saturate_d, 0, true);
1487 break; 1575 break;
1488 case SubOp::Rsq: 1576 case SubOp::Rsq:
1489 regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1, 1577 regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
1490 instr.alu.saturate_d); 1578 instr.alu.saturate_d, 0, true);
1491 break; 1579 break;
1492 case SubOp::Sqrt: 1580 case SubOp::Sqrt:
1493 regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1, 1581 regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1,
1494 instr.alu.saturate_d); 1582 instr.alu.saturate_d, 0, true);
1495 break; 1583 break;
1496 default: 1584 default:
1497 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", 1585 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}",
@@ -1512,7 +1600,11 @@ private:
1512 regs.SetRegisterToFloat(instr.gpr0, 0, 1600 regs.SetRegisterToFloat(instr.gpr0, 0,
1513 '(' + condition + ") ? min(" + parameters + ") : max(" + 1601 '(' + condition + ") ? min(" + parameters + ") : max(" +
1514 parameters + ')', 1602 parameters + ')',
1515 1, 1); 1603 1, 1, false, 0, true);
1604 if (instr.generates_cc) {
1605 LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code");
1606 UNREACHABLE();
1607 }
1516 break; 1608 break;
1517 } 1609 }
1518 case OpCode::Id::RRO_C: 1610 case OpCode::Id::RRO_C:
@@ -1525,14 +1617,15 @@ private:
1525 break; 1617 break;
1526 } 1618 }
1527 default: { 1619 default: {
1528 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); 1620 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}",
1621 opcode->get().GetName());
1529 UNREACHABLE(); 1622 UNREACHABLE();
1530 } 1623 }
1531 } 1624 }
1532 break; 1625 break;
1533 } 1626 }
1534 case OpCode::Type::ArithmeticImmediate: { 1627 case OpCode::Type::ArithmeticImmediate: {
1535 switch (opcode->GetId()) { 1628 switch (opcode->get().GetId()) {
1536 case OpCode::Id::MOV32_IMM: { 1629 case OpCode::Id::MOV32_IMM: {
1537 regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1); 1630 regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
1538 break; 1631 break;
@@ -1541,7 +1634,11 @@ private:
1541 regs.SetRegisterToFloat(instr.gpr0, 0, 1634 regs.SetRegisterToFloat(instr.gpr0, 0,
1542 regs.GetRegisterAsFloat(instr.gpr8) + " * " + 1635 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1543 GetImmediate32(instr), 1636 GetImmediate32(instr),
1544 1, 1, instr.fmul32.saturate); 1637 1, 1, instr.fmul32.saturate, 0, true);
1638 if (instr.op_32.generates_cc) {
1639 LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code");
1640 UNREACHABLE();
1641 }
1545 break; 1642 break;
1546 } 1643 }
1547 case OpCode::Id::FADD32I: { 1644 case OpCode::Id::FADD32I: {
@@ -1564,7 +1661,11 @@ private:
1564 op_b = "-(" + op_b + ')'; 1661 op_b = "-(" + op_b + ')';
1565 } 1662 }
1566 1663
1567 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1); 1664 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
1665 if (instr.op_32.generates_cc) {
1666 LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code");
1667 UNREACHABLE();
1668 }
1568 break; 1669 break;
1569 } 1670 }
1570 } 1671 }
@@ -1576,7 +1677,7 @@ private:
1576 std::string op_a = instr.bfe.negate_a ? "-" : ""; 1677 std::string op_a = instr.bfe.negate_a ? "-" : "";
1577 op_a += regs.GetRegisterAsInteger(instr.gpr8); 1678 op_a += regs.GetRegisterAsInteger(instr.gpr8);
1578 1679
1579 switch (opcode->GetId()) { 1680 switch (opcode->get().GetId()) {
1580 case OpCode::Id::BFE_IMM: { 1681 case OpCode::Id::BFE_IMM: {
1581 std::string inner_shift = 1682 std::string inner_shift =
1582 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; 1683 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
@@ -1585,10 +1686,14 @@ private:
1585 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; 1686 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
1586 1687
1587 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); 1688 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
1689 if (instr.generates_cc) {
1690 LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code");
1691 UNREACHABLE();
1692 }
1588 break; 1693 break;
1589 } 1694 }
1590 default: { 1695 default: {
1591 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName()); 1696 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName());
1592 UNREACHABLE(); 1697 UNREACHABLE();
1593 } 1698 }
1594 } 1699 }
@@ -1610,7 +1715,7 @@ private:
1610 } 1715 }
1611 } 1716 }
1612 1717
1613 switch (opcode->GetId()) { 1718 switch (opcode->get().GetId()) {
1614 case OpCode::Id::SHR_C: 1719 case OpCode::Id::SHR_C:
1615 case OpCode::Id::SHR_R: 1720 case OpCode::Id::SHR_R:
1616 case OpCode::Id::SHR_IMM: { 1721 case OpCode::Id::SHR_IMM: {
@@ -1622,15 +1727,23 @@ private:
1622 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift 1727 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift
1623 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', 1728 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
1624 1, 1); 1729 1, 1);
1730 if (instr.generates_cc) {
1731 LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code");
1732 UNREACHABLE();
1733 }
1625 break; 1734 break;
1626 } 1735 }
1627 case OpCode::Id::SHL_C: 1736 case OpCode::Id::SHL_C:
1628 case OpCode::Id::SHL_R: 1737 case OpCode::Id::SHL_R:
1629 case OpCode::Id::SHL_IMM: 1738 case OpCode::Id::SHL_IMM:
1630 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); 1739 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
1740 if (instr.generates_cc) {
1741 LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code");
1742 UNREACHABLE();
1743 }
1631 break; 1744 break;
1632 default: { 1745 default: {
1633 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); 1746 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName());
1634 UNREACHABLE(); 1747 UNREACHABLE();
1635 } 1748 }
1636 } 1749 }
@@ -1640,13 +1753,17 @@ private:
1640 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1753 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1641 std::string op_b = std::to_string(instr.alu.imm20_32.Value()); 1754 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
1642 1755
1643 switch (opcode->GetId()) { 1756 switch (opcode->get().GetId()) {
1644 case OpCode::Id::IADD32I: 1757 case OpCode::Id::IADD32I:
1645 if (instr.iadd32i.negate_a) 1758 if (instr.iadd32i.negate_a)
1646 op_a = "-(" + op_a + ')'; 1759 op_a = "-(" + op_a + ')';
1647 1760
1648 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1761 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1649 instr.iadd32i.saturate != 0); 1762 instr.iadd32i.saturate != 0);
1763 if (instr.op_32.generates_cc) {
1764 LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code");
1765 UNREACHABLE();
1766 }
1650 break; 1767 break;
1651 case OpCode::Id::LOP32I: { 1768 case OpCode::Id::LOP32I: {
1652 if (instr.alu.lop32i.invert_a) 1769 if (instr.alu.lop32i.invert_a)
@@ -1658,11 +1775,15 @@ private:
1658 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, 1775 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
1659 Tegra::Shader::PredicateResultMode::None, 1776 Tegra::Shader::PredicateResultMode::None,
1660 Tegra::Shader::Pred::UnusedIndex); 1777 Tegra::Shader::Pred::UnusedIndex);
1778 if (instr.op_32.generates_cc) {
1779 LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code");
1780 UNREACHABLE();
1781 }
1661 break; 1782 break;
1662 } 1783 }
1663 default: { 1784 default: {
1664 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", 1785 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}",
1665 opcode->GetName()); 1786 opcode->get().GetName());
1666 UNREACHABLE(); 1787 UNREACHABLE();
1667 } 1788 }
1668 } 1789 }
@@ -1682,7 +1803,7 @@ private:
1682 } 1803 }
1683 } 1804 }
1684 1805
1685 switch (opcode->GetId()) { 1806 switch (opcode->get().GetId()) {
1686 case OpCode::Id::IADD_C: 1807 case OpCode::Id::IADD_C:
1687 case OpCode::Id::IADD_R: 1808 case OpCode::Id::IADD_R:
1688 case OpCode::Id::IADD_IMM: { 1809 case OpCode::Id::IADD_IMM: {
@@ -1694,6 +1815,10 @@ private:
1694 1815
1695 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1816 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1696 instr.alu.saturate_d); 1817 instr.alu.saturate_d);
1818 if (instr.generates_cc) {
1819 LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code");
1820 UNREACHABLE();
1821 }
1697 break; 1822 break;
1698 } 1823 }
1699 case OpCode::Id::IADD3_C: 1824 case OpCode::Id::IADD3_C:
@@ -1718,7 +1843,7 @@ private:
1718 } 1843 }
1719 }; 1844 };
1720 1845
1721 if (opcode->GetId() == OpCode::Id::IADD3_R) { 1846 if (opcode->get().GetId() == OpCode::Id::IADD3_R) {
1722 apply_height(instr.iadd3.height_a, op_a); 1847 apply_height(instr.iadd3.height_a, op_a);
1723 apply_height(instr.iadd3.height_b, op_b); 1848 apply_height(instr.iadd3.height_b, op_b);
1724 apply_height(instr.iadd3.height_c, op_c); 1849 apply_height(instr.iadd3.height_c, op_c);
@@ -1734,7 +1859,7 @@ private:
1734 op_c = "-(" + op_c + ')'; 1859 op_c = "-(" + op_c + ')';
1735 1860
1736 std::string result; 1861 std::string result;
1737 if (opcode->GetId() == OpCode::Id::IADD3_R) { 1862 if (opcode->get().GetId() == OpCode::Id::IADD3_R) {
1738 switch (instr.iadd3.mode) { 1863 switch (instr.iadd3.mode) {
1739 case Tegra::Shader::IAdd3Mode::RightShift: 1864 case Tegra::Shader::IAdd3Mode::RightShift:
1740 // TODO(tech4me): According to 1865 // TODO(tech4me): According to
@@ -1755,6 +1880,11 @@ private:
1755 } 1880 }
1756 1881
1757 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); 1882 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
1883
1884 if (instr.generates_cc) {
1885 LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code");
1886 UNREACHABLE();
1887 }
1758 break; 1888 break;
1759 } 1889 }
1760 case OpCode::Id::ISCADD_C: 1890 case OpCode::Id::ISCADD_C:
@@ -1770,6 +1900,10 @@ private:
1770 1900
1771 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1901 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1772 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1902 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
1903 if (instr.generates_cc) {
1904 LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code");
1905 UNREACHABLE();
1906 }
1773 break; 1907 break;
1774 } 1908 }
1775 case OpCode::Id::POPC_C: 1909 case OpCode::Id::POPC_C:
@@ -1801,6 +1935,10 @@ private:
1801 1935
1802 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, 1936 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
1803 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); 1937 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
1938 if (instr.generates_cc) {
1939 LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code");
1940 UNREACHABLE();
1941 }
1804 break; 1942 break;
1805 } 1943 }
1806 case OpCode::Id::LOP3_C: 1944 case OpCode::Id::LOP3_C:
@@ -1809,13 +1947,17 @@ private:
1809 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1947 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1810 std::string lut; 1948 std::string lut;
1811 1949
1812 if (opcode->GetId() == OpCode::Id::LOP3_R) { 1950 if (opcode->get().GetId() == OpCode::Id::LOP3_R) {
1813 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')'; 1951 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')';
1814 } else { 1952 } else {
1815 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')'; 1953 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')';
1816 } 1954 }
1817 1955
1818 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); 1956 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
1957 if (instr.generates_cc) {
1958 LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code");
1959 UNREACHABLE();
1960 }
1819 break; 1961 break;
1820 } 1962 }
1821 case OpCode::Id::IMNMX_C: 1963 case OpCode::Id::IMNMX_C:
@@ -1830,6 +1972,10 @@ private:
1830 '(' + condition + ") ? min(" + parameters + ") : max(" + 1972 '(' + condition + ") ? min(" + parameters + ") : max(" +
1831 parameters + ')', 1973 parameters + ')',
1832 1, 1); 1974 1, 1);
1975 if (instr.generates_cc) {
1976 LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code");
1977 UNREACHABLE();
1978 }
1833 break; 1979 break;
1834 } 1980 }
1835 case OpCode::Id::LEA_R2: 1981 case OpCode::Id::LEA_R2:
@@ -1839,7 +1985,7 @@ private:
1839 case OpCode::Id::LEA_HI: { 1985 case OpCode::Id::LEA_HI: {
1840 std::string op_c; 1986 std::string op_c;
1841 1987
1842 switch (opcode->GetId()) { 1988 switch (opcode->get().GetId()) {
1843 case OpCode::Id::LEA_R2: { 1989 case OpCode::Id::LEA_R2: {
1844 op_a = regs.GetRegisterAsInteger(instr.gpr20); 1990 op_a = regs.GetRegisterAsInteger(instr.gpr20);
1845 op_b = regs.GetRegisterAsInteger(instr.gpr39); 1991 op_b = regs.GetRegisterAsInteger(instr.gpr39);
@@ -1884,7 +2030,8 @@ private:
1884 op_b = regs.GetRegisterAsInteger(instr.gpr8); 2030 op_b = regs.GetRegisterAsInteger(instr.gpr8);
1885 op_a = std::to_string(instr.lea.imm.entry_a); 2031 op_a = std::to_string(instr.lea.imm.entry_a);
1886 op_c = std::to_string(instr.lea.imm.entry_b); 2032 op_c = std::to_string(instr.lea.imm.entry_b);
1887 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", opcode->GetName()); 2033 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}",
2034 opcode->get().GetName());
1888 UNREACHABLE(); 2035 UNREACHABLE();
1889 } 2036 }
1890 } 2037 }
@@ -1899,7 +2046,7 @@ private:
1899 } 2046 }
1900 default: { 2047 default: {
1901 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", 2048 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
1902 opcode->GetName()); 2049 opcode->get().GetName());
1903 UNREACHABLE(); 2050 UNREACHABLE();
1904 } 2051 }
1905 } 2052 }
@@ -1907,20 +2054,21 @@ private:
1907 break; 2054 break;
1908 } 2055 }
1909 case OpCode::Type::ArithmeticHalf: { 2056 case OpCode::Type::ArithmeticHalf: {
1910 if (opcode->GetId() == OpCode::Id::HADD2_C || opcode->GetId() == OpCode::Id::HADD2_R) { 2057 if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
2058 opcode->get().GetId() == OpCode::Id::HADD2_R) {
1911 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); 2059 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented");
1912 } 2060 }
1913 const bool negate_a = 2061 const bool negate_a =
1914 opcode->GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; 2062 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
1915 const bool negate_b = 2063 const bool negate_b =
1916 opcode->GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; 2064 opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
1917 2065
1918 const std::string op_a = 2066 const std::string op_a =
1919 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a, 2067 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a,
1920 instr.alu_half.abs_a != 0, negate_a); 2068 instr.alu_half.abs_a != 0, negate_a);
1921 2069
1922 std::string op_b; 2070 std::string op_b;
1923 switch (opcode->GetId()) { 2071 switch (opcode->get().GetId()) {
1924 case OpCode::Id::HADD2_C: 2072 case OpCode::Id::HADD2_C:
1925 case OpCode::Id::HMUL2_C: 2073 case OpCode::Id::HMUL2_C:
1926 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 2074 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
@@ -1938,7 +2086,7 @@ private:
1938 op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b); 2086 op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b);
1939 2087
1940 const std::string result = [&]() { 2088 const std::string result = [&]() {
1941 switch (opcode->GetId()) { 2089 switch (opcode->get().GetId()) {
1942 case OpCode::Id::HADD2_C: 2090 case OpCode::Id::HADD2_C:
1943 case OpCode::Id::HADD2_R: 2091 case OpCode::Id::HADD2_R:
1944 return '(' + op_a + " + " + op_b + ')'; 2092 return '(' + op_a + " + " + op_b + ')';
@@ -1946,7 +2094,8 @@ private:
1946 case OpCode::Id::HMUL2_R: 2094 case OpCode::Id::HMUL2_R:
1947 return '(' + op_a + " * " + op_b + ')'; 2095 return '(' + op_a + " * " + op_b + ')';
1948 default: 2096 default:
1949 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", opcode->GetName()); 2097 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}",
2098 opcode->get().GetName());
1950 UNREACHABLE(); 2099 UNREACHABLE();
1951 return std::string("0"); 2100 return std::string("0");
1952 } 2101 }
@@ -1957,7 +2106,7 @@ private:
1957 break; 2106 break;
1958 } 2107 }
1959 case OpCode::Type::ArithmeticHalfImmediate: { 2108 case OpCode::Type::ArithmeticHalfImmediate: {
1960 if (opcode->GetId() == OpCode::Id::HADD2_IMM) { 2109 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
1961 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); 2110 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented");
1962 } else { 2111 } else {
1963 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, 2112 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None,
@@ -1971,7 +2120,7 @@ private:
1971 const std::string op_b = UnpackHalfImmediate(instr, true); 2120 const std::string op_b = UnpackHalfImmediate(instr, true);
1972 2121
1973 const std::string result = [&]() { 2122 const std::string result = [&]() {
1974 switch (opcode->GetId()) { 2123 switch (opcode->get().GetId()) {
1975 case OpCode::Id::HADD2_IMM: 2124 case OpCode::Id::HADD2_IMM:
1976 return op_a + " + " + op_b; 2125 return op_a + " + " + op_b;
1977 case OpCode::Id::HMUL2_IMM: 2126 case OpCode::Id::HMUL2_IMM:
@@ -1997,7 +2146,7 @@ private:
1997 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", 2146 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented",
1998 instr.ffma.tab5980_1.Value()); 2147 instr.ffma.tab5980_1.Value());
1999 2148
2000 switch (opcode->GetId()) { 2149 switch (opcode->get().GetId()) {
2001 case OpCode::Id::FFMA_CR: { 2150 case OpCode::Id::FFMA_CR: {
2002 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 2151 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2003 GLSLRegister::Type::Float); 2152 GLSLRegister::Type::Float);
@@ -2021,24 +2170,29 @@ private:
2021 break; 2170 break;
2022 } 2171 }
2023 default: { 2172 default: {
2024 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); 2173 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName());
2025 UNREACHABLE(); 2174 UNREACHABLE();
2026 } 2175 }
2027 } 2176 }
2028 2177
2029 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1, 2178 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
2030 instr.alu.saturate_d); 2179 1, 1, instr.alu.saturate_d, 0, true);
2180 if (instr.generates_cc) {
2181 LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code");
2182 UNREACHABLE();
2183 }
2184
2031 break; 2185 break;
2032 } 2186 }
2033 case OpCode::Type::Hfma2: { 2187 case OpCode::Type::Hfma2: {
2034 if (opcode->GetId() == OpCode::Id::HFMA2_RR) { 2188 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) {
2035 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, 2189 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None,
2036 "Unimplemented"); 2190 "Unimplemented");
2037 } else { 2191 } else {
2038 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, 2192 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None,
2039 "Unimplemented"); 2193 "Unimplemented");
2040 } 2194 }
2041 const bool saturate = opcode->GetId() == OpCode::Id::HFMA2_RR 2195 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR
2042 ? instr.hfma2.rr.saturate != 0 2196 ? instr.hfma2.rr.saturate != 0
2043 : instr.hfma2.saturate != 0; 2197 : instr.hfma2.saturate != 0;
2044 2198
@@ -2046,7 +2200,7 @@ private:
2046 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a); 2200 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a);
2047 std::string op_b, op_c; 2201 std::string op_b, op_c;
2048 2202
2049 switch (opcode->GetId()) { 2203 switch (opcode->get().GetId()) {
2050 case OpCode::Id::HFMA2_CR: 2204 case OpCode::Id::HFMA2_CR:
2051 op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 2205 op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2052 GLSLRegister::Type::UnsignedInteger), 2206 GLSLRegister::Type::UnsignedInteger),
@@ -2084,7 +2238,7 @@ private:
2084 break; 2238 break;
2085 } 2239 }
2086 case OpCode::Type::Conversion: { 2240 case OpCode::Type::Conversion: {
2087 switch (opcode->GetId()) { 2241 switch (opcode->get().GetId()) {
2088 case OpCode::Id::I2I_R: { 2242 case OpCode::Id::I2I_R: {
2089 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2243 ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
2090 2244
@@ -2132,6 +2286,11 @@ private:
2132 } 2286 }
2133 2287
2134 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2288 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2289
2290 if (instr.generates_cc) {
2291 LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code");
2292 UNREACHABLE();
2293 }
2135 break; 2294 break;
2136 } 2295 }
2137 case OpCode::Id::F2F_R: { 2296 case OpCode::Id::F2F_R: {
@@ -2170,6 +2329,11 @@ private:
2170 } 2329 }
2171 2330
2172 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); 2331 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
2332
2333 if (instr.generates_cc) {
2334 LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code");
2335 UNREACHABLE();
2336 }
2173 break; 2337 break;
2174 } 2338 }
2175 case OpCode::Id::F2I_R: 2339 case OpCode::Id::F2I_R:
@@ -2219,17 +2383,22 @@ private:
2219 2383
2220 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 2384 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
2221 1, false, 0, instr.conversion.dest_size); 2385 1, false, 0, instr.conversion.dest_size);
2386 if (instr.generates_cc) {
2387 LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code");
2388 UNREACHABLE();
2389 }
2222 break; 2390 break;
2223 } 2391 }
2224 default: { 2392 default: {
2225 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); 2393 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}",
2394 opcode->get().GetName());
2226 UNREACHABLE(); 2395 UNREACHABLE();
2227 } 2396 }
2228 } 2397 }
2229 break; 2398 break;
2230 } 2399 }
2231 case OpCode::Type::Memory: { 2400 case OpCode::Type::Memory: {
2232 switch (opcode->GetId()) { 2401 switch (opcode->get().GetId()) {
2233 case OpCode::Id::LD_A: { 2402 case OpCode::Id::LD_A: {
2234 // Note: Shouldn't this be interp mode flat? As in no interpolation made. 2403 // Note: Shouldn't this be interp mode flat? As in no interpolation made.
2235 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2404 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex,
@@ -2299,6 +2468,39 @@ private:
2299 shader.AddLine("}"); 2468 shader.AddLine("}");
2300 break; 2469 break;
2301 } 2470 }
2471 case OpCode::Id::LD_L: {
2472 // Add an extra scope and declare the index register inside to prevent
2473 // overwriting it in case it is used as an output of the LD instruction.
2474 shader.AddLine('{');
2475 ++shader.scope;
2476
2477 std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " +
2478 std::to_string(instr.smem_imm.Value()) + ')';
2479
2480 shader.AddLine("uint index = (" + op + " / 4);");
2481
2482 const std::string op_a = regs.GetLocalMemoryAsFloat("index");
2483
2484 if (instr.ld_l.unknown != 1) {
2485 LOG_CRITICAL(HW_GPU, "LD_L Unhandled mode: {}",
2486 static_cast<unsigned>(instr.ld_l.unknown.Value()));
2487 UNREACHABLE();
2488 }
2489
2490 switch (instr.ldst_sl.type.Value()) {
2491 case Tegra::Shader::StoreType::Bytes32:
2492 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2493 break;
2494 default:
2495 LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}",
2496 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2497 UNREACHABLE();
2498 }
2499
2500 --shader.scope;
2501 shader.AddLine('}');
2502 break;
2503 }
2302 case OpCode::Id::ST_A: { 2504 case OpCode::Id::ST_A: {
2303 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2505 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex,
2304 "Indirect attribute loads are not supported"); 2506 "Indirect attribute loads are not supported");
@@ -2327,6 +2529,37 @@ private:
2327 2529
2328 break; 2530 break;
2329 } 2531 }
2532 case OpCode::Id::ST_L: {
2533 // Add an extra scope and declare the index register inside to prevent
2534 // overwriting it in case it is used as an output of the LD instruction.
2535 shader.AddLine('{');
2536 ++shader.scope;
2537
2538 std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " +
2539 std::to_string(instr.smem_imm.Value()) + ')';
2540
2541 shader.AddLine("uint index = (" + op + " / 4);");
2542
2543 if (instr.st_l.unknown != 0) {
2544 LOG_CRITICAL(HW_GPU, "ST_L Unhandled mode: {}",
2545 static_cast<unsigned>(instr.st_l.unknown.Value()));
2546 UNREACHABLE();
2547 }
2548
2549 switch (instr.ldst_sl.type.Value()) {
2550 case Tegra::Shader::StoreType::Bytes32:
2551 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0));
2552 break;
2553 default:
2554 LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}",
2555 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2556 UNREACHABLE();
2557 }
2558
2559 --shader.scope;
2560 shader.AddLine('}');
2561 break;
2562 }
2330 case OpCode::Id::TEX: { 2563 case OpCode::Id::TEX: {
2331 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; 2564 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
2332 std::string coord; 2565 std::string coord;
@@ -2513,12 +2746,12 @@ private:
2513 } 2746 }
2514 case 3: { 2747 case 3: {
2515 if (is_array) { 2748 if (is_array) {
2516 UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented"); 2749 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2517 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2750 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2518 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2751 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2519 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2752 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2520 texture_type = Tegra::Shader::TextureType::Texture2D; 2753 coord =
2521 is_array = false; 2754 "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");";
2522 } else { 2755 } else {
2523 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2756 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2524 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2757 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -2548,7 +2781,11 @@ private:
2548 break; 2781 break;
2549 } 2782 }
2550 case Tegra::Shader::TextureProcessMode::LZ: { 2783 case Tegra::Shader::TextureProcessMode::LZ: {
2551 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 }
2552 break; 2789 break;
2553 } 2790 }
2554 case Tegra::Shader::TextureProcessMode::LL: { 2791 case Tegra::Shader::TextureProcessMode::LL: {
@@ -2809,7 +3046,7 @@ private:
2809 break; 3046 break;
2810 } 3047 }
2811 default: { 3048 default: {
2812 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); 3049 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName());
2813 UNREACHABLE(); 3050 UNREACHABLE();
2814 } 3051 }
2815 } 3052 }
@@ -2903,7 +3140,7 @@ private:
2903 instr.hsetp2.abs_a, instr.hsetp2.negate_a); 3140 instr.hsetp2.abs_a, instr.hsetp2.negate_a);
2904 3141
2905 const std::string op_b = [&]() { 3142 const std::string op_b = [&]() {
2906 switch (opcode->GetId()) { 3143 switch (opcode->get().GetId()) {
2907 case OpCode::Id::HSETP2_R: 3144 case OpCode::Id::HSETP2_R:
2908 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false), 3145 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
2909 instr.hsetp2.type_b, instr.hsetp2.abs_a, 3146 instr.hsetp2.type_b, instr.hsetp2.abs_a,
@@ -2962,10 +3199,15 @@ private:
2962 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); 3199 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
2963 } 3200 }
2964 3201
3202 if (instr.generates_cc) {
3203 LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code");
3204 UNREACHABLE();
3205 }
3206
2965 break; 3207 break;
2966 } 3208 }
2967 case OpCode::Type::PredicateSetPredicate: { 3209 case OpCode::Type::PredicateSetPredicate: {
2968 switch (opcode->GetId()) { 3210 switch (opcode->get().GetId()) {
2969 case OpCode::Id::PSETP: { 3211 case OpCode::Id::PSETP: {
2970 const std::string op_a = 3212 const std::string op_a =
2971 GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); 3213 GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0);
@@ -3011,7 +3253,8 @@ private:
3011 break; 3253 break;
3012 } 3254 }
3013 default: { 3255 default: {
3014 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName()); 3256 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}",
3257 opcode->get().GetName());
3015 UNREACHABLE(); 3258 UNREACHABLE();
3016 } 3259 }
3017 } 3260 }
@@ -3099,7 +3342,7 @@ private:
3099 instr.hset2.abs_a != 0, instr.hset2.negate_a != 0); 3342 instr.hset2.abs_a != 0, instr.hset2.negate_a != 0);
3100 3343
3101 const std::string op_b = [&]() { 3344 const std::string op_b = [&]() {
3102 switch (opcode->GetId()) { 3345 switch (opcode->get().GetId()) {
3103 case OpCode::Id::HSET2_R: 3346 case OpCode::Id::HSET2_R:
3104 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false), 3347 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3105 instr.hset2.type_b, instr.hset2.abs_b != 0, 3348 instr.hset2.type_b, instr.hset2.abs_b != 0,
@@ -3148,7 +3391,7 @@ private:
3148 const bool is_signed{instr.xmad.sign_a == 1}; 3391 const bool is_signed{instr.xmad.sign_a == 1};
3149 3392
3150 bool is_merge{}; 3393 bool is_merge{};
3151 switch (opcode->GetId()) { 3394 switch (opcode->get().GetId()) {
3152 case OpCode::Id::XMAD_CR: { 3395 case OpCode::Id::XMAD_CR: {
3153 is_merge = instr.xmad.merge_56; 3396 is_merge = instr.xmad.merge_56;
3154 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 3397 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
@@ -3177,7 +3420,7 @@ private:
3177 break; 3420 break;
3178 } 3421 }
3179 default: { 3422 default: {
3180 LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->GetName()); 3423 LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName());
3181 UNREACHABLE(); 3424 UNREACHABLE();
3182 } 3425 }
3183 } 3426 }
@@ -3226,15 +3469,25 @@ private:
3226 } 3469 }
3227 3470
3228 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); 3471 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
3472 if (instr.generates_cc) {
3473 LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code");
3474 UNREACHABLE();
3475 }
3229 break; 3476 break;
3230 } 3477 }
3231 default: { 3478 default: {
3232 switch (opcode->GetId()) { 3479 switch (opcode->get().GetId()) {
3233 case OpCode::Id::EXIT: { 3480 case OpCode::Id::EXIT: {
3234 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { 3481 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
3235 EmitFragmentOutputsWrite(); 3482 EmitFragmentOutputsWrite();
3236 } 3483 }
3237 3484
3485 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3486 if (cc != Tegra::Shader::ControlCode::T) {
3487 LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc));
3488 UNREACHABLE();
3489 }
3490
3238 switch (instr.flow.cond) { 3491 switch (instr.flow.cond) {
3239 case Tegra::Shader::FlowCondition::Always: 3492 case Tegra::Shader::FlowCondition::Always:
3240 shader.AddLine("return true;"); 3493 shader.AddLine("return true;");
@@ -3264,6 +3517,11 @@ private:
3264 3517
3265 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 3518 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
3266 // about unexecuted instructions that may follow this. 3519 // about unexecuted instructions that may follow this.
3520 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3521 if (cc != Tegra::Shader::ControlCode::T) {
3522 LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc));
3523 UNREACHABLE();
3524 }
3267 shader.AddLine("if (true) {"); 3525 shader.AddLine("if (true) {");
3268 ++shader.scope; 3526 ++shader.scope;
3269 shader.AddLine("discard;"); 3527 shader.AddLine("discard;");
@@ -3321,6 +3579,11 @@ private:
3321 case OpCode::Id::BRA: { 3579 case OpCode::Id::BRA: {
3322 ASSERT_MSG(instr.bra.constant_buffer == 0, 3580 ASSERT_MSG(instr.bra.constant_buffer == 0,
3323 "BRA with constant buffers are not implemented"); 3581 "BRA with constant buffers are not implemented");
3582 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3583 if (cc != Tegra::Shader::ControlCode::T) {
3584 LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc));
3585 UNREACHABLE();
3586 }
3324 const u32 target = offset + instr.bra.GetBranchTarget(); 3587 const u32 target = offset + instr.bra.GetBranchTarget();
3325 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); 3588 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
3326 break; 3589 break;
@@ -3361,13 +3624,21 @@ private:
3361 } 3624 }
3362 case OpCode::Id::SYNC: { 3625 case OpCode::Id::SYNC: {
3363 // The SYNC opcode jumps to the address previously set by the SSY opcode 3626 // The SYNC opcode jumps to the address previously set by the SSY opcode
3364 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3627 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3628 if (cc != Tegra::Shader::ControlCode::T) {
3629 LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc));
3630 UNREACHABLE();
3631 }
3365 EmitPopFromFlowStack(); 3632 EmitPopFromFlowStack();
3366 break; 3633 break;
3367 } 3634 }
3368 case OpCode::Id::BRK: { 3635 case OpCode::Id::BRK: {
3369 // The BRK opcode jumps to the address previously set by the PBK opcode 3636 // The BRK opcode jumps to the address previously set by the PBK opcode
3370 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3637 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3638 if (cc != Tegra::Shader::ControlCode::T) {
3639 LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc));
3640 UNREACHABLE();
3641 }
3371 EmitPopFromFlowStack(); 3642 EmitPopFromFlowStack();
3372 break; 3643 break;
3373 } 3644 }
@@ -3397,6 +3668,11 @@ private:
3397 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3668 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3398 instr.vmad.saturate == 1, 0, Register::Size::Word, 3669 instr.vmad.saturate == 1, 0, Register::Size::Word,
3399 instr.vmad.cc); 3670 instr.vmad.cc);
3671 if (instr.generates_cc) {
3672 LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code");
3673 UNREACHABLE();
3674 }
3675
3400 break; 3676 break;
3401 } 3677 }
3402 case OpCode::Id::VSETP: { 3678 case OpCode::Id::VSETP: {
@@ -3424,7 +3700,7 @@ private:
3424 break; 3700 break;
3425 } 3701 }
3426 default: { 3702 default: {
3427 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3703 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName());
3428 UNREACHABLE(); 3704 UNREACHABLE();
3429 } 3705 }
3430 } 3706 }
@@ -3550,6 +3826,7 @@ private:
3550 const u32 main_offset; 3826 const u32 main_offset;
3551 Maxwell3D::Regs::ShaderStage stage; 3827 Maxwell3D::Regs::ShaderStage stage;
3552 const std::string& suffix; 3828 const std::string& suffix;
3829 u64 local_memory_size;
3553 3830
3554 ShaderWriter shader; 3831 ShaderWriter shader;
3555 ShaderWriter declarations; 3832 ShaderWriter declarations;
@@ -3564,9 +3841,9 @@ std::string GetCommonDeclarations() {
3564 RasterizerOpenGL::MaxConstbufferSize / sizeof(GLvec4)); 3841 RasterizerOpenGL::MaxConstbufferSize / sizeof(GLvec4));
3565} 3842}
3566 3843
3567boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, 3844std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
3568 Maxwell3D::Regs::ShaderStage stage, 3845 Maxwell3D::Regs::ShaderStage stage,
3569 const std::string& suffix) { 3846 const std::string& suffix) {
3570 try { 3847 try {
3571 const auto subroutines = 3848 const auto subroutines =
3572 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); 3849 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
@@ -3575,7 +3852,7 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
3575 } catch (const DecompileFail& exception) { 3852 } catch (const DecompileFail& exception) {
3576 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); 3853 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());
3577 } 3854 }
3578 return boost::none; 3855 return {};
3579} 3856}
3580 3857
3581} // namespace OpenGL::GLShader::Decompiler 3858} // namespace OpenGL::GLShader::Decompiler
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index b20cc4bfa..d01a4a7ee 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -6,8 +6,8 @@
6 6
7#include <array> 7#include <array>
8#include <functional> 8#include <functional>
9#include <optional>
9#include <string> 10#include <string>
10#include <boost/optional.hpp>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/renderer_opengl/gl_shader_gen.h" 13#include "video_core/renderer_opengl/gl_shader_gen.h"
@@ -18,8 +18,8 @@ using Tegra::Engines::Maxwell3D;
18 18
19std::string GetCommonDeclarations(); 19std::string GetCommonDeclarations();
20 20
21boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, 21std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
22 Maxwell3D::Regs::ShaderStage stage, 22 Maxwell3D::Regs::ShaderStage stage,
23 const std::string& suffix); 23 const std::string& suffix);
24 24
25} // namespace OpenGL::GLShader::Decompiler 25} // namespace OpenGL::GLShader::Decompiler
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index e883ffb1d..eea090e52 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -19,9 +19,6 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
19 out += Decompiler::GetCommonDeclarations(); 19 out += Decompiler::GetCommonDeclarations();
20 20
21 out += R"( 21 out += R"(
22out gl_PerVertex {
23 vec4 gl_Position;
24};
25 22
26layout (location = 0) out vec4 position; 23layout (location = 0) out vec4 position;
27 24
@@ -40,7 +37,7 @@ layout(std140) uniform vs_config {
40 ProgramResult program = 37 ProgramResult program =
41 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, 38 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
42 Maxwell3D::Regs::ShaderStage::Vertex, "vertex") 39 Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
43 .get_value_or({}); 40 .value_or(ProgramResult());
44 41
45 out += program.first; 42 out += program.first;
46 43
@@ -48,7 +45,7 @@ layout(std140) uniform vs_config {
48 ProgramResult program_b = 45 ProgramResult program_b =
49 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET, 46 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
50 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b") 47 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
51 .get_value_or({}); 48 .value_or(ProgramResult());
52 out += program_b.first; 49 out += program_b.first;
53 } 50 }
54 51
@@ -85,15 +82,15 @@ void main() {
85} 82}
86 83
87ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { 84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
88 std::string out = "#version 430 core\n"; 85 // Version is intentionally skipped in shader generation, it's added by the lazy compilation.
89 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 86 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
90 out += Decompiler::GetCommonDeclarations(); 87 out += Decompiler::GetCommonDeclarations();
91 out += "bool exec_geometry();\n"; 88 out += "bool exec_geometry();\n";
92 89
93 ProgramResult program = 90 ProgramResult program =
94 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, 91 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
95 Maxwell3D::Regs::ShaderStage::Geometry, "geometry") 92 Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
96 .get_value_or({}); 93 .value_or(ProgramResult());
97 out += R"( 94 out += R"(
98out gl_PerVertex { 95out gl_PerVertex {
99 vec4 gl_Position; 96 vec4 gl_Position;
@@ -127,7 +124,7 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
127 ProgramResult program = 124 ProgramResult program =
128 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, 125 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
129 Maxwell3D::Regs::ShaderStage::Fragment, "fragment") 126 Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
130 .get_value_or({}); 127 .value_or(ProgramResult());
131 out += R"( 128 out += R"(
132layout(location = 0) out vec4 FragColor0; 129layout(location = 0) out vec4 FragColor0;
133layout(location = 1) out vec4 FragColor1; 130layout(location = 1) out vec4 FragColor1;
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 1fe26a2a9..98622a058 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -11,9 +11,10 @@
11namespace OpenGL { 11namespace OpenGL {
12 12
13OpenGLState OpenGLState::cur_state; 13OpenGLState OpenGLState::cur_state;
14 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 16 // These all match default OpenGL values
17 framebuffer_srgb.enabled = false;
17 cull.enabled = false; 18 cull.enabled = false;
18 cull.mode = GL_BACK; 19 cull.mode = GL_BACK;
19 cull.front_face = GL_CCW; 20 cull.front_face = GL_CCW;
@@ -22,11 +23,14 @@ OpenGLState::OpenGLState() {
22 depth.test_func = GL_LESS; 23 depth.test_func = GL_LESS;
23 depth.write_mask = GL_TRUE; 24 depth.write_mask = GL_TRUE;
24 25
25 color_mask.red_enabled = GL_TRUE; 26 primitive_restart.enabled = false;
26 color_mask.green_enabled = GL_TRUE; 27 primitive_restart.index = 0;
27 color_mask.blue_enabled = GL_TRUE; 28 for (auto& item : color_mask) {
28 color_mask.alpha_enabled = GL_TRUE; 29 item.red_enabled = GL_TRUE;
29 30 item.green_enabled = GL_TRUE;
31 item.blue_enabled = GL_TRUE;
32 item.alpha_enabled = GL_TRUE;
33 }
30 stencil.test_enabled = false; 34 stencil.test_enabled = false;
31 auto reset_stencil = [](auto& config) { 35 auto reset_stencil = [](auto& config) {
32 config.test_func = GL_ALWAYS; 36 config.test_func = GL_ALWAYS;
@@ -39,19 +43,33 @@ OpenGLState::OpenGLState() {
39 }; 43 };
40 reset_stencil(stencil.front); 44 reset_stencil(stencil.front);
41 reset_stencil(stencil.back); 45 reset_stencil(stencil.back);
42 46 for (auto& item : viewports) {
43 blend.enabled = true; 47 item.x = 0;
44 blend.rgb_equation = GL_FUNC_ADD; 48 item.y = 0;
45 blend.a_equation = GL_FUNC_ADD; 49 item.width = 0;
46 blend.src_rgb_func = GL_ONE; 50 item.height = 0;
47 blend.dst_rgb_func = GL_ZERO; 51 item.depth_range_near = 0.0f;
48 blend.src_a_func = GL_ONE; 52 item.depth_range_far = 1.0f;
49 blend.dst_a_func = GL_ZERO; 53 }
50 blend.color.red = 0.0f; 54 scissor.enabled = false;
51 blend.color.green = 0.0f; 55 scissor.x = 0;
52 blend.color.blue = 0.0f; 56 scissor.y = 0;
53 blend.color.alpha = 0.0f; 57 scissor.width = 0;
54 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;
55 logic_op.enabled = false; 73 logic_op.enabled = false;
56 logic_op.operation = GL_COPY; 74 logic_op.operation = GL_COPY;
57 75
@@ -67,138 +85,309 @@ OpenGLState::OpenGLState() {
67 draw.shader_program = 0; 85 draw.shader_program = 0;
68 draw.program_pipeline = 0; 86 draw.program_pipeline = 0;
69 87
70 scissor.enabled = false;
71 scissor.x = 0;
72 scissor.y = 0;
73 scissor.width = 0;
74 scissor.height = 0;
75
76 viewport.x = 0;
77 viewport.y = 0;
78 viewport.width = 0;
79 viewport.height = 0;
80
81 clip_distance = {}; 88 clip_distance = {};
82 89
83 point.size = 1; 90 point.size = 1;
84} 91}
85 92
86void OpenGLState::Apply() const { 93void OpenGLState::ApplyDefaultState() {
94 glDisable(GL_FRAMEBUFFER_SRGB);
95 glDisable(GL_CULL_FACE);
96 glDisable(GL_DEPTH_TEST);
97 glDisable(GL_PRIMITIVE_RESTART);
98 glDisable(GL_STENCIL_TEST);
99 glEnable(GL_BLEND);
100 glDisable(GL_COLOR_LOGIC_OP);
101 glDisable(GL_SCISSOR_TEST);
102}
103
104void OpenGLState::ApplySRgb() const {
105 // sRGB
106 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
107 if (framebuffer_srgb.enabled) {
108 // Track if sRGB is used
109 s_rgb_used = true;
110 glEnable(GL_FRAMEBUFFER_SRGB);
111 } else {
112 glDisable(GL_FRAMEBUFFER_SRGB);
113 }
114 }
115}
116
117void OpenGLState::ApplyCulling() const {
87 // Culling 118 // Culling
88 if (cull.enabled != cur_state.cull.enabled) { 119 const bool cull_changed = cull.enabled != cur_state.cull.enabled;
120 if (cull_changed) {
89 if (cull.enabled) { 121 if (cull.enabled) {
90 glEnable(GL_CULL_FACE); 122 glEnable(GL_CULL_FACE);
91 } else { 123 } else {
92 glDisable(GL_CULL_FACE); 124 glDisable(GL_CULL_FACE);
93 } 125 }
94 } 126 }
127 if (cull.enabled) {
128 if (cull_changed || cull.mode != cur_state.cull.mode) {
129 glCullFace(cull.mode);
130 }
95 131
96 if (cull.mode != cur_state.cull.mode) { 132 if (cull_changed || cull.front_face != cur_state.cull.front_face) {
97 glCullFace(cull.mode); 133 glFrontFace(cull.front_face);
134 }
98 } 135 }
136}
99 137
100 if (cull.front_face != cur_state.cull.front_face) { 138void OpenGLState::ApplyColorMask() const {
101 glFrontFace(cull.front_face); 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 }
102 } 161 }
162}
103 163
164void OpenGLState::ApplyDepth() const {
104 // Depth test 165 // Depth test
105 if (depth.test_enabled != cur_state.depth.test_enabled) { 166 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
167 if (depth_test_changed) {
106 if (depth.test_enabled) { 168 if (depth.test_enabled) {
107 glEnable(GL_DEPTH_TEST); 169 glEnable(GL_DEPTH_TEST);
108 } else { 170 } else {
109 glDisable(GL_DEPTH_TEST); 171 glDisable(GL_DEPTH_TEST);
110 } 172 }
111 } 173 }
112 174 if (depth.test_enabled &&
113 if (depth.test_func != cur_state.depth.test_func) { 175 (depth_test_changed || depth.test_func != cur_state.depth.test_func)) {
114 glDepthFunc(depth.test_func); 176 glDepthFunc(depth.test_func);
115 } 177 }
116
117 // Depth mask 178 // Depth mask
118 if (depth.write_mask != cur_state.depth.write_mask) { 179 if (depth.write_mask != cur_state.depth.write_mask) {
119 glDepthMask(depth.write_mask); 180 glDepthMask(depth.write_mask);
120 } 181 }
182}
121 183
122 // Color mask 184void OpenGLState::ApplyPrimitiveRestart() const {
123 if (color_mask.red_enabled != cur_state.color_mask.red_enabled || 185 const bool primitive_restart_changed =
124 color_mask.green_enabled != cur_state.color_mask.green_enabled || 186 primitive_restart.enabled != cur_state.primitive_restart.enabled;
125 color_mask.blue_enabled != cur_state.color_mask.blue_enabled || 187 if (primitive_restart_changed) {
126 color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) { 188 if (primitive_restart.enabled) {
127 glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled, 189 glEnable(GL_PRIMITIVE_RESTART);
128 color_mask.alpha_enabled); 190 } else {
191 glDisable(GL_PRIMITIVE_RESTART);
192 }
193 }
194 if (primitive_restart_changed ||
195 (primitive_restart.enabled &&
196 primitive_restart.index != cur_state.primitive_restart.index)) {
197 glPrimitiveRestartIndex(primitive_restart.index);
129 } 198 }
199}
130 200
131 // Stencil test 201void OpenGLState::ApplyStencilTest() const {
132 if (stencil.test_enabled != cur_state.stencil.test_enabled) { 202 const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled;
203 if (stencil_test_changed) {
133 if (stencil.test_enabled) { 204 if (stencil.test_enabled) {
134 glEnable(GL_STENCIL_TEST); 205 glEnable(GL_STENCIL_TEST);
135 } else { 206 } else {
136 glDisable(GL_STENCIL_TEST); 207 glDisable(GL_STENCIL_TEST);
137 } 208 }
138 } 209 }
139 auto config_stencil = [](GLenum face, const auto& config, const auto& prev_config) { 210 if (stencil.test_enabled) {
140 if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || 211 auto config_stencil = [stencil_test_changed](GLenum face, const auto& config,
141 config.test_mask != prev_config.test_mask) { 212 const auto& prev_config) {
142 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); 213 if (stencil_test_changed || config.test_func != prev_config.test_func ||
214 config.test_ref != prev_config.test_ref ||
215 config.test_mask != prev_config.test_mask) {
216 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
217 }
218 if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail ||
219 config.action_depth_pass != prev_config.action_depth_pass ||
220 config.action_stencil_fail != prev_config.action_stencil_fail) {
221 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
222 config.action_depth_pass);
223 }
224 if (config.write_mask != prev_config.write_mask) {
225 glStencilMaskSeparate(face, config.write_mask);
226 }
227 };
228 config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front);
229 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
230 }
231}
232
233void OpenGLState::ApplyScissor() const {
234 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
235 if (scissor_changed) {
236 if (scissor.enabled) {
237 glEnable(GL_SCISSOR_TEST);
238 } else {
239 glDisable(GL_SCISSOR_TEST);
143 } 240 }
144 if (config.action_depth_fail != prev_config.action_depth_fail || 241 }
145 config.action_depth_pass != prev_config.action_depth_pass || 242 if (scissor.enabled &&
146 config.action_stencil_fail != prev_config.action_stencil_fail) { 243 (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
147 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, 244 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
148 config.action_depth_pass); 245 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
246 }
247}
248
249void OpenGLState::ApplyViewport() const {
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 }
149 } 263 }
150 if (config.write_mask != prev_config.write_mask) { 264 } else {
151 glStencilMaskSeparate(face, config.write_mask); 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));
152 } 271 }
153 }; 272 if (updated.depth_range_near != current.depth_range_near ||
154 config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); 273 updated.depth_range_far != current.depth_range_far) {
155 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); 274 glDepthRange(updated.depth_range_near, updated.depth_range_far);
275 }
276 }
277}
156 278
157 // Blending 279void OpenGLState::ApplyGlobalBlending() const {
158 if (blend.enabled != cur_state.blend.enabled) { 280 const Blend& current = cur_state.blend[0];
159 if (blend.enabled) { 281 const Blend& updated = blend[0];
160 ASSERT(!logic_op.enabled); 282 const bool blend_changed = updated.enabled != current.enabled;
283 if (blend_changed) {
284 if (updated.enabled) {
161 glEnable(GL_BLEND); 285 glEnable(GL_BLEND);
162 } else { 286 } else {
163 glDisable(GL_BLEND); 287 glDisable(GL_BLEND);
164 } 288 }
165 } 289 }
290 if (!updated.enabled) {
291 return;
292 }
293 if (updated.separate_alpha) {
294 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
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);
299 }
300
301 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
302 updated.a_equation != current.a_equation) {
303 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
304 }
305 } else {
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);
309 }
310
311 if (blend_changed || updated.rgb_equation != current.rgb_equation) {
312 glBlendEquation(updated.rgb_equation);
313 }
314 }
315}
166 316
167 if (blend.color.red != cur_state.blend.color.red || 317void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
168 blend.color.green != cur_state.blend.color.green || 318 const Blend& updated = blend[target];
169 blend.color.blue != cur_state.blend.color.blue || 319 const Blend& current = cur_state.blend[target];
170 blend.color.alpha != cur_state.blend.color.alpha) { 320 const bool blend_changed = updated.enabled != current.enabled || force;
171 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); 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;
172 } 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 }
173 350
174 if (blend.src_rgb_func != cur_state.blend.src_rgb_func || 351 if (blend_changed || updated.rgb_equation != current.rgb_equation) {
175 blend.dst_rgb_func != cur_state.blend.dst_rgb_func || 352 glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation);
176 blend.src_a_func != cur_state.blend.src_a_func || 353 }
177 blend.dst_a_func != cur_state.blend.dst_a_func) {
178 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
179 blend.dst_a_func);
180 } 354 }
355}
181 356
182 if (blend.rgb_equation != cur_state.blend.rgb_equation || 357void OpenGLState::ApplyBlending() const {
183 blend.a_equation != cur_state.blend.a_equation) { 358 if (independant_blend.enabled) {
184 glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); 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);
185 } 371 }
372}
186 373
187 // Logic Operation 374void OpenGLState::ApplyLogicOp() const {
188 if (logic_op.enabled != cur_state.logic_op.enabled) { 375 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled;
376 if (logic_op_changed) {
189 if (logic_op.enabled) { 377 if (logic_op.enabled) {
190 ASSERT(!blend.enabled);
191 glEnable(GL_COLOR_LOGIC_OP); 378 glEnable(GL_COLOR_LOGIC_OP);
192 } else { 379 } else {
193 glDisable(GL_COLOR_LOGIC_OP); 380 glDisable(GL_COLOR_LOGIC_OP);
194 } 381 }
195 } 382 }
196 383
197 if (logic_op.operation != cur_state.logic_op.operation) { 384 if (logic_op.enabled &&
385 (logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) {
198 glLogicOp(logic_op.operation); 386 glLogicOp(logic_op.operation);
199 } 387 }
388}
200 389
201 // Textures 390void OpenGLState::ApplyTextures() const {
202 for (std::size_t i = 0; i < std::size(texture_units); ++i) { 391 for (std::size_t i = 0; i < std::size(texture_units); ++i) {
203 const auto& texture_unit = texture_units[i]; 392 const auto& texture_unit = texture_units[i];
204 const auto& cur_state_texture_unit = cur_state.texture_units[i]; 393 const auto& cur_state_texture_unit = cur_state.texture_units[i];
@@ -217,28 +406,29 @@ void OpenGLState::Apply() const {
217 glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); 406 glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
218 } 407 }
219 } 408 }
409}
220 410
221 // Samplers 411void OpenGLState::ApplySamplers() const {
222 { 412 bool has_delta{};
223 bool has_delta{}; 413 std::size_t first{}, last{};
224 std::size_t first{}, last{}; 414 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers;
225 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; 415 for (std::size_t i = 0; i < std::size(samplers); ++i) {
226 for (std::size_t i = 0; i < std::size(samplers); ++i) { 416 samplers[i] = texture_units[i].sampler;
227 samplers[i] = texture_units[i].sampler; 417 if (samplers[i] != cur_state.texture_units[i].sampler) {
228 if (samplers[i] != cur_state.texture_units[i].sampler) { 418 if (!has_delta) {
229 if (!has_delta) { 419 first = i;
230 first = i; 420 has_delta = true;
231 has_delta = true;
232 }
233 last = i;
234 } 421 }
422 last = i;
235 } 423 }
236 if (has_delta) {
237 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
238 samplers.data());
239 }
240 } 424 }
425 if (has_delta) {
426 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
427 samplers.data());
428 }
429}
241 430
431void OpenGLState::ApplyFramebufferState() const {
242 // Framebuffer 432 // Framebuffer
243 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 433 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
244 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 434 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -246,7 +436,9 @@ void OpenGLState::Apply() const {
246 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) { 436 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
247 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); 437 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
248 } 438 }
439}
249 440
441void OpenGLState::ApplyVertexBufferState() const {
250 // Vertex array 442 // Vertex array
251 if (draw.vertex_array != cur_state.draw.vertex_array) { 443 if (draw.vertex_array != cur_state.draw.vertex_array) {
252 glBindVertexArray(draw.vertex_array); 444 glBindVertexArray(draw.vertex_array);
@@ -256,7 +448,11 @@ void OpenGLState::Apply() const {
256 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) { 448 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
257 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer); 449 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
258 } 450 }
451}
259 452
453void OpenGLState::Apply() const {
454 ApplyFramebufferState();
455 ApplyVertexBufferState();
260 // Uniform buffer 456 // Uniform buffer
261 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) { 457 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
262 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer); 458 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
@@ -271,27 +467,6 @@ void OpenGLState::Apply() const {
271 if (draw.program_pipeline != cur_state.draw.program_pipeline) { 467 if (draw.program_pipeline != cur_state.draw.program_pipeline) {
272 glBindProgramPipeline(draw.program_pipeline); 468 glBindProgramPipeline(draw.program_pipeline);
273 } 469 }
274
275 // Scissor test
276 if (scissor.enabled != cur_state.scissor.enabled) {
277 if (scissor.enabled) {
278 glEnable(GL_SCISSOR_TEST);
279 } else {
280 glDisable(GL_SCISSOR_TEST);
281 }
282 }
283
284 if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
285 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
286 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
287 }
288
289 if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
290 viewport.width != cur_state.viewport.width ||
291 viewport.height != cur_state.viewport.height) {
292 glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
293 }
294
295 // Clip distance 470 // Clip distance
296 for (std::size_t i = 0; i < clip_distance.size(); ++i) { 471 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
297 if (clip_distance[i] != cur_state.clip_distance[i]) { 472 if (clip_distance[i] != cur_state.clip_distance[i]) {
@@ -302,12 +477,22 @@ void OpenGLState::Apply() const {
302 } 477 }
303 } 478 }
304 } 479 }
305
306 // Point 480 // Point
307 if (point.size != cur_state.point.size) { 481 if (point.size != cur_state.point.size) {
308 glPointSize(point.size); 482 glPointSize(point.size);
309 } 483 }
310 484 ApplyColorMask();
485 ApplyViewport();
486 ApplyScissor();
487 ApplyStencilTest();
488 ApplySRgb();
489 ApplyCulling();
490 ApplyDepth();
491 ApplyPrimitiveRestart();
492 ApplyBlending();
493 ApplyLogicOp();
494 ApplyTextures();
495 ApplySamplers();
311 cur_state = *this; 496 cur_state = *this;
312} 497}
313 498
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index dc21a2ee3..e5d1baae6 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -36,6 +36,10 @@ constexpr TextureUnit ProcTexDiffLUT{9};
36class OpenGLState { 36class OpenGLState {
37public: 37public:
38 struct { 38 struct {
39 bool enabled; // GL_FRAMEBUFFER_SRGB
40 } framebuffer_srgb;
41
42 struct {
39 bool enabled; // GL_CULL_FACE 43 bool enabled; // GL_CULL_FACE
40 GLenum mode; // GL_CULL_FACE_MODE 44 GLenum mode; // GL_CULL_FACE_MODE
41 GLenum front_face; // GL_FRONT_FACE 45 GLenum front_face; // GL_FRONT_FACE
@@ -48,12 +52,18 @@ public:
48 } depth; 52 } depth;
49 53
50 struct { 54 struct {
55 bool enabled;
56 GLuint index;
57 } primitive_restart; // GL_PRIMITIVE_RESTART
58
59 struct ColorMask {
51 GLboolean red_enabled; 60 GLboolean red_enabled;
52 GLboolean green_enabled; 61 GLboolean green_enabled;
53 GLboolean blue_enabled; 62 GLboolean blue_enabled;
54 GLboolean alpha_enabled; 63 GLboolean alpha_enabled;
55 } color_mask; // GL_COLOR_WRITEMASK 64 };
56 65 std::array<ColorMask, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets>
66 color_mask; // GL_COLOR_WRITEMASK
57 struct { 67 struct {
58 bool test_enabled; // GL_STENCIL_TEST 68 bool test_enabled; // GL_STENCIL_TEST
59 struct { 69 struct {
@@ -67,22 +77,28 @@ public:
67 } front, back; 77 } front, back;
68 } stencil; 78 } stencil;
69 79
70 struct { 80 struct Blend {
71 bool enabled; // GL_BLEND 81 bool enabled; // GL_BLEND
82 bool separate_alpha; // Independent blend enabled
72 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB 83 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
73 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA 84 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
74 GLenum src_rgb_func; // GL_BLEND_SRC_RGB 85 GLenum src_rgb_func; // GL_BLEND_SRC_RGB
75 GLenum dst_rgb_func; // GL_BLEND_DST_RGB 86 GLenum dst_rgb_func; // GL_BLEND_DST_RGB
76 GLenum src_a_func; // GL_BLEND_SRC_ALPHA 87 GLenum src_a_func; // GL_BLEND_SRC_ALPHA
77 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;
78 91
79 struct { 92 struct {
80 GLclampf red; 93 bool enabled;
81 GLclampf green; 94 } independant_blend;
82 GLclampf blue; 95
83 GLclampf alpha; 96 struct {
84 } color; // GL_BLEND_COLOR 97 GLclampf red;
85 } blend; 98 GLclampf green;
99 GLclampf blue;
100 GLclampf alpha;
101 } blend_color; // GL_BLEND_COLOR
86 102
87 struct { 103 struct {
88 bool enabled; // GL_LOGIC_OP_MODE 104 bool enabled; // GL_LOGIC_OP_MODE
@@ -127,6 +143,16 @@ public:
127 GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING 143 GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
128 } draw; 144 } draw;
129 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
130 struct { 156 struct {
131 bool enabled; // GL_SCISSOR_TEST 157 bool enabled; // GL_SCISSOR_TEST
132 GLint x; 158 GLint x;
@@ -136,13 +162,6 @@ public:
136 } scissor; 162 } scissor;
137 163
138 struct { 164 struct {
139 GLint x;
140 GLint y;
141 GLsizei width;
142 GLsizei height;
143 } viewport;
144
145 struct {
146 float size; // GL_POINT_SIZE 165 float size; // GL_POINT_SIZE
147 } point; 166 } point;
148 167
@@ -154,10 +173,20 @@ public:
154 static OpenGLState GetCurState() { 173 static OpenGLState GetCurState() {
155 return cur_state; 174 return cur_state;
156 } 175 }
157 176 static bool GetsRGBUsed() {
177 return s_rgb_used;
178 }
179 static void ClearsRGBUsed() {
180 s_rgb_used = false;
181 }
158 /// Apply this state as the current OpenGL state 182 /// Apply this state as the current OpenGL state
159 void Apply() const; 183 void Apply() const;
160 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;
188 /// Set the initial OpenGL state
189 static void ApplyDefaultState();
161 /// Resets any references to the given resource 190 /// Resets any references to the given resource
162 OpenGLState& UnbindTexture(GLuint handle); 191 OpenGLState& UnbindTexture(GLuint handle);
163 OpenGLState& ResetSampler(GLuint handle); 192 OpenGLState& ResetSampler(GLuint handle);
@@ -169,6 +198,23 @@ public:
169 198
170private: 199private:
171 static OpenGLState cur_state; 200 static OpenGLState cur_state;
201 // Workaround for sRGB problems caused by
202 // QT not supporting srgb output
203 static bool s_rgb_used;
204 void ApplySRgb() const;
205 void ApplyCulling() const;
206 void ApplyColorMask() const;
207 void ApplyDepth() const;
208 void ApplyPrimitiveRestart() const;
209 void ApplyStencilTest() const;
210 void ApplyViewport() const;
211 void ApplyTargetBlending(std::size_t target, bool force) const;
212 void ApplyGlobalBlending() const;
213 void ApplyBlending() const;
214 void ApplyLogicOp() const;
215 void ApplyTextures() const;
216 void ApplySamplers() const;
217 void ApplyScissor() const;
172}; 218};
173 219
174} // 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 0f6dcab2b..3ce2cc6d2 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -135,17 +135,32 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
135 return {}; 135 return {};
136} 136}
137 137
138inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { 138inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
139 Tegra::Texture::TextureMipmapFilter mip_filter_mode) {
139 switch (filter_mode) { 140 switch (filter_mode) {
140 case Tegra::Texture::TextureFilter::Linear: 141 case Tegra::Texture::TextureFilter::Linear: {
141 return GL_LINEAR; 142 switch (mip_filter_mode) {
142 case Tegra::Texture::TextureFilter::Nearest: 143 case Tegra::Texture::TextureMipmapFilter::None:
143 return GL_NEAREST; 144 return GL_LINEAR;
145 case Tegra::Texture::TextureMipmapFilter::Nearest:
146 return GL_NEAREST_MIPMAP_LINEAR;
147 case Tegra::Texture::TextureMipmapFilter::Linear:
148 return GL_LINEAR_MIPMAP_LINEAR;
149 }
144 } 150 }
145 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", 151 case Tegra::Texture::TextureFilter::Nearest: {
146 static_cast<u32>(filter_mode)); 152 switch (mip_filter_mode) {
147 UNREACHABLE(); 153 case Tegra::Texture::TextureMipmapFilter::None:
148 return {}; 154 return GL_NEAREST;
155 case Tegra::Texture::TextureMipmapFilter::Nearest:
156 return GL_NEAREST_MIPMAP_NEAREST;
157 case Tegra::Texture::TextureMipmapFilter::Linear:
158 return GL_LINEAR_MIPMAP_NEAREST;
159 }
160 }
161 }
162 LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode));
163 return GL_LINEAR;
149} 164}
150 165
151inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { 166inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
@@ -166,9 +181,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
166 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
167 return GL_MIRROR_CLAMP_TO_EDGE; 182 return GL_MIRROR_CLAMP_TO_EDGE;
168 } 183 }
169 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));
170 UNREACHABLE(); 185 return GL_REPEAT;
171 return {};
172} 186}
173 187
174inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { 188inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
@@ -190,10 +204,9 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
190 case Tegra::Texture::DepthCompareFunc::Always: 204 case Tegra::Texture::DepthCompareFunc::Always:
191 return GL_ALWAYS; 205 return GL_ALWAYS;
192 } 206 }
193 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}", 207 LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}",
194 static_cast<u32>(func)); 208 static_cast<u32>(func));
195 UNREACHABLE(); 209 return GL_GREATER;
196 return {};
197} 210}
198 211
199inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 212inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
@@ -209,9 +222,8 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
209 case Maxwell::Blend::Equation::Max: 222 case Maxwell::Blend::Equation::Max:
210 return GL_MAX; 223 return GL_MAX;
211 } 224 }
212 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); 225 LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
213 UNREACHABLE(); 226 return GL_FUNC_ADD;
214 return {};
215} 227}
216 228
217inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { 229inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
@@ -274,9 +286,8 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
274 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 286 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
275 return GL_ONE_MINUS_CONSTANT_ALPHA; 287 return GL_ONE_MINUS_CONSTANT_ALPHA;
276 } 288 }
277 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); 289 LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
278 UNREACHABLE(); 290 return GL_ZERO;
279 return {};
280} 291}
281 292
282inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { 293inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
@@ -295,9 +306,8 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
295 case Tegra::Texture::SwizzleSource::OneFloat: 306 case Tegra::Texture::SwizzleSource::OneFloat:
296 return GL_ONE; 307 return GL_ONE;
297 } 308 }
298 LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); 309 LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
299 UNREACHABLE(); 310 return GL_ZERO;
300 return {};
301} 311}
302 312
303inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { 313inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
@@ -327,33 +337,39 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
327 case Maxwell::ComparisonOp::AlwaysOld: 337 case Maxwell::ComparisonOp::AlwaysOld:
328 return GL_ALWAYS; 338 return GL_ALWAYS;
329 } 339 }
330 LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); 340 LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
331 UNREACHABLE(); 341 return GL_ALWAYS;
332 return {};
333} 342}
334 343
335inline GLenum StencilOp(Maxwell::StencilOp stencil) { 344inline GLenum StencilOp(Maxwell::StencilOp stencil) {
336 switch (stencil) { 345 switch (stencil) {
337 case Maxwell::StencilOp::Keep: 346 case Maxwell::StencilOp::Keep:
347 case Maxwell::StencilOp::KeepOGL:
338 return GL_KEEP; 348 return GL_KEEP;
339 case Maxwell::StencilOp::Zero: 349 case Maxwell::StencilOp::Zero:
350 case Maxwell::StencilOp::ZeroOGL:
340 return GL_ZERO; 351 return GL_ZERO;
341 case Maxwell::StencilOp::Replace: 352 case Maxwell::StencilOp::Replace:
353 case Maxwell::StencilOp::ReplaceOGL:
342 return GL_REPLACE; 354 return GL_REPLACE;
343 case Maxwell::StencilOp::Incr: 355 case Maxwell::StencilOp::Incr:
356 case Maxwell::StencilOp::IncrOGL:
344 return GL_INCR; 357 return GL_INCR;
345 case Maxwell::StencilOp::Decr: 358 case Maxwell::StencilOp::Decr:
359 case Maxwell::StencilOp::DecrOGL:
346 return GL_DECR; 360 return GL_DECR;
347 case Maxwell::StencilOp::Invert: 361 case Maxwell::StencilOp::Invert:
362 case Maxwell::StencilOp::InvertOGL:
348 return GL_INVERT; 363 return GL_INVERT;
349 case Maxwell::StencilOp::IncrWrap: 364 case Maxwell::StencilOp::IncrWrap:
365 case Maxwell::StencilOp::IncrWrapOGL:
350 return GL_INCR_WRAP; 366 return GL_INCR_WRAP;
351 case Maxwell::StencilOp::DecrWrap: 367 case Maxwell::StencilOp::DecrWrap:
368 case Maxwell::StencilOp::DecrWrapOGL:
352 return GL_DECR_WRAP; 369 return GL_DECR_WRAP;
353 } 370 }
354 LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); 371 LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
355 UNREACHABLE(); 372 return GL_KEEP;
356 return {};
357} 373}
358 374
359inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { 375inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
@@ -363,9 +379,8 @@ inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
363 case Maxwell::Cull::FrontFace::CounterClockWise: 379 case Maxwell::Cull::FrontFace::CounterClockWise:
364 return GL_CCW; 380 return GL_CCW;
365 } 381 }
366 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));
367 UNREACHABLE(); 383 return GL_CCW;
368 return {};
369} 384}
370 385
371inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { 386inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
@@ -377,9 +392,8 @@ inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
377 case Maxwell::Cull::CullFace::FrontAndBack: 392 case Maxwell::Cull::CullFace::FrontAndBack:
378 return GL_FRONT_AND_BACK; 393 return GL_FRONT_AND_BACK;
379 } 394 }
380 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));
381 UNREACHABLE(); 396 return GL_BACK;
382 return {};
383} 397}
384 398
385inline GLenum LogicOp(Maxwell::LogicOperation operation) { 399inline GLenum LogicOp(Maxwell::LogicOperation operation) {
@@ -417,9 +431,8 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
417 case Maxwell::LogicOperation::Set: 431 case Maxwell::LogicOperation::Set:
418 return GL_SET; 432 return GL_SET;
419 } 433 }
420 LOG_CRITICAL(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); 434 LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
421 UNREACHABLE(); 435 return GL_COPY;
422 return {};
423} 436}
424 437
425} // namespace MaxwellToGL 438} // namespace MaxwellToGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 96d916b07..ea38da932 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -115,7 +115,8 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window)
115RendererOpenGL::~RendererOpenGL() = default; 115RendererOpenGL::~RendererOpenGL() = default;
116 116
117/// Swap buffers (render frame) 117/// Swap buffers (render frame)
118void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { 118void RendererOpenGL::SwapBuffers(
119 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
119 ScopeAcquireGLContext acquire_context{render_window}; 120 ScopeAcquireGLContext acquire_context{render_window};
120 121
121 Core::System::GetInstance().GetPerfStats().EndSystemFrame(); 122 Core::System::GetInstance().GetPerfStats().EndSystemFrame();
@@ -124,11 +125,11 @@ void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&
124 OpenGLState prev_state = OpenGLState::GetCurState(); 125 OpenGLState prev_state = OpenGLState::GetCurState();
125 state.Apply(); 126 state.Apply();
126 127
127 if (framebuffer != boost::none) { 128 if (framebuffer) {
128 // If framebuffer is provided, reload it from memory to a texture 129 // If framebuffer is provided, reload it from memory to a texture
129 if (screen_info.texture.width != (GLsizei)framebuffer->width || 130 if (screen_info.texture.width != (GLsizei)framebuffer->get().width ||
130 screen_info.texture.height != (GLsizei)framebuffer->height || 131 screen_info.texture.height != (GLsizei)framebuffer->get().height ||
131 screen_info.texture.pixel_format != framebuffer->pixel_format) { 132 screen_info.texture.pixel_format != framebuffer->get().pixel_format) {
132 // Reallocate texture if the framebuffer size has changed. 133 // Reallocate texture if the framebuffer size has changed.
133 // This is expected to not happen very often and hence should not be a 134 // This is expected to not happen very often and hence should not be a
134 // performance problem. 135 // performance problem.
@@ -283,7 +284,8 @@ void RendererOpenGL::CreateRasterizer() {
283 if (rasterizer) { 284 if (rasterizer) {
284 return; 285 return;
285 } 286 }
286 287 // Initialize sRGB Usage
288 OpenGLState::ClearsRGBUsed();
287 rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info); 289 rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info);
288} 290}
289 291
@@ -356,13 +358,20 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
356 358
357 state.texture_units[0].texture = screen_info.display_texture; 359 state.texture_units[0].texture = screen_info.display_texture;
358 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; 360 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
361 // Workaround brigthness problems in SMO by enabling sRGB in the final output
362 // if it has been used in the frame
363 // Needed because of this bug in QT
364 // QTBUG-50987
365 state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
359 state.Apply(); 366 state.Apply();
360
361 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); 367 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
362 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 368 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
363 369 // restore default state
370 state.framebuffer_srgb.enabled = false;
364 state.texture_units[0].texture = 0; 371 state.texture_units[0].texture = 0;
365 state.Apply(); 372 state.Apply();
373 // Clear sRGB state for the next frame
374 OpenGLState::ClearsRGBUsed();
366} 375}
367 376
368/** 377/**
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 961467a62..c0868c0e4 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -51,7 +51,8 @@ public:
51 ~RendererOpenGL() override; 51 ~RendererOpenGL() override;
52 52
53 /// Swap buffers (render frame) 53 /// Swap buffers (render frame)
54 void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override; 54 void SwapBuffers(
55 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
55 56
56 /// Initialize the renderer 57 /// Initialize the renderer
57 bool Init() override; 58 bool Init() override;
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp
new file mode 100644
index 000000000..d84634cb3
--- /dev/null
+++ b/src/video_core/renderer_opengl/utils.cpp
@@ -0,0 +1,38 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6#include <fmt/format.h>
7#include <glad/glad.h>
8#include "common/common_types.h"
9#include "video_core/renderer_opengl/utils.h"
10
11namespace OpenGL {
12
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) {
14 if (!GLAD_GL_KHR_debug) {
15 return; // We don't need to throw an error as this is just for debugging
16 }
17 const std::string nice_addr = fmt::format("0x{:016x}", addr);
18 std::string object_label;
19
20 if (extra_info.empty()) {
21 switch (identifier) {
22 case GL_TEXTURE:
23 object_label = "Texture@" + nice_addr;
24 break;
25 case GL_PROGRAM:
26 object_label = "Shader@" + nice_addr;
27 break;
28 default:
29 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
30 break;
31 }
32 } else {
33 object_label = extra_info + '@' + nice_addr;
34 }
35 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
36}
37
38} // namespace OpenGL \ No newline at end of file
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h
new file mode 100644
index 000000000..1fcb6fc11
--- /dev/null
+++ b/src/video_core/renderer_opengl/utils.h
@@ -0,0 +1,15 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <glad/glad.h>
9#include "common/common_types.h"
10
11namespace OpenGL {
12
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = "");
14
15} // namespace OpenGL \ No newline at end of file
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
new file mode 100644
index 000000000..9582dd2ca
--- /dev/null
+++ b/src/video_core/surface.cpp
@@ -0,0 +1,490 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/math_util.h"
7#include "video_core/surface.h"
8
9namespace VideoCore::Surface {
10
11SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
12 switch (texture_type) {
13 case Tegra::Texture::TextureType::Texture1D:
14 return SurfaceTarget::Texture1D;
15 case Tegra::Texture::TextureType::Texture2D:
16 case Tegra::Texture::TextureType::Texture2DNoMipmap:
17 return SurfaceTarget::Texture2D;
18 case Tegra::Texture::TextureType::Texture3D:
19 return SurfaceTarget::Texture3D;
20 case Tegra::Texture::TextureType::TextureCubemap:
21 return SurfaceTarget::TextureCubemap;
22 case Tegra::Texture::TextureType::TextureCubeArray:
23 return SurfaceTarget::TextureCubeArray;
24 case Tegra::Texture::TextureType::Texture1DArray:
25 return SurfaceTarget::Texture1DArray;
26 case Tegra::Texture::TextureType::Texture2DArray:
27 return SurfaceTarget::Texture2DArray;
28 default:
29 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
30 UNREACHABLE();
31 return SurfaceTarget::Texture2D;
32 }
33}
34
35bool SurfaceTargetIsLayered(SurfaceTarget target) {
36 switch (target) {
37 case SurfaceTarget::Texture1D:
38 case SurfaceTarget::Texture2D:
39 case SurfaceTarget::Texture3D:
40 return false;
41 case SurfaceTarget::Texture1DArray:
42 case SurfaceTarget::Texture2DArray:
43 case SurfaceTarget::TextureCubemap:
44 case SurfaceTarget::TextureCubeArray:
45 return true;
46 default:
47 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
48 UNREACHABLE();
49 return false;
50 }
51}
52
53PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
54 switch (format) {
55 case Tegra::DepthFormat::S8_Z24_UNORM:
56 return PixelFormat::S8Z24;
57 case Tegra::DepthFormat::Z24_S8_UNORM:
58 return PixelFormat::Z24S8;
59 case Tegra::DepthFormat::Z32_FLOAT:
60 return PixelFormat::Z32F;
61 case Tegra::DepthFormat::Z16_UNORM:
62 return PixelFormat::Z16;
63 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
64 return PixelFormat::Z32FS8;
65 default:
66 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
67 UNREACHABLE();
68 }
69}
70
71PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
72 switch (format) {
73 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
74 // gamma.
75 case Tegra::RenderTargetFormat::RGBA8_SRGB:
76 return PixelFormat::RGBA8_SRGB;
77 case Tegra::RenderTargetFormat::RGBA8_UNORM:
78 return PixelFormat::ABGR8U;
79 case Tegra::RenderTargetFormat::RGBA8_SNORM:
80 return PixelFormat::ABGR8S;
81 case Tegra::RenderTargetFormat::RGBA8_UINT:
82 return PixelFormat::ABGR8UI;
83 case Tegra::RenderTargetFormat::BGRA8_SRGB:
84 return PixelFormat::BGRA8_SRGB;
85 case Tegra::RenderTargetFormat::BGRA8_UNORM:
86 return PixelFormat::BGRA8;
87 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
88 return PixelFormat::A2B10G10R10U;
89 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
90 return PixelFormat::RGBA16F;
91 case Tegra::RenderTargetFormat::RGBA16_UNORM:
92 return PixelFormat::RGBA16U;
93 case Tegra::RenderTargetFormat::RGBA16_UINT:
94 return PixelFormat::RGBA16UI;
95 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
96 return PixelFormat::RGBA32F;
97 case Tegra::RenderTargetFormat::RG32_FLOAT:
98 return PixelFormat::RG32F;
99 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
100 return PixelFormat::R11FG11FB10F;
101 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
102 return PixelFormat::B5G6R5U;
103 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
104 return PixelFormat::A1B5G5R5U;
105 case Tegra::RenderTargetFormat::RGBA32_UINT:
106 return PixelFormat::RGBA32UI;
107 case Tegra::RenderTargetFormat::R8_UNORM:
108 return PixelFormat::R8U;
109 case Tegra::RenderTargetFormat::R8_UINT:
110 return PixelFormat::R8UI;
111 case Tegra::RenderTargetFormat::RG16_FLOAT:
112 return PixelFormat::RG16F;
113 case Tegra::RenderTargetFormat::RG16_UINT:
114 return PixelFormat::RG16UI;
115 case Tegra::RenderTargetFormat::RG16_SINT:
116 return PixelFormat::RG16I;
117 case Tegra::RenderTargetFormat::RG16_UNORM:
118 return PixelFormat::RG16;
119 case Tegra::RenderTargetFormat::RG16_SNORM:
120 return PixelFormat::RG16S;
121 case Tegra::RenderTargetFormat::RG8_UNORM:
122 return PixelFormat::RG8U;
123 case Tegra::RenderTargetFormat::RG8_SNORM:
124 return PixelFormat::RG8S;
125 case Tegra::RenderTargetFormat::R16_FLOAT:
126 return PixelFormat::R16F;
127 case Tegra::RenderTargetFormat::R16_UNORM:
128 return PixelFormat::R16U;
129 case Tegra::RenderTargetFormat::R16_SNORM:
130 return PixelFormat::R16S;
131 case Tegra::RenderTargetFormat::R16_UINT:
132 return PixelFormat::R16UI;
133 case Tegra::RenderTargetFormat::R16_SINT:
134 return PixelFormat::R16I;
135 case Tegra::RenderTargetFormat::R32_FLOAT:
136 return PixelFormat::R32F;
137 case Tegra::RenderTargetFormat::R32_UINT:
138 return PixelFormat::R32UI;
139 case Tegra::RenderTargetFormat::RG32_UINT:
140 return PixelFormat::RG32UI;
141 default:
142 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
143 UNREACHABLE();
144 }
145}
146
147PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
148 Tegra::Texture::ComponentType component_type,
149 bool is_srgb) {
150 // TODO(Subv): Properly implement this
151 switch (format) {
152 case Tegra::Texture::TextureFormat::A8R8G8B8:
153 if (is_srgb) {
154 return PixelFormat::RGBA8_SRGB;
155 }
156 switch (component_type) {
157 case Tegra::Texture::ComponentType::UNORM:
158 return PixelFormat::ABGR8U;
159 case Tegra::Texture::ComponentType::SNORM:
160 return PixelFormat::ABGR8S;
161 case Tegra::Texture::ComponentType::UINT:
162 return PixelFormat::ABGR8UI;
163 }
164 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
165 UNREACHABLE();
166 case Tegra::Texture::TextureFormat::B5G6R5:
167 switch (component_type) {
168 case Tegra::Texture::ComponentType::UNORM:
169 return PixelFormat::B5G6R5U;
170 }
171 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
172 UNREACHABLE();
173 case Tegra::Texture::TextureFormat::A2B10G10R10:
174 switch (component_type) {
175 case Tegra::Texture::ComponentType::UNORM:
176 return PixelFormat::A2B10G10R10U;
177 }
178 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
179 UNREACHABLE();
180 case Tegra::Texture::TextureFormat::A1B5G5R5:
181 switch (component_type) {
182 case Tegra::Texture::ComponentType::UNORM:
183 return PixelFormat::A1B5G5R5U;
184 }
185 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
186 UNREACHABLE();
187 case Tegra::Texture::TextureFormat::R8:
188 switch (component_type) {
189 case Tegra::Texture::ComponentType::UNORM:
190 return PixelFormat::R8U;
191 case Tegra::Texture::ComponentType::UINT:
192 return PixelFormat::R8UI;
193 }
194 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
195 UNREACHABLE();
196 case Tegra::Texture::TextureFormat::G8R8:
197 switch (component_type) {
198 case Tegra::Texture::ComponentType::UNORM:
199 return PixelFormat::G8R8U;
200 case Tegra::Texture::ComponentType::SNORM:
201 return PixelFormat::G8R8S;
202 }
203 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
204 UNREACHABLE();
205 case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
206 switch (component_type) {
207 case Tegra::Texture::ComponentType::UNORM:
208 return PixelFormat::RGBA16U;
209 case Tegra::Texture::ComponentType::FLOAT:
210 return PixelFormat::RGBA16F;
211 }
212 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
213 UNREACHABLE();
214 case Tegra::Texture::TextureFormat::BF10GF11RF11:
215 switch (component_type) {
216 case Tegra::Texture::ComponentType::FLOAT:
217 return PixelFormat::R11FG11FB10F;
218 }
219 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
220 UNREACHABLE();
221 case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
222 switch (component_type) {
223 case Tegra::Texture::ComponentType::FLOAT:
224 return PixelFormat::RGBA32F;
225 case Tegra::Texture::ComponentType::UINT:
226 return PixelFormat::RGBA32UI;
227 }
228 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
229 UNREACHABLE();
230 case Tegra::Texture::TextureFormat::R32_G32:
231 switch (component_type) {
232 case Tegra::Texture::ComponentType::FLOAT:
233 return PixelFormat::RG32F;
234 case Tegra::Texture::ComponentType::UINT:
235 return PixelFormat::RG32UI;
236 }
237 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
238 UNREACHABLE();
239 case Tegra::Texture::TextureFormat::R32_G32_B32:
240 switch (component_type) {
241 case Tegra::Texture::ComponentType::FLOAT:
242 return PixelFormat::RGB32F;
243 }
244 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
245 UNREACHABLE();
246 case Tegra::Texture::TextureFormat::R16:
247 switch (component_type) {
248 case Tegra::Texture::ComponentType::FLOAT:
249 return PixelFormat::R16F;
250 case Tegra::Texture::ComponentType::UNORM:
251 return PixelFormat::R16U;
252 case Tegra::Texture::ComponentType::SNORM:
253 return PixelFormat::R16S;
254 case Tegra::Texture::ComponentType::UINT:
255 return PixelFormat::R16UI;
256 case Tegra::Texture::ComponentType::SINT:
257 return PixelFormat::R16I;
258 }
259 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
260 UNREACHABLE();
261 case Tegra::Texture::TextureFormat::R32:
262 switch (component_type) {
263 case Tegra::Texture::ComponentType::FLOAT:
264 return PixelFormat::R32F;
265 case Tegra::Texture::ComponentType::UINT:
266 return PixelFormat::R32UI;
267 }
268 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
269 UNREACHABLE();
270 case Tegra::Texture::TextureFormat::ZF32:
271 return PixelFormat::Z32F;
272 case Tegra::Texture::TextureFormat::Z16:
273 return PixelFormat::Z16;
274 case Tegra::Texture::TextureFormat::Z24S8:
275 return PixelFormat::Z24S8;
276 case Tegra::Texture::TextureFormat::DXT1:
277 return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
278 case Tegra::Texture::TextureFormat::DXT23:
279 return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23;
280 case Tegra::Texture::TextureFormat::DXT45:
281 return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45;
282 case Tegra::Texture::TextureFormat::DXN1:
283 return PixelFormat::DXN1;
284 case Tegra::Texture::TextureFormat::DXN2:
285 switch (component_type) {
286 case Tegra::Texture::ComponentType::UNORM:
287 return PixelFormat::DXN2UNORM;
288 case Tegra::Texture::ComponentType::SNORM:
289 return PixelFormat::DXN2SNORM;
290 }
291 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
292 UNREACHABLE();
293 case Tegra::Texture::TextureFormat::BC7U:
294 return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U;
295 case Tegra::Texture::TextureFormat::BC6H_UF16:
296 return PixelFormat::BC6H_UF16;
297 case Tegra::Texture::TextureFormat::BC6H_SF16:
298 return PixelFormat::BC6H_SF16;
299 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
300 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
301 case Tegra::Texture::TextureFormat::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;
305 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
306 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
307 case Tegra::Texture::TextureFormat::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;
311 case Tegra::Texture::TextureFormat::R16_G16:
312 switch (component_type) {
313 case Tegra::Texture::ComponentType::FLOAT:
314 return PixelFormat::RG16F;
315 case Tegra::Texture::ComponentType::UNORM:
316 return PixelFormat::RG16;
317 case Tegra::Texture::ComponentType::SNORM:
318 return PixelFormat::RG16S;
319 case Tegra::Texture::ComponentType::UINT:
320 return PixelFormat::RG16UI;
321 case Tegra::Texture::ComponentType::SINT:
322 return PixelFormat::RG16I;
323 }
324 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
325 UNREACHABLE();
326 default:
327 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
328 static_cast<u32>(component_type));
329 UNREACHABLE();
330 }
331}
332
333ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
334 // TODO(Subv): Implement more component types
335 switch (type) {
336 case Tegra::Texture::ComponentType::UNORM:
337 return ComponentType::UNorm;
338 case Tegra::Texture::ComponentType::FLOAT:
339 return ComponentType::Float;
340 case Tegra::Texture::ComponentType::SNORM:
341 return ComponentType::SNorm;
342 case Tegra::Texture::ComponentType::UINT:
343 return ComponentType::UInt;
344 case Tegra::Texture::ComponentType::SINT:
345 return ComponentType::SInt;
346 default:
347 LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
348 UNREACHABLE();
349 }
350}
351
352ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
353 // TODO(Subv): Implement more render targets
354 switch (format) {
355 case Tegra::RenderTargetFormat::RGBA8_UNORM:
356 case Tegra::RenderTargetFormat::RGBA8_SRGB:
357 case Tegra::RenderTargetFormat::BGRA8_UNORM:
358 case Tegra::RenderTargetFormat::BGRA8_SRGB:
359 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
360 case Tegra::RenderTargetFormat::R8_UNORM:
361 case Tegra::RenderTargetFormat::RG16_UNORM:
362 case Tegra::RenderTargetFormat::R16_UNORM:
363 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
364 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
365 case Tegra::RenderTargetFormat::RG8_UNORM:
366 case Tegra::RenderTargetFormat::RGBA16_UNORM:
367 return ComponentType::UNorm;
368 case Tegra::RenderTargetFormat::RGBA8_SNORM:
369 case Tegra::RenderTargetFormat::RG16_SNORM:
370 case Tegra::RenderTargetFormat::R16_SNORM:
371 case Tegra::RenderTargetFormat::RG8_SNORM:
372 return ComponentType::SNorm;
373 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
374 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
375 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
376 case Tegra::RenderTargetFormat::RG32_FLOAT:
377 case Tegra::RenderTargetFormat::RG16_FLOAT:
378 case Tegra::RenderTargetFormat::R16_FLOAT:
379 case Tegra::RenderTargetFormat::R32_FLOAT:
380 return ComponentType::Float;
381 case Tegra::RenderTargetFormat::RGBA32_UINT:
382 case Tegra::RenderTargetFormat::RGBA16_UINT:
383 case Tegra::RenderTargetFormat::RG16_UINT:
384 case Tegra::RenderTargetFormat::R8_UINT:
385 case Tegra::RenderTargetFormat::R16_UINT:
386 case Tegra::RenderTargetFormat::RG32_UINT:
387 case Tegra::RenderTargetFormat::R32_UINT:
388 case Tegra::RenderTargetFormat::RGBA8_UINT:
389 return ComponentType::UInt;
390 case Tegra::RenderTargetFormat::RG16_SINT:
391 case Tegra::RenderTargetFormat::R16_SINT:
392 return ComponentType::SInt;
393 default:
394 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
395 UNREACHABLE();
396 }
397}
398
399PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
400 switch (format) {
401 case Tegra::FramebufferConfig::PixelFormat::ABGR8:
402 return PixelFormat::ABGR8U;
403 default:
404 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
405 UNREACHABLE();
406 }
407}
408
409ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
410 switch (format) {
411 case Tegra::DepthFormat::Z16_UNORM:
412 case Tegra::DepthFormat::S8_Z24_UNORM:
413 case Tegra::DepthFormat::Z24_S8_UNORM:
414 return ComponentType::UNorm;
415 case Tegra::DepthFormat::Z32_FLOAT:
416 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
417 return ComponentType::Float;
418 default:
419 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
420 UNREACHABLE();
421 }
422}
423
424SurfaceType GetFormatType(PixelFormat pixel_format) {
425 if (static_cast<std::size_t>(pixel_format) <
426 static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
427 return SurfaceType::ColorTexture;
428 }
429
430 if (static_cast<std::size_t>(pixel_format) <
431 static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
432 return SurfaceType::Depth;
433 }
434
435 if (static_cast<std::size_t>(pixel_format) <
436 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
437 return SurfaceType::DepthStencil;
438 }
439
440 // TODO(Subv): Implement the other formats
441 ASSERT(false);
442
443 return SurfaceType::Invalid;
444}
445
446bool IsPixelFormatASTC(PixelFormat format) {
447 switch (format) {
448 case PixelFormat::ASTC_2D_4X4:
449 case PixelFormat::ASTC_2D_5X4:
450 case PixelFormat::ASTC_2D_5X5:
451 case PixelFormat::ASTC_2D_8X8:
452 case PixelFormat::ASTC_2D_8X5:
453 case PixelFormat::ASTC_2D_4X4_SRGB:
454 case PixelFormat::ASTC_2D_5X4_SRGB:
455 case PixelFormat::ASTC_2D_5X5_SRGB:
456 case PixelFormat::ASTC_2D_8X8_SRGB:
457 case PixelFormat::ASTC_2D_8X5_SRGB:
458 case PixelFormat::ASTC_2D_10X8:
459 case PixelFormat::ASTC_2D_10X8_SRGB:
460 return true;
461 default:
462 return false;
463 }
464}
465
466std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
467 return {GetDefaultBlockWidth(format), GetDefaultBlockHeight(format)};
468}
469
470bool IsFormatBCn(PixelFormat format) {
471 switch (format) {
472 case PixelFormat::DXT1:
473 case PixelFormat::DXT23:
474 case PixelFormat::DXT45:
475 case PixelFormat::DXN1:
476 case PixelFormat::DXN2SNORM:
477 case PixelFormat::DXN2UNORM:
478 case PixelFormat::BC7U:
479 case PixelFormat::BC6H_UF16:
480 case PixelFormat::BC6H_SF16:
481 case PixelFormat::DXT1_SRGB:
482 case PixelFormat::DXT23_SRGB:
483 case PixelFormat::DXT45_SRGB:
484 case PixelFormat::BC7U_SRGB:
485 return true;
486 }
487 return false;
488}
489
490} // namespace VideoCore::Surface
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
new file mode 100644
index 000000000..0dd3eb2e4
--- /dev/null
+++ b/src/video_core/surface.h
@@ -0,0 +1,477 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <climits>
8#include <utility>
9#include "common/assert.h"
10#include "common/common_types.h"
11#include "common/logging/log.h"
12#include "video_core/gpu.h"
13#include "video_core/textures/texture.h"
14
15namespace VideoCore::Surface {
16
17enum class PixelFormat {
18 ABGR8U = 0,
19 ABGR8S = 1,
20 ABGR8UI = 2,
21 B5G6R5U = 3,
22 A2B10G10R10U = 4,
23 A1B5G5R5U = 5,
24 R8U = 6,
25 R8UI = 7,
26 RGBA16F = 8,
27 RGBA16U = 9,
28 RGBA16UI = 10,
29 R11FG11FB10F = 11,
30 RGBA32UI = 12,
31 DXT1 = 13,
32 DXT23 = 14,
33 DXT45 = 15,
34 DXN1 = 16, // This is also known as BC4
35 DXN2UNORM = 17,
36 DXN2SNORM = 18,
37 BC7U = 19,
38 BC6H_UF16 = 20,
39 BC6H_SF16 = 21,
40 ASTC_2D_4X4 = 22,
41 G8R8U = 23,
42 G8R8S = 24,
43 BGRA8 = 25,
44 RGBA32F = 26,
45 RG32F = 27,
46 R32F = 28,
47 R16F = 29,
48 R16U = 30,
49 R16S = 31,
50 R16UI = 32,
51 R16I = 33,
52 RG16 = 34,
53 RG16F = 35,
54 RG16UI = 36,
55 RG16I = 37,
56 RG16S = 38,
57 RGB32F = 39,
58 RGBA8_SRGB = 40,
59 RG8U = 41,
60 RG8S = 42,
61 RG32UI = 43,
62 R32UI = 44,
63 ASTC_2D_8X8 = 45,
64 ASTC_2D_8X5 = 46,
65 ASTC_2D_5X4 = 47,
66 BGRA8_SRGB = 48,
67 DXT1_SRGB = 49,
68 DXT23_SRGB = 50,
69 DXT45_SRGB = 51,
70 BC7U_SRGB = 52,
71 ASTC_2D_4X4_SRGB = 53,
72 ASTC_2D_8X8_SRGB = 54,
73 ASTC_2D_8X5_SRGB = 55,
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,
79
80 MaxColorFormat,
81
82 // Depth formats
83 Z32F = 61,
84 Z16 = 62,
85
86 MaxDepthFormat,
87
88 // DepthStencil formats
89 Z24S8 = 63,
90 S8Z24 = 64,
91 Z32FS8 = 65,
92
93 MaxDepthStencilFormat,
94
95 Max = MaxDepthStencilFormat,
96 Invalid = 255,
97};
98
99static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);
100
101enum class ComponentType {
102 Invalid = 0,
103 SNorm = 1,
104 UNorm = 2,
105 SInt = 3,
106 UInt = 4,
107 Float = 5,
108};
109
110enum class SurfaceType {
111 ColorTexture = 0,
112 Depth = 1,
113 DepthStencil = 2,
114 Fill = 3,
115 Invalid = 4,
116};
117
118enum class SurfaceTarget {
119 Texture1D,
120 Texture2D,
121 Texture3D,
122 Texture1DArray,
123 Texture2DArray,
124 TextureCubemap,
125 TextureCubeArray,
126};
127
128/**
129 * Gets the compression factor for the specified PixelFormat. This applies to just the
130 * "compressed width" and "compressed height", not the overall compression factor of a
131 * compressed image. This is used for maintaining proper surface sizes for compressed
132 * texture formats.
133 */
134static constexpr u32 GetCompressionFactor(PixelFormat format) {
135 if (format == PixelFormat::Invalid)
136 return 0;
137
138 constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
139 1, // ABGR8U
140 1, // ABGR8S
141 1, // ABGR8UI
142 1, // B5G6R5U
143 1, // A2B10G10R10U
144 1, // A1B5G5R5U
145 1, // R8U
146 1, // R8UI
147 1, // RGBA16F
148 1, // RGBA16U
149 1, // RGBA16UI
150 1, // R11FG11FB10F
151 1, // RGBA32UI
152 4, // DXT1
153 4, // DXT23
154 4, // DXT45
155 4, // DXN1
156 4, // DXN2UNORM
157 4, // DXN2SNORM
158 4, // BC7U
159 4, // BC6H_UF16
160 4, // BC6H_SF16
161 4, // ASTC_2D_4X4
162 1, // G8R8U
163 1, // G8R8S
164 1, // BGRA8
165 1, // RGBA32F
166 1, // RG32F
167 1, // R32F
168 1, // R16F
169 1, // R16U
170 1, // R16S
171 1, // R16UI
172 1, // R16I
173 1, // RG16
174 1, // RG16F
175 1, // RG16UI
176 1, // RG16I
177 1, // RG16S
178 1, // RGB32F
179 1, // RGBA8_SRGB
180 1, // RG8U
181 1, // RG8S
182 1, // RG32UI
183 1, // R32UI
184 4, // ASTC_2D_8X8
185 4, // ASTC_2D_8X5
186 4, // ASTC_2D_5X4
187 1, // BGRA8_SRGB
188 4, // DXT1_SRGB
189 4, // DXT23_SRGB
190 4, // DXT45_SRGB
191 4, // BC7U_SRGB
192 4, // ASTC_2D_4X4_SRGB
193 4, // ASTC_2D_8X8_SRGB
194 4, // ASTC_2D_8X5_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
200 1, // Z32F
201 1, // Z16
202 1, // Z24S8
203 1, // S8Z24
204 1, // Z32FS8
205 }};
206
207 ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
208 return compression_factor_table[static_cast<std::size_t>(format)];
209}
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
286static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
287 if (format == PixelFormat::Invalid)
288 return 0;
289
290 constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
291 1, // ABGR8U
292 1, // ABGR8S
293 1, // ABGR8UI
294 1, // B5G6R5U
295 1, // A2B10G10R10U
296 1, // A1B5G5R5U
297 1, // R8U
298 1, // R8UI
299 1, // RGBA16F
300 1, // RGBA16U
301 1, // RGBA16UI
302 1, // R11FG11FB10F
303 1, // RGBA32UI
304 4, // DXT1
305 4, // DXT23
306 4, // DXT45
307 4, // DXN1
308 4, // DXN2UNORM
309 4, // DXN2SNORM
310 4, // BC7U
311 4, // BC6H_UF16
312 4, // BC6H_SF16
313 4, // ASTC_2D_4X4
314 1, // G8R8U
315 1, // G8R8S
316 1, // BGRA8
317 1, // RGBA32F
318 1, // RG32F
319 1, // R32F
320 1, // R16F
321 1, // R16U
322 1, // R16S
323 1, // R16UI
324 1, // R16I
325 1, // RG16
326 1, // RG16F
327 1, // RG16UI
328 1, // RG16I
329 1, // RG16S
330 1, // RGB32F
331 1, // RGBA8_SRGB
332 1, // RG8U
333 1, // RG8S
334 1, // RG32UI
335 1, // R32UI
336 8, // ASTC_2D_8X8
337 5, // ASTC_2D_8X5
338 4, // ASTC_2D_5X4
339 1, // BGRA8_SRGB
340 4, // DXT1_SRGB
341 4, // DXT23_SRGB
342 4, // DXT45_SRGB
343 4, // BC7U_SRGB
344 4, // ASTC_2D_4X4_SRGB
345 8, // ASTC_2D_8X8_SRGB
346 5, // ASTC_2D_8X5_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
352 1, // Z32F
353 1, // Z16
354 1, // Z24S8
355 1, // S8Z24
356 1, // Z32FS8
357 }};
358
359 ASSERT(static_cast<std::size_t>(format) < block_height_table.size());
360 return block_height_table[static_cast<std::size_t>(format)];
361}
362
363static constexpr u32 GetFormatBpp(PixelFormat format) {
364 if (format == PixelFormat::Invalid)
365 return 0;
366
367 constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
368 32, // ABGR8U
369 32, // ABGR8S
370 32, // ABGR8UI
371 16, // B5G6R5U
372 32, // A2B10G10R10U
373 16, // A1B5G5R5U
374 8, // R8U
375 8, // R8UI
376 64, // RGBA16F
377 64, // RGBA16U
378 64, // RGBA16UI
379 32, // R11FG11FB10F
380 128, // RGBA32UI
381 64, // DXT1
382 128, // DXT23
383 128, // DXT45
384 64, // DXN1
385 128, // DXN2UNORM
386 128, // DXN2SNORM
387 128, // BC7U
388 128, // BC6H_UF16
389 128, // BC6H_SF16
390 128, // ASTC_2D_4X4
391 16, // G8R8U
392 16, // G8R8S
393 32, // BGRA8
394 128, // RGBA32F
395 64, // RG32F
396 32, // R32F
397 16, // R16F
398 16, // R16U
399 16, // R16S
400 16, // R16UI
401 16, // R16I
402 32, // RG16
403 32, // RG16F
404 32, // RG16UI
405 32, // RG16I
406 32, // RG16S
407 96, // RGB32F
408 32, // RGBA8_SRGB
409 16, // RG8U
410 16, // RG8S
411 64, // RG32UI
412 32, // R32UI
413 128, // ASTC_2D_8X8
414 128, // ASTC_2D_8X5
415 128, // ASTC_2D_5X4
416 32, // BGRA8_SRGB
417 64, // DXT1_SRGB
418 128, // DXT23_SRGB
419 128, // DXT45_SRGB
420 128, // BC7U
421 128, // ASTC_2D_4X4_SRGB
422 128, // ASTC_2D_8X8_SRGB
423 128, // ASTC_2D_8X5_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
429 32, // Z32F
430 16, // Z16
431 32, // Z24S8
432 32, // S8Z24
433 64, // Z32FS8
434 }};
435
436 ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
437 return bpp_table[static_cast<std::size_t>(format)];
438}
439
440/// Returns the sizer in bytes of the specified pixel format
441static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
442 if (pixel_format == PixelFormat::Invalid) {
443 return 0;
444 }
445 return GetFormatBpp(pixel_format) / CHAR_BIT;
446}
447
448SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);
449
450bool SurfaceTargetIsLayered(SurfaceTarget target);
451
452PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
453
454PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
455
456PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
457 Tegra::Texture::ComponentType component_type,
458 bool is_srgb);
459
460ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type);
461
462ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format);
463
464PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format);
465
466ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format);
467
468SurfaceType GetFormatType(PixelFormat pixel_format);
469
470bool IsPixelFormatASTC(PixelFormat format);
471
472std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);
473
474/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
475bool IsFormatBCn(PixelFormat format);
476
477} // namespace VideoCore::Surface
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 f1b40e7f5..c9160b467 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,24 +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 blocks = blocks_on_x * blocks_on_y * blocks_on_z; 145 constexpr u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
146 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
147 const u32 xy_block_size = gob_size * block_height; 146 const u32 xy_block_size = gob_size * block_height;
148 const u32 block_size = xy_block_size * block_depth; 147 const u32 block_size = xy_block_size * block_depth;
149 u32 tile_offset = 0; 148 u32 tile_offset = 0;
@@ -172,7 +171,7 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
172} 171}
173 172
174void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, 173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
175 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,
176 bool unswizzle, u32 block_height, u32 block_depth) { 175 bool unswizzle, u32 block_height, u32 block_depth) {
177 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) {
178 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, 177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
@@ -203,6 +202,8 @@ u32 BytesPerPixel(TextureFormat format) {
203 case TextureFormat::ASTC_2D_5X4: 202 case TextureFormat::ASTC_2D_5X4:
204 case TextureFormat::ASTC_2D_8X8: 203 case TextureFormat::ASTC_2D_8X8:
205 case TextureFormat::ASTC_2D_8X5: 204 case TextureFormat::ASTC_2D_8X5:
205 case TextureFormat::ASTC_2D_10X8:
206 case TextureFormat::ASTC_2D_5X5:
206 case TextureFormat::A8R8G8B8: 207 case TextureFormat::A8R8G8B8:
207 case TextureFormat::A2B10G10R10: 208 case TextureFormat::A2B10G10R10:
208 case TextureFormat::BF10GF11RF11: 209 case TextureFormat::BF10GF11RF11:
@@ -228,12 +229,21 @@ u32 BytesPerPixel(TextureFormat format) {
228 } 229 }
229} 230}
230 231
231std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 232void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
232 u32 height, u32 depth, u32 block_height, u32 block_depth) { 233 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
234 u32 block_depth) {
235 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
236 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
237 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
238 block_height, block_depth);
239}
240
241std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
242 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
243 u32 block_height, u32 block_depth) {
233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 244 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
234 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel, 245 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
235 Memory::GetPointer(address), unswizzled_data.data(), true, block_height, 246 width, height, depth, block_height, block_depth);
236 block_depth);
237 return unswizzled_data; 247 return unswizzled_data;
238} 248}
239 249
@@ -293,6 +303,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
293 case TextureFormat::BC6H_SF16: 303 case TextureFormat::BC6H_SF16:
294 case TextureFormat::ASTC_2D_4X4: 304 case TextureFormat::ASTC_2D_4X4:
295 case TextureFormat::ASTC_2D_8X8: 305 case TextureFormat::ASTC_2D_8X8:
306 case TextureFormat::ASTC_2D_5X5:
307 case TextureFormat::ASTC_2D_10X8:
296 case TextureFormat::A8R8G8B8: 308 case TextureFormat::A8R8G8B8:
297 case TextureFormat::A2B10G10R10: 309 case TextureFormat::A2B10G10R10:
298 case TextureFormat::A1B5G5R5: 310 case TextureFormat::A1B5G5R5:
@@ -320,13 +332,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
320std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 332std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
321 u32 block_height, u32 block_depth) { 333 u32 block_height, u32 block_depth) {
322 if (tiled) { 334 if (tiled) {
323 const u32 gobs_in_x = 64 / bytes_per_pixel; 335 constexpr u32 gobs_in_x = 64;
324 const u32 gobs_in_y = 8; 336 constexpr u32 gobs_in_y = 8;
325 const u32 gobs_in_z = 1; 337 constexpr u32 gobs_in_z = 1;
326 const u32 aligned_width = Common::AlignUp(width, gobs_in_x); 338 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
327 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); 339 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
328 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); 340 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
329 return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; 341 return aligned_width * aligned_height * aligned_depth;
330 } else { 342 } else {
331 return width * height * depth * bytes_per_pixel; 343 return width * height * depth * bytes_per_pixel;
332 } 344 }
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 4726f54a5..f4ef7c73e 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -10,11 +10,24 @@
10 10
11namespace Tegra::Texture { 11namespace Tegra::Texture {
12 12
13// GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents
14// an small rect of (64/bytes_per_pixel)X8.
15inline std::size_t GetGOBSize() {
16 return 512;
17}
18
19/**
20 * Unswizzles a swizzled texture without changing its format.
21 */
22void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight);
13/** 26/**
14 * Unswizzles a swizzled texture without changing its format. 27 * Unswizzles a swizzled texture without changing its format.
15 */ 28 */
16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 29std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
17 u32 height, u32 depth, 30 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
18 u32 block_height = TICEntry::DefaultBlockHeight, 31 u32 block_height = TICEntry::DefaultBlockHeight,
19 u32 block_depth = TICEntry::DefaultBlockHeight); 32 u32 block_depth = TICEntry::DefaultBlockHeight);
20 33
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 5947bd2b9..e199d019a 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -168,19 +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;
177 BitField<22, 1, u32> srgb_conversion;
176 BitField<23, 4, TextureType> texture_type; 178 BitField<23, 4, TextureType> texture_type;
179 BitField<29, 3, u32> border_size;
177 }; 180 };
178 union { 181 union {
179 BitField<0, 16, u32> height_minus_1; 182 BitField<0, 16, u32> height_minus_1;
180 BitField<16, 15, u32> depth_minus_1; 183 BitField<16, 15, u32> depth_minus_1;
181 }; 184 };
185 union {
186 BitField<6, 13, u32> mip_lod_bias;
187 BitField<27, 3, u32> max_anisotropy;
188 };
182 189
183 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 };
184 194
185 GPUVAddr Address() const { 195 GPUVAddr Address() const {
186 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);
@@ -227,6 +237,10 @@ struct TICEntry {
227 return header_version == TICHeaderVersion::BlockLinear || 237 return header_version == TICHeaderVersion::BlockLinear ||
228 header_version == TICHeaderVersion::BlockLinearColorKey; 238 header_version == TICHeaderVersion::BlockLinearColorKey;
229 } 239 }
240
241 bool IsSrgbConversionEnabled() const {
242 return srgb_conversion != 0;
243 }
230}; 244};
231static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); 245static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");
232 246
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 237cc1307..e0a14d48f 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -161,30 +161,4 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe
161 } 161 }
162} 162}
163 163
164static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
165 std::string extra_info = "") {
166 if (!GLAD_GL_KHR_debug) {
167 return; // We don't need to throw an error as this is just for debugging
168 }
169 const std::string nice_addr = fmt::format("0x{:016x}", addr);
170 std::string object_label;
171
172 if (extra_info.empty()) {
173 switch (identifier) {
174 case GL_TEXTURE:
175 object_label = "Texture@" + nice_addr;
176 break;
177 case GL_PROGRAM:
178 object_label = "Shader@" + nice_addr;
179 break;
180 default:
181 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
182 break;
183 }
184 } else {
185 object_label = extra_info + '@' + nice_addr;
186 }
187 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
188}
189
190} // namespace VideoCore 164} // namespace VideoCore
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 0a8f2bd9e..9156ce802 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -102,16 +102,27 @@ void TelemetryJson::Complete() {
102 impl->SerializeSection(Telemetry::FieldType::App, "App"); 102 impl->SerializeSection(Telemetry::FieldType::App, "App");
103 impl->SerializeSection(Telemetry::FieldType::Session, "Session"); 103 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance"); 104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
105 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
106 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 105 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
107 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 106 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
108 107
109 auto content = impl->TopSection().dump(); 108 auto content = impl->TopSection().dump();
110 // Send the telemetry async but don't handle the errors since they were written to the log 109 // Send the telemetry async but don't handle the errors since they were written to the log
111 Common::DetachedTasks::AddTask( 110 Common::DetachedTasks::AddTask([host{impl->host}, content]() {
112 [host{impl->host}, username{impl->username}, token{impl->token}, content]() { 111 Client{host, "", ""}.PostJson("/telemetry", content, true);
113 Client{host, username, token}.PostJson("/telemetry", content, true); 112 });
114 }); 113}
114
115bool TelemetryJson::SubmitTestcase() {
116 impl->SerializeSection(Telemetry::FieldType::App, "App");
117 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
118 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
119 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
120
121 auto content = impl->TopSection().dump();
122 Client client(impl->host, impl->username, impl->token);
123 auto value = client.PostJson("/gamedb/testcase", content, false);
124
125 return value.result_code == Common::WebResult::Code::Success;
115} 126}
116 127
117} // namespace WebService 128} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 93371414a..dfd202829 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -35,6 +35,7 @@ public:
35 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override; 35 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
36 36
37 void Complete() override; 37 void Complete() override;
38 bool SubmitTestcase() override;
38 39
39private: 40private:
40 struct Impl; 41 struct Impl;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 9379d9110..f9ca2948e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -56,6 +56,8 @@ add_executable(yuzu
56 main.h 56 main.h
57 ui_settings.cpp 57 ui_settings.cpp
58 ui_settings.h 58 ui_settings.h
59 util/limitable_input_dialog.cpp
60 util/limitable_input_dialog.h
59 util/spinbox.cpp 61 util/spinbox.cpp
60 util/spinbox.h 62 util/spinbox.h
61 util/util.cpp 63 util/util.cpp
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index e8ab23326..39eef8858 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -8,7 +8,6 @@
8 8
9#include "common/microprofile.h" 9#include "common/microprofile.h"
10#include "common/scm_rev.h" 10#include "common/scm_rev.h"
11#include "common/string_util.h"
12#include "core/core.h" 11#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h" 12#include "core/frontend/framebuffer_layout.h"
14#include "core/settings.h" 13#include "core/settings.h"
@@ -107,9 +106,8 @@ private:
107GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 106GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
108 : QWidget(parent), child(nullptr), emu_thread(emu_thread) { 107 : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
109 108
110 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, 109 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
111 Common::g_scm_branch, Common::g_scm_desc); 110 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
112 setWindowTitle(QString::fromStdString(window_title));
113 setAttribute(Qt::WA_AcceptTouchEvents); 111 setAttribute(Qt::WA_AcceptTouchEvents);
114 112
115 InputCommon::Init(); 113 InputCommon::Init();
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 91e754274..5f0896f84 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -5,6 +5,7 @@
5#include <QButtonGroup> 5#include <QButtonGroup>
6#include <QMessageBox> 6#include <QMessageBox>
7#include <QPushButton> 7#include <QPushButton>
8#include <QtConcurrent/qtconcurrentrun.h>
8#include "common/logging/log.h" 9#include "common/logging/log.h"
9#include "common/telemetry.h" 10#include "common/telemetry.h"
10#include "core/core.h" 11#include "core/core.h"
@@ -23,6 +24,8 @@ CompatDB::CompatDB(QWidget* parent)
23 connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); 24 connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
24 connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); 25 connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
25 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); 26 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
27 connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
28 &CompatDB::OnTestcaseSubmitted);
26} 29}
27 30
28CompatDB::~CompatDB() = default; 31CompatDB::~CompatDB() = default;
@@ -48,18 +51,38 @@ void CompatDB::Submit() {
48 } 51 }
49 break; 52 break;
50 case CompatDBPage::Final: 53 case CompatDBPage::Final:
54 back();
51 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); 55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
52 Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", 56 Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
53 compatibility->checkedId()); 57 compatibility->checkedId());
54 // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a 58
55 // workaround 59 button(NextButton)->setEnabled(false);
60 button(NextButton)->setText(tr("Submitting"));
56 button(QWizard::CancelButton)->setVisible(false); 61 button(QWizard::CancelButton)->setVisible(false);
62
63 testcase_watcher.setFuture(QtConcurrent::run(
64 [this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
57 break; 65 break;
58 default: 66 default:
59 LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); 67 LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
60 } 68 }
61} 69}
62 70
71void CompatDB::OnTestcaseSubmitted() {
72 if (!testcase_watcher.result()) {
73 QMessageBox::critical(this, tr("Communication error"),
74 tr("An error occured while sending the Testcase"));
75 button(NextButton)->setEnabled(true);
76 button(NextButton)->setText(tr("Next"));
77 button(QWizard::CancelButton)->setVisible(true);
78 } else {
79 next();
80 // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
81 // workaround
82 button(QWizard::CancelButton)->setVisible(false);
83 }
84}
85
63void CompatDB::EnableNext() { 86void CompatDB::EnableNext() {
64 button(NextButton)->setEnabled(true); 87 button(NextButton)->setEnabled(true);
65} 88}
diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h
index ca0dd11d6..5381f67f7 100644
--- a/src/yuzu/compatdb.h
+++ b/src/yuzu/compatdb.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <QFutureWatcher>
8#include <QWizard> 9#include <QWizard>
9 10
10namespace Ui { 11namespace Ui {
@@ -19,8 +20,11 @@ public:
19 ~CompatDB(); 20 ~CompatDB();
20 21
21private: 22private:
23 QFutureWatcher<bool> testcase_watcher;
24
22 std::unique_ptr<Ui::CompatDB> ui; 25 std::unique_ptr<Ui::CompatDB> ui;
23 26
24 void Submit(); 27 void Submit();
28 void OnTestcaseSubmitted();
25 void EnableNext(); 29 void EnableNext();
26}; 30};
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 71c6ebb41..be69fb831 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -4,6 +4,7 @@
4 4
5#include <QSettings> 5#include <QSettings>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h"
7#include "input_common/main.h" 8#include "input_common/main.h"
8#include "yuzu/configuration/config.h" 9#include "yuzu/configuration/config.h"
9#include "yuzu/ui_settings.h" 10#include "yuzu/ui_settings.h"
@@ -12,11 +13,16 @@ Config::Config() {
12 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 13 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
13 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; 14 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
14 FileUtil::CreateFullPath(qt_config_loc); 15 FileUtil::CreateFullPath(qt_config_loc);
15 qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 16 qt_config =
17 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
16 18
17 Reload(); 19 Reload();
18} 20}
19 21
22Config::~Config() {
23 Save();
24}
25
20const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 26const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
21 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, 27 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
22 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, 28 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
@@ -122,8 +128,20 @@ void Config::ReadValues() {
122 128
123 qt_config->beginGroup("System"); 129 qt_config->beginGroup("System");
124 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 130 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
125 Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); 131 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
132
133 Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0,
134 Service::Account::MAX_USERS - 1);
135
126 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
127 qt_config->endGroup(); 145 qt_config->endGroup();
128 146
129 qt_config->beginGroup("Miscellaneous"); 147 qt_config->beginGroup("Miscellaneous");
@@ -135,6 +153,7 @@ void Config::ReadValues() {
135 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 153 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
136 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 154 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
137 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();
138 qt_config->endGroup(); 157 qt_config->endGroup();
139 158
140 qt_config->beginGroup("WebService"); 159 qt_config->beginGroup("WebService");
@@ -152,6 +171,7 @@ void Config::ReadValues() {
152 171
153 qt_config->beginGroup("UIGameList"); 172 qt_config->beginGroup("UIGameList");
154 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();
155 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); 175 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt();
156 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();
157 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();
@@ -258,8 +278,14 @@ void Config::SaveValues() {
258 278
259 qt_config->beginGroup("System"); 279 qt_config->beginGroup("System");
260 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 280 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
261 qt_config->setValue("username", QString::fromStdString(Settings::values.username)); 281 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
282 qt_config->setValue("current_user", Settings::values.current_user);
283
262 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
263 qt_config->endGroup(); 289 qt_config->endGroup();
264 290
265 qt_config->beginGroup("Miscellaneous"); 291 qt_config->beginGroup("Miscellaneous");
@@ -271,6 +297,7 @@ void Config::SaveValues() {
271 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 297 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
272 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 298 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
273 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);
274 qt_config->endGroup(); 301 qt_config->endGroup();
275 302
276 qt_config->beginGroup("WebService"); 303 qt_config->beginGroup("WebService");
@@ -286,6 +313,7 @@ void Config::SaveValues() {
286 313
287 qt_config->beginGroup("UIGameList"); 314 qt_config->beginGroup("UIGameList");
288 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);
289 qt_config->setValue("icon_size", UISettings::values.icon_size); 317 qt_config->setValue("icon_size", UISettings::values.icon_size);
290 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);
291 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);
@@ -335,9 +363,3 @@ void Config::Reload() {
335void Config::Save() { 363void Config::Save() {
336 SaveValues(); 364 SaveValues();
337} 365}
338
339Config::~Config() {
340 Save();
341
342 delete qt_config;
343}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index cbf745ea2..9c99c1b75 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <memory>
8#include <string> 9#include <string>
9#include <QVariant> 10#include <QVariant>
10#include "core/settings.h" 11#include "core/settings.h"
@@ -12,12 +13,6 @@
12class QSettings; 13class QSettings;
13 14
14class Config { 15class Config {
15 QSettings* qt_config;
16 std::string qt_config_loc;
17
18 void ReadValues();
19 void SaveValues();
20
21public: 16public:
22 Config(); 17 Config();
23 ~Config(); 18 ~Config();
@@ -27,4 +22,11 @@ public:
27 22
28 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
29 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
25
26private:
27 void ReadValues();
28 void SaveValues();
29
30 std::unique_ptr<QSettings> qt_config;
31 std::string qt_config_loc;
30}; 32};
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 8743ce982..ae8cac243 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -36,20 +36,36 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
36 InitializeRowComboBoxes(); 36 InitializeRowComboBoxes();
37 37
38 this->setConfiguration(); 38 this->setConfiguration();
39
40 // Force game list reload if any of the relevant settings are changed.
41 connect(ui->show_unknown, &QCheckBox::stateChanged, this,
42 &ConfigureGameList::RequestGameListUpdate);
43 connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
44 &ConfigureGameList::RequestGameListUpdate);
45 connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
46 &ConfigureGameList::RequestGameListUpdate);
47 connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
48 &ConfigureGameList::RequestGameListUpdate);
39} 49}
40 50
41ConfigureGameList::~ConfigureGameList() = default; 51ConfigureGameList::~ConfigureGameList() = default;
42 52
43void ConfigureGameList::applyConfiguration() { 53void ConfigureGameList::applyConfiguration() {
44 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();
45 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); 56 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
46 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();
47 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();
48 Settings::Apply(); 59 Settings::Apply();
49} 60}
50 61
62void ConfigureGameList::RequestGameListUpdate() {
63 UISettings::values.is_game_list_reload_pending.exchange(true);
64}
65
51void ConfigureGameList::setConfiguration() { 66void ConfigureGameList::setConfiguration() {
52 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);
53 ui->icon_size_combobox->setCurrentIndex( 69 ui->icon_size_combobox->setCurrentIndex(
54 ui->icon_size_combobox->findData(UISettings::values.icon_size)); 70 ui->icon_size_combobox->findData(UISettings::values.icon_size));
55 ui->row_1_text_combobox->setCurrentIndex( 71 ui->row_1_text_combobox->setCurrentIndex(
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h
index ff7406c60..bbf7e25f1 100644
--- a/src/yuzu/configuration/configure_gamelist.h
+++ b/src/yuzu/configuration/configure_gamelist.h
@@ -21,6 +21,8 @@ public:
21 void applyConfiguration(); 21 void applyConfiguration();
22 22
23private: 23private:
24 void RequestGameListUpdate();
25
24 void setConfiguration(); 26 void setConfiguration();
25 27
26 void changeEvent(QEvent*) override; 28 void changeEvent(QEvent*) override;
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 f5db9e55b..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"
@@ -19,8 +23,10 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
19 23
20 this->setConfiguration(); 24 this->setConfiguration();
21 25
26 connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
27 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
28
22 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 29 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
23 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
24} 30}
25 31
26ConfigureGeneral::~ConfigureGeneral() = default; 32ConfigureGeneral::~ConfigureGeneral() = default;
@@ -31,12 +37,40 @@ void ConfigureGeneral::setConfiguration() {
31 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 37 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
32 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 38 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
33 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 39 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
40 ui->enable_nfc->setChecked(Settings::values.enable_nfc);
34} 41}
35 42
36void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { 43void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
37 ui->widget->Populate(registry); 44 ui->widget->Populate(registry);
38} 45}
39 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
40void ConfigureGeneral::applyConfiguration() { 74void ConfigureGeneral::applyConfiguration() {
41 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 75 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
42 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 76 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -44,5 +78,9 @@ void ConfigureGeneral::applyConfiguration() {
44 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 78 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
45 79
46 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;
47 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
85 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
48} 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_general.ui b/src/yuzu/configuration/configure_general.ui
index 1775c4d40..b82fffde8 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -68,19 +68,26 @@
68 <property name="title"> 68 <property name="title">
69 <string>Emulation</string> 69 <string>Emulation</string>
70 </property> 70 </property>
71 <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> 71 <layout class="QHBoxLayout" name="EmulationHorizontalLayout">
72 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout">
74 <item>
75 <widget class="QCheckBox" name="use_docked_mode">
76 <property name="text">
77 <string>Enable docked mode</string>
78 </property>
79 </widget>
80 </item>
72 <item> 81 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout"> 82 <widget class="QCheckBox" name="enable_nfc">
74 <item> 83 <property name="text">
75 <widget class="QCheckBox" name="use_docked_mode"> 84 <string>Enable NFC</string>
76 <property name="text"> 85 </property>
77 <string>Enable docked mode</string> 86 </widget>
78 </property>
79 </widget>
80 </item>
81 </layout>
82 </item> 87 </item>
83 </layout> 88 </layout>
89 </item>
90 </layout>
84 </widget> 91 </widget>
85 </item> 92 </item>
86 <item> 93 <item>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 94789c064..42a7beac6 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -322,7 +322,7 @@ void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool a
322 } 322 }
323 323
324 updateButtonLabels(); 324 updateButtonLabels();
325 input_setter = boost::none; 325 input_setter = {};
326} 326}
327 327
328void ConfigureInput::keyPressEvent(QKeyEvent* event) { 328void ConfigureInput::keyPressEvent(QKeyEvent* event) {
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index d1198db81..32c7183f9 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,11 +7,13 @@
7#include <array> 7#include <array>
8#include <functional> 8#include <functional>
9#include <memory> 9#include <memory>
10#include <optional>
10#include <string> 11#include <string>
11#include <unordered_map> 12#include <unordered_map>
13
12#include <QKeyEvent> 14#include <QKeyEvent>
13#include <QWidget> 15#include <QWidget>
14#include <boost/optional.hpp> 16
15#include "common/param_package.h" 17#include "common/param_package.h"
16#include "core/settings.h" 18#include "core/settings.h"
17#include "input_common/main.h" 19#include "input_common/main.h"
@@ -41,7 +43,7 @@ private:
41 std::unique_ptr<QTimer> poll_timer; 43 std::unique_ptr<QTimer> poll_timer;
42 44
43 /// This will be the the setting function when an input is awaiting configuration. 45 /// This will be the the setting function when an input is awaiting configuration.
44 boost::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 46 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
45 47
46 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 48 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
47 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 49 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index e9ed9c38f..ab5d46492 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -2,14 +2,27 @@
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 <algorithm>
6#include <QFileDialog>
7#include <QGraphicsItem>
8#include <QGraphicsScene>
9#include <QHeaderView>
5#include <QMessageBox> 10#include <QMessageBox>
11#include <QStandardItemModel>
12#include <QTreeView>
13#include <QVBoxLayout>
14#include "common/assert.h"
15#include "common/file_util.h"
16#include "common/string_util.h"
6#include "core/core.h" 17#include "core/core.h"
18#include "core/hle/service/acc/profile_manager.h"
7#include "core/settings.h" 19#include "core/settings.h"
8#include "ui_configure_system.h" 20#include "ui_configure_system.h"
9#include "yuzu/configuration/configure_system.h" 21#include "yuzu/configuration/configure_system.h"
10#include "yuzu/main.h" 22#include "yuzu/util/limitable_input_dialog.h"
11 23
12static const std::array<int, 12> days_in_month = {{ 24namespace {
25constexpr std::array<int, 12> days_in_month = {{
13 31, 26 31,
14 29, 27 29,
15 31, 28 31,
@@ -24,13 +37,114 @@ static const std::array<int, 12> days_in_month = {{
24 31, 37 31,
25}}; 38}};
26 39
27ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { 40// Same backup JPEG used by acc IProfile::GetImage if no jpeg found
41constexpr std::array<u8, 107> backup_jpeg{
42 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
43 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
44 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
45 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
46 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
47 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
48 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
49};
50
51QString GetImagePath(Service::Account::UUID uuid) {
52 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
53 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
54 return QString::fromStdString(path);
55}
56
57QString GetAccountUsername(const Service::Account::ProfileManager& manager,
58 Service::Account::UUID uuid) {
59 Service::Account::ProfileBase profile;
60 if (!manager.GetProfileBase(uuid, profile)) {
61 return {};
62 }
63
64 const auto text = Common::StringFromFixedZeroTerminatedBuffer(
65 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
66 return QString::fromStdString(text);
67}
68
69QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) {
70 return ConfigureSystem::tr("%1\n%2",
71 "%1 is the profile username, %2 is the formatted UUID (e.g. "
72 "00112233-4455-6677-8899-AABBCCDDEEFF))")
73 .arg(username, QString::fromStdString(uuid.FormatSwitch()));
74}
75
76QPixmap GetIcon(Service::Account::UUID uuid) {
77 QPixmap icon{GetImagePath(uuid)};
78
79 if (!icon) {
80 icon.fill(Qt::black);
81 icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size()));
82 }
83
84 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
85}
86
87QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) {
88 return LimitableInputDialog::GetText(parent, ConfigureSystem::tr("Enter Username"),
89 description_text, 1,
90 static_cast<int>(Service::Account::profile_username_size));
91}
92} // Anonymous namespace
93
94ConfigureSystem::ConfigureSystem(QWidget* parent)
95 : QWidget(parent), ui(new Ui::ConfigureSystem),
96 profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
28 ui->setupUi(this); 97 ui->setupUi(this);
29 connect(ui->combo_birthmonth, 98 connect(ui->combo_birthmonth,
30 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 99 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
31 &ConfigureSystem::updateBirthdayComboBox); 100 &ConfigureSystem::UpdateBirthdayComboBox);
32 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, 101 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
33 &ConfigureSystem::refreshConsoleID); 102 &ConfigureSystem::RefreshConsoleID);
103
104 layout = new QVBoxLayout;
105 tree_view = new QTreeView;
106 item_model = new QStandardItemModel(tree_view);
107 tree_view->setModel(item_model);
108
109 tree_view->setAlternatingRowColors(true);
110 tree_view->setSelectionMode(QHeaderView::SingleSelection);
111 tree_view->setSelectionBehavior(QHeaderView::SelectRows);
112 tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
113 tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
114 tree_view->setSortingEnabled(true);
115 tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
116 tree_view->setUniformRowHeights(true);
117 tree_view->setIconSize({64, 64});
118 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
119
120 item_model->insertColumns(0, 1);
121 item_model->setHeaderData(0, Qt::Horizontal, "Users");
122
123 // We must register all custom types with the Qt Automoc system so that we are able to use it
124 // with signals/slots. In this case, QList falls under the umbrells of custom types.
125 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
126
127 layout->setContentsMargins(0, 0, 0, 0);
128 layout->setSpacing(0);
129 layout->addWidget(tree_view);
130
131 ui->scrollArea->setLayout(layout);
132
133 connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser);
134
135 connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser);
136 connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser);
137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
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
146 scene = new QGraphicsScene;
147 ui->current_user_icon->setScene(scene);
34 148
35 this->setConfiguration(); 149 this->setConfiguration();
36} 150}
@@ -39,8 +153,52 @@ ConfigureSystem::~ConfigureSystem() = default;
39 153
40void ConfigureSystem::setConfiguration() { 154void ConfigureSystem::setConfiguration() {
41 enabled = !Core::System::GetInstance().IsPoweredOn(); 155 enabled = !Core::System::GetInstance().IsPoweredOn();
42 ui->edit_username->setText(QString::fromStdString(Settings::values.username)); 156
43 ui->combo_language->setCurrentIndex(Settings::values.language_index); 157 ui->combo_language->setCurrentIndex(Settings::values.language_index);
158
159 item_model->removeRows(0, item_model->rowCount());
160 list_items.clear();
161
162 PopulateUserList();
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);
171}
172
173void ConfigureSystem::PopulateUserList() {
174 const auto& profiles = profile_manager->GetAllUsers();
175 for (const auto& user : profiles) {
176 Service::Account::ProfileBase profile;
177 if (!profile_manager->GetProfileBase(user, profile))
178 continue;
179
180 const auto username = Common::StringFromFixedZeroTerminatedBuffer(
181 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
182
183 list_items.push_back(QList<QStandardItem*>{new QStandardItem{
184 GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}});
185 }
186
187 for (const auto& item : list_items)
188 item_model->appendRow(item);
189}
190
191void ConfigureSystem::UpdateCurrentUser() {
192 ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS);
193
194 const auto& current_user = profile_manager->GetUser(Settings::values.current_user);
195 ASSERT(current_user);
196 const auto username = GetAccountUsername(*profile_manager, *current_user);
197
198 scene->clear();
199 scene->addPixmap(
200 GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
201 ui->current_user_username->setText(username);
44} 202}
45 203
46void ConfigureSystem::ReadSystemSettings() {} 204void ConfigureSystem::ReadSystemSettings() {}
@@ -48,12 +206,18 @@ void ConfigureSystem::ReadSystemSettings() {}
48void ConfigureSystem::applyConfiguration() { 206void ConfigureSystem::applyConfiguration() {
49 if (!enabled) 207 if (!enabled)
50 return; 208 return;
51 Settings::values.username = ui->edit_username->text().toStdString(); 209
52 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
53 Settings::Apply(); 217 Settings::Apply();
54} 218}
55 219
56void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { 220void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
57 if (birthmonth_index < 0 || birthmonth_index >= 12) 221 if (birthmonth_index < 0 || birthmonth_index >= 12)
58 return; 222 return;
59 223
@@ -78,7 +242,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) {
78 ui->combo_birthday->setCurrentIndex(birthday_index); 242 ui->combo_birthday->setCurrentIndex(birthday_index);
79} 243}
80 244
81void ConfigureSystem::refreshConsoleID() { 245void ConfigureSystem::RefreshConsoleID() {
82 QMessageBox::StandardButton reply; 246 QMessageBox::StandardButton reply;
83 QString warning_text = tr("This will replace your current virtual Switch with a new one. " 247 QString warning_text = tr("This will replace your current virtual Switch with a new one. "
84 "Your current virtual Switch will not be recoverable. " 248 "Your current virtual Switch will not be recoverable. "
@@ -92,3 +256,130 @@ void ConfigureSystem::refreshConsoleID() {
92 ui->label_console_id->setText( 256 ui->label_console_id->setText(
93 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); 257 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
94} 258}
259
260void ConfigureSystem::SelectUser(const QModelIndex& index) {
261 Settings::values.current_user =
262 std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1));
263
264 UpdateCurrentUser();
265
266 ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2);
267 ui->pm_rename->setEnabled(true);
268 ui->pm_set_image->setEnabled(true);
269}
270
271void ConfigureSystem::AddUser() {
272 const auto username =
273 GetProfileUsernameFromUser(this, tr("Enter a username for the new user:"));
274 if (username.isEmpty()) {
275 return;
276 }
277
278 const auto uuid = Service::Account::UUID::Generate();
279 profile_manager->CreateNewUser(uuid, username.toStdString());
280
281 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
282}
283
284void ConfigureSystem::RenameUser() {
285 const auto user = tree_view->currentIndex().row();
286 const auto uuid = profile_manager->GetUser(user);
287 ASSERT(uuid);
288
289 Service::Account::ProfileBase profile;
290 if (!profile_manager->GetProfileBase(*uuid, profile))
291 return;
292
293 const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:"));
294 if (new_username.isEmpty()) {
295 return;
296 }
297
298 const auto username_std = new_username.toStdString();
299 std::fill(profile.username.begin(), profile.username.end(), '\0');
300 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
301
302 profile_manager->SetProfileBase(*uuid, profile);
303
304 item_model->setItem(
305 user, 0,
306 new QStandardItem{GetIcon(*uuid),
307 FormatUserEntryText(QString::fromStdString(username_std), *uuid)});
308 UpdateCurrentUser();
309}
310
311void ConfigureSystem::DeleteUser() {
312 const auto index = tree_view->currentIndex().row();
313 const auto uuid = profile_manager->GetUser(index);
314 ASSERT(uuid);
315 const auto username = GetAccountUsername(*profile_manager, *uuid);
316
317 const auto confirm = QMessageBox::question(
318 this, tr("Confirm Delete"),
319 tr("You are about to delete user with name \"%1\". Are you sure?").arg(username));
320
321 if (confirm == QMessageBox::No)
322 return;
323
324 if (Settings::values.current_user == tree_view->currentIndex().row())
325 Settings::values.current_user = 0;
326 UpdateCurrentUser();
327
328 if (!profile_manager->RemoveUser(*uuid))
329 return;
330
331 item_model->removeRows(tree_view->currentIndex().row(), 1);
332 tree_view->clearSelection();
333
334 ui->pm_remove->setEnabled(false);
335 ui->pm_rename->setEnabled(false);
336}
337
338void ConfigureSystem::SetUserImage() {
339 const auto index = tree_view->currentIndex().row();
340 const auto uuid = profile_manager->GetUser(index);
341 ASSERT(uuid);
342
343 const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
344 tr("JPEG Images (*.jpg *.jpeg)"));
345
346 if (file.isEmpty()) {
347 return;
348 }
349
350 const auto image_path = GetImagePath(*uuid);
351 if (QFile::exists(image_path) && !QFile::remove(image_path)) {
352 QMessageBox::warning(
353 this, tr("Error deleting image"),
354 tr("Error occurred attempting to overwrite previous image at: %1.").arg(image_path));
355 return;
356 }
357
358 const auto raw_path = QString::fromStdString(
359 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010");
360 const QFileInfo raw_info{raw_path};
361 if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) {
362 QMessageBox::warning(this, tr("Error deleting file"),
363 tr("Unable to delete existing file: %1.").arg(raw_path));
364 return;
365 }
366
367 const QString absolute_dst_path = QFileInfo{image_path}.absolutePath();
368 if (!QDir{raw_path}.mkpath(absolute_dst_path)) {
369 QMessageBox::warning(
370 this, tr("Error creating user image directory"),
371 tr("Unable to create directory %1 for storing user images.").arg(absolute_dst_path));
372 return;
373 }
374
375 if (!QFile::copy(file, image_path)) {
376 QMessageBox::warning(this, tr("Error copying user image"),
377 tr("Unable to copy image from %1 to %2").arg(file, image_path));
378 return;
379 }
380
381 const auto username = GetAccountUsername(*profile_manager, *uuid);
382 item_model->setItem(index, 0,
383 new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
384 UpdateCurrentUser();
385}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index f13de17d4..07764e1f7 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -5,8 +5,20 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8
9#include <QList>
8#include <QWidget> 10#include <QWidget>
9 11
12class QGraphicsScene;
13class QStandardItem;
14class QStandardItemModel;
15class QTreeView;
16class QVBoxLayout;
17
18namespace Service::Account {
19class ProfileManager;
20}
21
10namespace Ui { 22namespace Ui {
11class ConfigureSystem; 23class ConfigureSystem;
12} 24}
@@ -16,23 +28,39 @@ class ConfigureSystem : public QWidget {
16 28
17public: 29public:
18 explicit ConfigureSystem(QWidget* parent = nullptr); 30 explicit ConfigureSystem(QWidget* parent = nullptr);
19 ~ConfigureSystem(); 31 ~ConfigureSystem() override;
20 32
21 void applyConfiguration(); 33 void applyConfiguration();
22 void setConfiguration(); 34 void setConfiguration();
23 35
24public slots:
25 void updateBirthdayComboBox(int birthmonth_index);
26 void refreshConsoleID();
27
28private: 36private:
29 void ReadSystemSettings(); 37 void ReadSystemSettings();
30 38
39 void UpdateBirthdayComboBox(int birthmonth_index);
40 void RefreshConsoleID();
41
42 void PopulateUserList();
43 void UpdateCurrentUser();
44 void SelectUser(const QModelIndex& index);
45 void AddUser();
46 void RenameUser();
47 void DeleteUser();
48 void SetUserImage();
49
50 QVBoxLayout* layout;
51 QTreeView* tree_view;
52 QStandardItemModel* item_model;
53 QGraphicsScene* scene;
54
55 std::vector<QList<QStandardItem*>> list_items;
56
31 std::unique_ptr<Ui::ConfigureSystem> ui; 57 std::unique_ptr<Ui::ConfigureSystem> ui;
32 bool enabled; 58 bool enabled = false;
59
60 int birthmonth = 0;
61 int birthday = 0;
62 int language_index = 0;
63 int sound_index = 0;
33 64
34 std::u16string username; 65 std::unique_ptr<Service::Account::ProfileManager> profile_manager;
35 int birthmonth, birthday;
36 int language_index;
37 int sound_index;
38}; 66};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index f3f8db038..a91580893 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -6,8 +6,8 @@
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>377</height> 10 <height>483</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -22,34 +22,120 @@
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="0" column="0"> 25 <item row="1" column="1">
26 <widget class="QLabel" name="label_username"> 26 <widget class="QComboBox" name="combo_language">
27 <property name="text"> 27 <property name="toolTip">
28 <string>Username</string> 28 <string>Note: this can be overridden when region setting is auto-select</string>
29 </property> 29 </property>
30 <item>
31 <property name="text">
32 <string>Japanese (日本語)</string>
33 </property>
34 </item>
35 <item>
36 <property name="text">
37 <string>English</string>
38 </property>
39 </item>
40 <item>
41 <property name="text">
42 <string>French (français)</string>
43 </property>
44 </item>
45 <item>
46 <property name="text">
47 <string>German (Deutsch)</string>
48 </property>
49 </item>
50 <item>
51 <property name="text">
52 <string>Italian (italiano)</string>
53 </property>
54 </item>
55 <item>
56 <property name="text">
57 <string>Spanish (español)</string>
58 </property>
59 </item>
60 <item>
61 <property name="text">
62 <string>Chinese</string>
63 </property>
64 </item>
65 <item>
66 <property name="text">
67 <string>Korean (한국어)</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string>Dutch (Nederlands)</string>
73 </property>
74 </item>
75 <item>
76 <property name="text">
77 <string>Portuguese (português)</string>
78 </property>
79 </item>
80 <item>
81 <property name="text">
82 <string>Russian (Русский)</string>
83 </property>
84 </item>
85 <item>
86 <property name="text">
87 <string>Taiwanese</string>
88 </property>
89 </item>
90 <item>
91 <property name="text">
92 <string>British English</string>
93 </property>
94 </item>
95 <item>
96 <property name="text">
97 <string>Canadian French</string>
98 </property>
99 </item>
100 <item>
101 <property name="text">
102 <string>Latin American Spanish</string>
103 </property>
104 </item>
105 <item>
106 <property name="text">
107 <string>Simplified Chinese</string>
108 </property>
109 </item>
110 <item>
111 <property name="text">
112 <string>Traditional Chinese (正體中文)</string>
113 </property>
114 </item>
30 </widget> 115 </widget>
31 </item> 116 </item>
32 <item row="0" column="1"> 117 <item row="3" column="0">
33 <widget class="QLineEdit" name="edit_username"> 118 <widget class="QLabel" name="label_console_id">
34 <property name="sizePolicy"> 119 <property name="text">
35 <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> 120 <string>Console ID:</string>
36 <horstretch>0</horstretch>
37 <verstretch>0</verstretch>
38 </sizepolicy>
39 </property> 121 </property>
40 <property name="maxLength"> 122 </widget>
41 <number>32</number> 123 </item>
124 <item row="2" column="0">
125 <widget class="QLabel" name="label_sound">
126 <property name="text">
127 <string>Sound output mode</string>
42 </property> 128 </property>
43 </widget> 129 </widget>
44 </item> 130 </item>
45 <item row="1" column="0"> 131 <item row="0" column="0">
46 <widget class="QLabel" name="label_birthday"> 132 <widget class="QLabel" name="label_birthday">
47 <property name="text"> 133 <property name="text">
48 <string>Birthday</string> 134 <string>Birthday</string>
49 </property> 135 </property>
50 </widget> 136 </widget>
51 </item> 137 </item>
52 <item row="1" column="1"> 138 <item row="0" column="1">
53 <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> 139 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
54 <item> 140 <item>
55 <widget class="QComboBox" name="combo_birthmonth"> 141 <widget class="QComboBox" name="combo_birthmonth">
@@ -120,113 +206,23 @@
120 </item> 206 </item>
121 </layout> 207 </layout>
122 </item> 208 </item>
123 <item row="2" column="0"> 209 <item row="3" column="1">
124 <widget class="QLabel" name="label_language"> 210 <widget class="QPushButton" name="button_regenerate_console_id">
125 <property name="text"> 211 <property name="sizePolicy">
126 <string>Language</string> 212 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
213 <horstretch>0</horstretch>
214 <verstretch>0</verstretch>
215 </sizepolicy>
127 </property> 216 </property>
128 </widget> 217 <property name="layoutDirection">
129 </item> 218 <enum>Qt::RightToLeft</enum>
130 <item row="2" column="1">
131 <widget class="QComboBox" name="combo_language">
132 <property name="toolTip">
133 <string>Note: this can be overridden when region setting is auto-select</string>
134 </property> 219 </property>
135 <item>
136 <property name="text">
137 <string>Japanese (日本語)</string>
138 </property>
139 </item>
140 <item>
141 <property name="text">
142 <string>English</string>
143 </property>
144 </item>
145 <item>
146 <property name="text">
147 <string>French (français)</string>
148 </property>
149 </item>
150 <item>
151 <property name="text">
152 <string>German (Deutsch)</string>
153 </property>
154 </item>
155 <item>
156 <property name="text">
157 <string>Italian (italiano)</string>
158 </property>
159 </item>
160 <item>
161 <property name="text">
162 <string>Spanish (español)</string>
163 </property>
164 </item>
165 <item>
166 <property name="text">
167 <string>Chinese</string>
168 </property>
169 </item>
170 <item>
171 <property name="text">
172 <string>Korean (한국어)</string>
173 </property>
174 </item>
175 <item>
176 <property name="text">
177 <string>Dutch (Nederlands)</string>
178 </property>
179 </item>
180 <item>
181 <property name="text">
182 <string>Portuguese (português)</string>
183 </property>
184 </item>
185 <item>
186 <property name="text">
187 <string>Russian (Русский)</string>
188 </property>
189 </item>
190 <item>
191 <property name="text">
192 <string>Taiwanese</string>
193 </property>
194 </item>
195 <item>
196 <property name="text">
197 <string>British English</string>
198 </property>
199 </item>
200 <item>
201 <property name="text">
202 <string>Canadian French</string>
203 </property>
204 </item>
205 <item>
206 <property name="text">
207 <string>Latin American Spanish</string>
208 </property>
209 </item>
210 <item>
211 <property name="text">
212 <string>Simplified Chinese</string>
213 </property>
214 </item>
215 <item>
216 <property name="text">
217 <string>Traditional Chinese (正體中文)</string>
218 </property>
219 </item>
220 </widget>
221 </item>
222 <item row="3" column="0">
223 <widget class="QLabel" name="label_sound">
224 <property name="text"> 220 <property name="text">
225 <string>Sound output mode</string> 221 <string>Regenerate</string>
226 </property> 222 </property>
227 </widget> 223 </widget>
228 </item> 224 </item>
229 <item row="3" column="1"> 225 <item row="2" column="1">
230 <widget class="QComboBox" name="combo_sound"> 226 <widget class="QComboBox" name="combo_sound">
231 <item> 227 <item>
232 <property name="text"> 228 <property name="text">
@@ -245,26 +241,38 @@
245 </item> 241 </item>
246 </widget> 242 </widget>
247 </item> 243 </item>
244 <item row="1" column="0">
245 <widget class="QLabel" name="label_language">
246 <property name="text">
247 <string>Language</string>
248 </property>
249 </widget>
250 </item>
248 <item row="4" column="0"> 251 <item row="4" column="0">
249 <widget class="QLabel" name="label_console_id"> 252 <widget class="QCheckBox" name="rng_seed_checkbox">
250 <property name="text"> 253 <property name="text">
251 <string>Console ID:</string> 254 <string>RNG Seed</string>
252 </property> 255 </property>
253 </widget> 256 </widget>
254 </item> 257 </item>
255 <item row="4" column="1"> 258 <item row="4" column="1">
256 <widget class="QPushButton" name="button_regenerate_console_id"> 259 <widget class="QLineEdit" name="rng_seed_edit">
257 <property name="sizePolicy"> 260 <property name="sizePolicy">
258 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 261 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
259 <horstretch>0</horstretch> 262 <horstretch>0</horstretch>
260 <verstretch>0</verstretch> 263 <verstretch>0</verstretch>
261 </sizepolicy> 264 </sizepolicy>
262 </property> 265 </property>
263 <property name="layoutDirection"> 266 <property name="font">
264 <enum>Qt::RightToLeft</enum> 267 <font>
268 <family>Lucida Console</family>
269 </font>
265 </property> 270 </property>
266 <property name="text"> 271 <property name="inputMask">
267 <string>Regenerate</string> 272 <string notr="true">HHHHHHHH</string>
273 </property>
274 <property name="maxLength">
275 <number>8</number>
268 </property> 276 </property>
269 </widget> 277 </widget>
270 </item> 278 </item>
@@ -272,6 +280,143 @@
272 </widget> 280 </widget>
273 </item> 281 </item>
274 <item> 282 <item>
283 <widget class="QGroupBox" name="gridGroupBox">
284 <property name="title">
285 <string>Profile Manager</string>
286 </property>
287 <layout class="QGridLayout" name="gridLayout_2">
288 <property name="sizeConstraint">
289 <enum>QLayout::SetNoConstraint</enum>
290 </property>
291 <item row="0" column="0">
292 <layout class="QHBoxLayout" name="horizontalLayout_2">
293 <item>
294 <widget class="QLabel" name="label">
295 <property name="sizePolicy">
296 <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
297 <horstretch>0</horstretch>
298 <verstretch>0</verstretch>
299 </sizepolicy>
300 </property>
301 <property name="text">
302 <string>Current User</string>
303 </property>
304 </widget>
305 </item>
306 <item>
307 <widget class="QGraphicsView" name="current_user_icon">
308 <property name="minimumSize">
309 <size>
310 <width>48</width>
311 <height>48</height>
312 </size>
313 </property>
314 <property name="maximumSize">
315 <size>
316 <width>48</width>
317 <height>48</height>
318 </size>
319 </property>
320 <property name="verticalScrollBarPolicy">
321 <enum>Qt::ScrollBarAlwaysOff</enum>
322 </property>
323 <property name="horizontalScrollBarPolicy">
324 <enum>Qt::ScrollBarAlwaysOff</enum>
325 </property>
326 <property name="interactive">
327 <bool>false</bool>
328 </property>
329 </widget>
330 </item>
331 <item>
332 <widget class="QLabel" name="current_user_username">
333 <property name="sizePolicy">
334 <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
335 <horstretch>0</horstretch>
336 <verstretch>0</verstretch>
337 </sizepolicy>
338 </property>
339 <property name="text">
340 <string>Username</string>
341 </property>
342 </widget>
343 </item>
344 </layout>
345 </item>
346 <item row="1" column="0">
347 <widget class="QScrollArea" name="scrollArea">
348 <property name="sizePolicy">
349 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
350 <horstretch>0</horstretch>
351 <verstretch>0</verstretch>
352 </sizepolicy>
353 </property>
354 <property name="frameShape">
355 <enum>QFrame::StyledPanel</enum>
356 </property>
357 <property name="widgetResizable">
358 <bool>false</bool>
359 </property>
360 </widget>
361 </item>
362 <item row="2" column="0">
363 <layout class="QHBoxLayout" name="horizontalLayout_3">
364 <item>
365 <widget class="QPushButton" name="pm_set_image">
366 <property name="enabled">
367 <bool>false</bool>
368 </property>
369 <property name="text">
370 <string>Set Image</string>
371 </property>
372 </widget>
373 </item>
374 <item>
375 <spacer name="horizontalSpacer">
376 <property name="orientation">
377 <enum>Qt::Horizontal</enum>
378 </property>
379 <property name="sizeHint" stdset="0">
380 <size>
381 <width>40</width>
382 <height>20</height>
383 </size>
384 </property>
385 </spacer>
386 </item>
387 <item>
388 <widget class="QPushButton" name="pm_add">
389 <property name="text">
390 <string>Add</string>
391 </property>
392 </widget>
393 </item>
394 <item>
395 <widget class="QPushButton" name="pm_rename">
396 <property name="enabled">
397 <bool>false</bool>
398 </property>
399 <property name="text">
400 <string>Rename</string>
401 </property>
402 </widget>
403 </item>
404 <item>
405 <widget class="QPushButton" name="pm_remove">
406 <property name="enabled">
407 <bool>false</bool>
408 </property>
409 <property name="text">
410 <string>Remove</string>
411 </property>
412 </widget>
413 </item>
414 </layout>
415 </item>
416 </layout>
417 </widget>
418 </item>
419 <item>
275 <widget class="QLabel" name="label_disable_info"> 420 <widget class="QLabel" name="label_disable_info">
276 <property name="text"> 421 <property name="text">
277 <string>System settings are available only when game is not running.</string> 422 <string>System settings are available only when game is not running.</string>
@@ -281,19 +426,6 @@
281 </property> 426 </property>
282 </widget> 427 </widget>
283 </item> 428 </item>
284 <item>
285 <spacer name="verticalSpacer">
286 <property name="orientation">
287 <enum>Qt::Vertical</enum>
288 </property>
289 <property name="sizeHint" stdset="0">
290 <size>
291 <width>20</width>
292 <height>40</height>
293 </size>
294 </property>
295 </spacer>
296 </item>
297 </layout> 429 </layout>
298 </item> 430 </item>
299 </layout> 431 </layout>
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
index b5c88f944..67ed0ba6d 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
@@ -2,7 +2,6 @@
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 <map>
6#include <QLabel> 5#include <QLabel>
7#include <QMetaType> 6#include <QMetaType>
8#include <QPushButton> 7#include <QPushButton>
@@ -32,21 +31,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
32 switch (role) { 31 switch (role) {
33 case Qt::DisplayRole: { 32 case Qt::DisplayRole: {
34 if (index.column() == 0) { 33 if (index.column() == 0) {
35 static const std::map<Tegra::DebugContext::Event, QString> map = { 34 return DebugContextEventToString(event);
36 {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")},
37 {Tegra::DebugContext::Event::MaxwellCommandProcessed,
38 tr("Maxwell command processed")},
39 {Tegra::DebugContext::Event::IncomingPrimitiveBatch,
40 tr("Incoming primitive batch")},
41 {Tegra::DebugContext::Event::FinishedPrimitiveBatch,
42 tr("Finished primitive batch")},
43 };
44
45 DEBUG_ASSERT(map.size() ==
46 static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents));
47 return (map.find(event) != map.end()) ? map.at(event) : QString();
48 } 35 }
49
50 break; 36 break;
51 } 37 }
52 38
@@ -128,6 +114,23 @@ void BreakPointModel::OnResumed() {
128 active_breakpoint = context->active_breakpoint; 114 active_breakpoint = context->active_breakpoint;
129} 115}
130 116
117QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) {
118 switch (event) {
119 case Tegra::DebugContext::Event::MaxwellCommandLoaded:
120 return tr("Maxwell command loaded");
121 case Tegra::DebugContext::Event::MaxwellCommandProcessed:
122 return tr("Maxwell command processed");
123 case Tegra::DebugContext::Event::IncomingPrimitiveBatch:
124 return tr("Incoming primitive batch");
125 case Tegra::DebugContext::Event::FinishedPrimitiveBatch:
126 return tr("Finished primitive batch");
127 case Tegra::DebugContext::Event::NumEvents:
128 break;
129 }
130
131 return tr("Unknown debug context event");
132}
133
131GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( 134GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
132 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) 135 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
133 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( 136 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
index 7112b87e6..fb488e38f 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
@@ -29,6 +29,8 @@ public:
29 void OnResumed(); 29 void OnResumed();
30 30
31private: 31private:
32 static QString DebugContextEventToString(Tegra::DebugContext::Event event);
33
32 std::weak_ptr<Tegra::DebugContext> context_weak; 34 std::weak_ptr<Tegra::DebugContext> context_weak;
33 bool at_breakpoint; 35 bool at_breakpoint;
34 Tegra::DebugContext::Event active_breakpoint; 36 Tegra::DebugContext::Event active_breakpoint;
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 44d423da2..707747422 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -382,13 +382,13 @@ void GraphicsSurfaceWidget::OnUpdate() {
382 // TODO: Implement a good way to visualize alpha components! 382 // TODO: Implement a good way to visualize alpha components!
383 383
384 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); 384 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
385 boost::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); 385 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
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);
@@ -444,7 +444,7 @@ void GraphicsSurfaceWidget::SaveSurface() {
444 pixmap->save(&file, "PNG"); 444 pixmap->save(&file, "PNG");
445 } else if (selectedFilter == bin_filter) { 445 } else if (selectedFilter == bin_filter) {
446 auto& gpu = Core::System::GetInstance().GPU(); 446 auto& gpu = Core::System::GetInstance().GPU();
447 boost::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); 447 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
448 448
449 const u8* buffer = Memory::GetPointer(*address); 449 const u8* buffer = Memory::GetPointer(*address);
450 ASSERT_MSG(buffer != nullptr, "Memory not accessible"); 450 ASSERT_MSG(buffer != nullptr, "Memory not accessible");
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index defbf734f..331f89885 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -11,7 +11,6 @@
11#include <QAbstractItemModel> 11#include <QAbstractItemModel>
12#include <QDockWidget> 12#include <QDockWidget>
13#include <QTreeView> 13#include <QTreeView>
14#include <boost/container/flat_set.hpp>
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "core/hle/kernel/object.h" 15#include "core/hle/kernel/object.h"
17 16
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 67890455a..11a8c390b 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -16,7 +16,6 @@
16#include <fmt/format.h> 16#include <fmt/format.h>
17#include "common/common_paths.h" 17#include "common/common_paths.h"
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "common/file_util.h"
20#include "common/logging/log.h" 19#include "common/logging/log.h"
21#include "core/file_sys/patch_manager.h" 20#include "core/file_sys/patch_manager.h"
22#include "yuzu/compatibility_list.h" 21#include "yuzu/compatibility_list.h"
@@ -216,12 +215,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
216 tree_view->setUniformRowHeights(true); 215 tree_view->setUniformRowHeights(true);
217 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
218 217
219 item_model->insertColumns(0, COLUMN_COUNT); 218 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
220 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); 219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
221 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); 220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
222 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); 221
223 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); 222 if (UISettings::values.show_add_ons) {
224 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "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 }
225 230
226 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 231 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
227 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 232 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
@@ -387,14 +392,33 @@ void GameList::LoadCompatibilityList() {
387} 392}
388 393
389void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { 394void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
390 if (!FileUtil::Exists(dir_path.toStdString()) || 395 const QFileInfo dir_info{dir_path};
391 !FileUtil::IsDirectory(dir_path.toStdString())) { 396 if (!dir_info.exists() || !dir_info.isDir()) {
392 LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); 397 LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString());
393 search_field->setFilterResult(0, 0); 398 search_field->setFilterResult(0, 0);
394 return; 399 return;
395 } 400 }
396 401
397 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
398 // Delete any rows that might already exist if we're repopulating 422 // Delete any rows that might already exist if we're repopulating
399 item_model->removeRows(0, item_model->rowCount()); 423 item_model->removeRows(0, item_model->rowCount());
400 424
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 3881aba5f..362902e46 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -62,19 +62,24 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
62 FileSys::VirtualFile update_raw; 62 FileSys::VirtualFile update_raw;
63 loader.ReadUpdateRaw(update_raw); 63 loader.ReadUpdateRaw(update_raw);
64 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { 64 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) {
65 if (!updatable && kv.first == "Update") 65 const bool is_update = kv.first == "Update";
66 if (!updatable && is_update) {
66 continue; 67 continue;
68 }
69
70 const QString type = QString::fromStdString(kv.first);
67 71
68 if (kv.second.empty()) { 72 if (kv.second.empty()) {
69 out.append(fmt::format("{}\n", kv.first).c_str()); 73 out.append(QStringLiteral("%1\n").arg(type));
70 } else { 74 } else {
71 auto ver = kv.second; 75 auto ver = kv.second;
72 76
73 // Display container name for packed updates 77 // Display container name for packed updates
74 if (ver == "PACKED" && kv.first == "Update") 78 if (is_update && ver == "PACKED") {
75 ver = Loader::GetFileTypeString(loader.GetFileType()); 79 ver = Loader::GetFileTypeString(loader.GetFileType());
80 }
76 81
77 out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); 82 out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver)));
78 } 83 }
79 } 84 }
80 85
@@ -118,17 +123,22 @@ void GameListWorker::AddInstalledTitlesToGameList() {
118 if (it != compatibility_list.end()) 123 if (it != compatibility_list.end())
119 compatibility = it->second.first; 124 compatibility = it->second.first;
120 125
121 emit EntryReady({ 126 QList<QStandardItem*> list{
122 new GameListItemPath( 127 new GameListItemPath(
123 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), 128 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
124 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
125 program_id), 130 program_id),
126 new GameListItemCompat(compatibility), 131 new GameListItemCompat(compatibility),
127 new GameListItem(FormatPatchNameVersions(patch, *loader)),
128 new GameListItem( 132 new GameListItem(
129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 133 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
130 new GameListItemSize(file->GetSize()), 134 new GameListItemSize(file->GetSize()),
131 }); 135 };
136
137 if (UISettings::values.show_add_ons) {
138 list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
139 }
140
141 emit EntryReady(list);
132 } 142 }
133 143
134 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, 144 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
@@ -211,18 +221,23 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
211 if (it != compatibility_list.end()) 221 if (it != compatibility_list.end())
212 compatibility = it->second.first; 222 compatibility = it->second.first;
213 223
214 emit EntryReady({ 224 QList<QStandardItem*> list{
215 new GameListItemPath( 225 new GameListItemPath(
216 FormatGameName(physical_name), icon, QString::fromStdString(name), 226 FormatGameName(physical_name), icon, QString::fromStdString(name),
217 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 227 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
218 program_id), 228 program_id),
219 new GameListItemCompat(compatibility), 229 new GameListItemCompat(compatibility),
220 new GameListItem( 230 new GameListItem(
221 FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
222 new GameListItem(
223 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 231 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
224 new GameListItemSize(FileUtil::GetSize(physical_name)), 232 new GameListItemSize(FileUtil::GetSize(physical_name)),
225 }); 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));
226 } else if (is_dir && recursion > 0) { 241 } else if (is_dir && recursion > 0) {
227 watch_list.append(QString::fromStdString(physical_name)); 242 watch_list.append(QString::fromStdString(physical_name));
228 AddFstEntriesToGameList(physical_name, recursion - 1); 243 AddFstEntriesToGameList(physical_name, recursion - 1);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bef9df00d..4b969119c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -10,8 +10,9 @@
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
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 14
14// 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
15// defines. 16// defines.
16static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( 17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
17 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { 18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
@@ -29,6 +30,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
29#define QT_NO_OPENGL 30#define QT_NO_OPENGL
30#include <QDesktopWidget> 31#include <QDesktopWidget>
31#include <QDialogButtonBox> 32#include <QDialogButtonBox>
33#include <QFile>
32#include <QFileDialog> 34#include <QFileDialog>
33#include <QMessageBox> 35#include <QMessageBox>
34#include <QtConcurrent/QtConcurrent> 36#include <QtConcurrent/QtConcurrent>
@@ -60,6 +62,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
60#include "core/hle/kernel/process.h" 62#include "core/hle/kernel/process.h"
61#include "core/hle/service/filesystem/filesystem.h" 63#include "core/hle/service/filesystem/filesystem.h"
62#include "core/hle/service/filesystem/fsp_ldr.h" 64#include "core/hle/service/filesystem/fsp_ldr.h"
65#include "core/hle/service/nfp/nfp.h"
66#include "core/hle/service/sm/sm.h"
63#include "core/loader/loader.h" 67#include "core/loader/loader.h"
64#include "core/perf_stats.h" 68#include "core/perf_stats.h"
65#include "core/settings.h" 69#include "core/settings.h"
@@ -100,6 +104,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
100} 104}
101#endif 105#endif
102 106
107constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
108
103/** 109/**
104 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 110 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
105 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the 111 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
@@ -136,6 +142,9 @@ static void InitializeLogging() {
136 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 142 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
137 FileUtil::CreateFullPath(log_dir); 143 FileUtil::CreateFullPath(log_dir);
138 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
139} 148}
140 149
141GMainWindow::GMainWindow() 150GMainWindow::GMainWindow()
@@ -299,6 +308,8 @@ void GMainWindow::InitializeHotkeys() {
299 Qt::ApplicationShortcut); 308 Qt::ApplicationShortcut);
300 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), 309 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
301 Qt::ApplicationShortcut); 310 Qt::ApplicationShortcut);
311 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
312 Qt::ApplicationShortcut);
302 hotkey_registry.LoadHotkeys(); 313 hotkey_registry.LoadHotkeys();
303 314
304 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 315 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -352,6 +363,12 @@ void GMainWindow::InitializeHotkeys() {
352 UpdateStatusBar(); 363 UpdateStatusBar();
353 } 364 }
354 }); 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 });
355} 372}
356 373
357void GMainWindow::SetDefaultUIGeometry() { 374void GMainWindow::SetDefaultUIGeometry() {
@@ -422,6 +439,7 @@ void GMainWindow::ConnectMenuEvents() {
422 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, 439 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
423 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); 440 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
424 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 441 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
442 connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
425 443
426 // Emulation 444 // Emulation
427 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); 445 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
@@ -447,6 +465,7 @@ void GMainWindow::ConnectMenuEvents() {
447 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 465 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
448 466
449 // Help 467 // Help
468 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
450 connect(ui.action_Rederive, &QAction::triggered, this, 469 connect(ui.action_Rederive, &QAction::triggered, this,
451 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); 470 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
452 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 471 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
@@ -690,6 +709,7 @@ void GMainWindow::ShutdownGame() {
690 ui.action_Stop->setEnabled(false); 709 ui.action_Stop->setEnabled(false);
691 ui.action_Restart->setEnabled(false); 710 ui.action_Restart->setEnabled(false);
692 ui.action_Report_Compatibility->setEnabled(false); 711 ui.action_Report_Compatibility->setEnabled(false);
712 ui.action_Load_Amiibo->setEnabled(false);
693 render_window->hide(); 713 render_window->hide();
694 game_list->show(); 714 game_list->show();
695 game_list->setFilterFocus(); 715 game_list->setFilterFocus();
@@ -751,12 +771,43 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
751 open_target = "Save Data"; 771 open_target = "Save Data";
752 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 772 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
753 ASSERT(program_id != 0); 773 ASSERT(program_id != 0);
754 // TODO(tech4me): Update this to work with arbitrary user profile 774
755 // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor 775 Service::Account::ProfileManager manager{};
756 constexpr u128 user_id = {1, 0}; 776 const auto user_ids = manager.GetAllUsers();
777 QStringList list;
778 for (const auto& user_id : user_ids) {
779 if (user_id == Service::Account::UUID{})
780 continue;
781 Service::Account::ProfileBase base;
782 if (!manager.GetProfileBase(user_id, base))
783 continue;
784
785 list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer(
786 reinterpret_cast<const char*>(base.username.data()), base.username.size())));
787 }
788
789 bool ok = false;
790 const auto index_string =
791 QInputDialog::getItem(this, tr("Select User"),
792 tr("Please select the user's save data you would like to open."),
793 list, Settings::values.current_user, false, &ok);
794 if (!ok)
795 return;
796
797 const auto index = list.indexOf(index_string);
798 ASSERT(index != -1 && index < 8);
799
800 const auto user_id = manager.GetUser(index);
801 ASSERT(user_id);
757 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, 802 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
758 FileSys::SaveDataType::SaveData, 803 FileSys::SaveDataType::SaveData,
759 program_id, user_id, 0); 804 program_id, user_id->uuid, 0);
805
806 if (!FileUtil::Exists(path)) {
807 FileUtil::CreateFullPath(path);
808 FileUtil::CreateDir(path);
809 }
810
760 break; 811 break;
761 } 812 }
762 case GameListOpenTarget::ModData: { 813 case GameListOpenTarget::ModData: {
@@ -823,14 +874,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
823} 874}
824 875
825void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 876void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
826 const auto path = fmt::format("{}{:016X}/romfs", 877 const auto failed = [this] {
827 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
828
829 const auto failed = [this, &path] {
830 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 878 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
831 tr("There was an error copying the RomFS files or the user " 879 tr("There was an error copying the RomFS files or the user "
832 "cancelled the operation.")); 880 "cancelled the operation."));
833 vfs->DeleteDirectory(path);
834 }; 881 };
835 882
836 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 883 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
@@ -845,10 +892,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
845 return; 892 return;
846 } 893 }
847 894
848 const auto romfs = 895 const auto installed = Service::FileSystem::GetUnionContents();
849 loader->IsRomFSUpdatable() 896 auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
850 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) 897
851 : file; 898 if (!romfs_title_id) {
899 failed();
900 return;
901 }
902
903 const auto path = fmt::format(
904 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
905
906 FileSys::VirtualFile romfs;
907
908 if (*romfs_title_id == program_id) {
909 romfs = file;
910 } else {
911 romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
912 }
852 913
853 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); 914 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
854 if (extracted == nullptr) { 915 if (extracted == nullptr) {
@@ -860,6 +921,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
860 921
861 if (out == nullptr) { 922 if (out == nullptr) {
862 failed(); 923 failed();
924 vfs->DeleteDirectory(path);
863 return; 925 return;
864 } 926 }
865 927
@@ -870,13 +932,17 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
870 "files into the new directory while <br>skeleton will only create the directory " 932 "files into the new directory while <br>skeleton will only create the directory "
871 "structure."), 933 "structure."),
872 {"Full", "Skeleton"}, 0, false, &ok); 934 {"Full", "Skeleton"}, 0, false, &ok);
873 if (!ok) 935 if (!ok) {
874 failed(); 936 failed();
937 vfs->DeleteDirectory(path);
938 return;
939 }
875 940
876 const auto full = res == "Full"; 941 const auto full = res == "Full";
877 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 942 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
878 943
879 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);
880 progress.setWindowModality(Qt::WindowModal); 946 progress.setWindowModality(Qt::WindowModal);
881 progress.setMinimumDuration(100); 947 progress.setMinimumDuration(100);
882 948
@@ -888,6 +954,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
888 } else { 954 } else {
889 progress.close(); 955 progress.close();
890 failed(); 956 failed();
957 vfs->DeleteDirectory(path);
891 } 958 }
892} 959}
893 960
@@ -1174,6 +1241,7 @@ void GMainWindow::OnStartGame() {
1174 ui.action_Report_Compatibility->setEnabled(true); 1241 ui.action_Report_Compatibility->setEnabled(true);
1175 1242
1176 discord_rpc->Update(); 1243 discord_rpc->Update();
1244 ui.action_Load_Amiibo->setEnabled(true);
1177} 1245}
1178 1246
1179void GMainWindow::OnPauseGame() { 1247void GMainWindow::OnPauseGame() {
@@ -1273,11 +1341,63 @@ void GMainWindow::OnConfigure() {
1273 UpdateUITheme(); 1341 UpdateUITheme();
1274 if (UISettings::values.enable_discord_presence != old_discord_presence) 1342 if (UISettings::values.enable_discord_presence != old_discord_presence)
1275 SetDiscordEnabled(UISettings::values.enable_discord_presence); 1343 SetDiscordEnabled(UISettings::values.enable_discord_presence);
1276 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1344
1345 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1346 if (reload) {
1347 game_list->PopulateAsync(UISettings::values.gamedir,
1348 UISettings::values.gamedir_deepscan);
1349 }
1350
1277 config->Save(); 1351 config->Save();
1278 } 1352 }
1279} 1353}
1280 1354
1355void GMainWindow::OnLoadAmiibo() {
1356 const QString extensions{"*.bin"};
1357 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
1358 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
1359
1360 if (filename.isEmpty()) {
1361 return;
1362 }
1363
1364 Core::System& system{Core::System::GetInstance()};
1365 Service::SM::ServiceManager& sm = system.ServiceManager();
1366 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
1367 if (nfc == nullptr) {
1368 return;
1369 }
1370
1371 QFile nfc_file{filename};
1372 if (!nfc_file.open(QIODevice::ReadOnly)) {
1373 QMessageBox::warning(this, tr("Error opening Amiibo data file"),
1374 tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
1375 return;
1376 }
1377
1378 const u64 nfc_file_size = nfc_file.size();
1379 std::vector<u8> buffer(nfc_file_size);
1380 const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size);
1381 if (nfc_file_size != read_size) {
1382 QMessageBox::warning(this, tr("Error reading Amiibo data file"),
1383 tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
1384 "was only able to read %2 bytes.")
1385 .arg(nfc_file_size)
1386 .arg(read_size));
1387 return;
1388 }
1389
1390 if (!nfc->LoadAmiibo(buffer)) {
1391 QMessageBox::warning(this, tr("Error loading Amiibo data"),
1392 tr("Unable to load Amiibo data."));
1393 }
1394}
1395
1396void GMainWindow::OnOpenYuzuFolder() {
1397 QDesktopServices::openUrl(QUrl::fromLocalFile(
1398 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
1399}
1400
1281void GMainWindow::OnAbout() { 1401void GMainWindow::OnAbout() {
1282 AboutDialog aboutDialog(this); 1402 AboutDialog aboutDialog(this);
1283 aboutDialog.exec(); 1403 aboutDialog.exec();
@@ -1318,15 +1438,17 @@ void GMainWindow::UpdateStatusBar() {
1318void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { 1438void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
1319 QMessageBox::StandardButton answer; 1439 QMessageBox::StandardButton answer;
1320 QString status_message; 1440 QString status_message;
1321 const QString common_message = tr( 1441 const QString common_message =
1322 "The game you are trying to load requires additional files from your Switch to be dumped " 1442 tr("The game you are trying to load requires additional files from your Switch to be "
1323 "before playing.<br/><br/>For more information on dumping these files, please see the " 1443 "dumped "
1324 "following wiki page: <a " 1444 "before playing.<br/><br/>For more information on dumping these files, please see the "
1325 "href='https://yuzu-emu.org/wiki/" 1445 "following wiki page: <a "
1326 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " 1446 "href='https://yuzu-emu.org/wiki/"
1327 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit " 1447 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
1328 "back to the game list? Continuing emulation may result in crashes, corrupted save " 1448 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
1329 "data, or other bugs."); 1449 "quit "
1450 "back to the game list? Continuing emulation may result in crashes, corrupted save "
1451 "data, or other bugs.");
1330 switch (result) { 1452 switch (result) {
1331 case Core::System::ResultStatus::ErrorSystemFiles: { 1453 case Core::System::ResultStatus::ErrorSystemFiles: {
1332 QString message = "yuzu was unable to locate a Switch system archive"; 1454 QString message = "yuzu was unable to locate a Switch system archive";
@@ -1357,9 +1479,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1357 this, tr("Fatal Error"), 1479 this, tr("Fatal Error"),
1358 tr("yuzu has encountered a fatal error, please see the log for more details. " 1480 tr("yuzu has encountered a fatal error, please see the log for more details. "
1359 "For more information on accessing the log, please see the following page: " 1481 "For more information on accessing the log, please see the following page: "
1360 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to " 1482 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
1361 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? " 1483 "to "
1362 "Continuing emulation may result in crashes, corrupted save data, or other bugs."), 1484 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
1485 "list? "
1486 "Continuing emulation may result in crashes, corrupted save data, or other "
1487 "bugs."),
1363 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 1488 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
1364 status_message = "Fatal Error encountered"; 1489 status_message = "Fatal Error encountered";
1365 break; 1490 break;
@@ -1431,7 +1556,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1431 "derivation. It will be attempted but may not complete.<br><br>") + 1556 "derivation. It will be attempted but may not complete.<br><br>") +
1432 errors + 1557 errors +
1433 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 "
1434 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the " 1559 "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
1435 "quickstart guide</a>. Alternatively, you can use another method of dumping " 1560 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1436 "to obtain all of your keys.")); 1561 "to obtain all of your keys."));
1437 } 1562 }
@@ -1459,6 +1584,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1459 } 1584 }
1460} 1585}
1461 1586
1587std::optional<u64> GMainWindow::SelectRomFSDumpTarget(
1588 const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
1589 const auto dlc_entries =
1590 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1591 std::vector<FileSys::RegisteredCacheEntry> dlc_match;
1592 dlc_match.reserve(dlc_entries.size());
1593 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1594 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) {
1595 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1596 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1597 });
1598
1599 std::vector<u64> romfs_tids;
1600 romfs_tids.push_back(program_id);
1601 for (const auto& entry : dlc_match)
1602 romfs_tids.push_back(entry.title_id);
1603
1604 if (romfs_tids.size() > 1) {
1605 QStringList list{"Base"};
1606 for (std::size_t i = 1; i < romfs_tids.size(); ++i)
1607 list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
1608
1609 bool ok;
1610 const auto res = QInputDialog::getItem(
1611 this, tr("Select RomFS Dump Target"),
1612 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
1613 if (!ok) {
1614 return {};
1615 }
1616
1617 return romfs_tids[list.indexOf(res)];
1618 }
1619
1620 return program_id;
1621}
1622
1462bool GMainWindow::ConfirmClose() { 1623bool GMainWindow::ConfirmClose() {
1463 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1624 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1464 return true; 1625 return true;
@@ -1475,7 +1636,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
1475 return; 1636 return;
1476 } 1637 }
1477 1638
1478 if (ui.action_Fullscreen->isChecked()) { 1639 if (!ui.action_Fullscreen->isChecked()) {
1479 UISettings::values.geometry = saveGeometry(); 1640 UISettings::values.geometry = saveGeometry();
1480 UISettings::values.renderwindow_geometry = render_window->saveGeometry(); 1641 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
1481 } 1642 }
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 3663d6aed..929250e8c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <optional>
8#include <unordered_map> 9#include <unordered_map>
9 10
10#include <QMainWindow> 11#include <QMainWindow>
@@ -29,8 +30,9 @@ class WaitTreeWidget;
29enum class GameListOpenTarget; 30enum class GameListOpenTarget;
30 31
31namespace FileSys { 32namespace FileSys {
33class RegisteredCacheUnion;
32class VfsFilesystem; 34class VfsFilesystem;
33} 35} // namespace FileSys
34 36
35namespace Tegra { 37namespace Tegra {
36class DebugContext; 38class DebugContext;
@@ -164,6 +166,8 @@ private slots:
164 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target); 166 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
165 void OnMenuRecentFile(); 167 void OnMenuRecentFile();
166 void OnConfigure(); 168 void OnConfigure();
169 void OnLoadAmiibo();
170 void OnOpenYuzuFolder();
167 void OnAbout(); 171 void OnAbout();
168 void OnToggleFilterBar(); 172 void OnToggleFilterBar();
169 void OnDisplayTitleBars(bool); 173 void OnDisplayTitleBars(bool);
@@ -175,6 +179,7 @@ private slots:
175 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 179 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
176 180
177private: 181private:
182 std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id);
178 void UpdateStatusBar(); 183 void UpdateStatusBar();
179 184
180 Ui::MainWindow ui; 185 Ui::MainWindow ui;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index dffd9c788..75e96387f 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -57,8 +57,8 @@
57 <string>Recent Files</string> 57 <string>Recent Files</string>
58 </property> 58 </property>
59 </widget> 59 </widget>
60 <addaction name="action_Install_File_NAND" /> 60 <addaction name="action_Install_File_NAND"/>
61 <addaction name="separator"/> 61 <addaction name="separator"/>
62 <addaction name="action_Load_File"/> 62 <addaction name="action_Load_File"/>
63 <addaction name="action_Load_Folder"/> 63 <addaction name="action_Load_Folder"/>
64 <addaction name="separator"/> 64 <addaction name="separator"/>
@@ -68,6 +68,10 @@
68 <addaction name="action_Select_NAND_Directory"/> 68 <addaction name="action_Select_NAND_Directory"/>
69 <addaction name="action_Select_SDMC_Directory"/> 69 <addaction name="action_Select_SDMC_Directory"/>
70 <addaction name="separator"/> 70 <addaction name="separator"/>
71 <addaction name="action_Load_Amiibo"/>
72 <addaction name="separator"/>
73 <addaction name="action_Open_yuzu_Folder"/>
74 <addaction name="separator"/>
71 <addaction name="action_Exit"/> 75 <addaction name="action_Exit"/>
72 </widget> 76 </widget>
73 <widget class="QMenu" name="menu_Emulation"> 77 <widget class="QMenu" name="menu_Emulation">
@@ -117,11 +121,14 @@
117 <addaction name="menu_Tools" /> 121 <addaction name="menu_Tools" />
118 <addaction name="menu_Help"/> 122 <addaction name="menu_Help"/>
119 </widget> 123 </widget>
120 <action name="action_Install_File_NAND"> 124 <action name="action_Install_File_NAND">
121 <property name="text"> 125 <property name="enabled">
122 <string>Install File to NAND...</string> 126 <bool>true</bool>
123 </property> 127 </property>
124 </action> 128 <property name="text">
129 <string>Install File to NAND...</string>
130 </property>
131 </action>
125 <action name="action_Load_File"> 132 <action name="action_Load_File">
126 <property name="text"> 133 <property name="text">
127 <string>Load File...</string> 134 <string>Load File...</string>
@@ -253,6 +260,14 @@
253 <string>Restart</string> 260 <string>Restart</string>
254 </property> 261 </property>
255 </action> 262 </action>
263 <action name="action_Load_Amiibo">
264 <property name="enabled">
265 <bool>false</bool>
266 </property>
267 <property name="text">
268 <string>Load Amiibo...</string>
269 </property>
270 </action>
256 <action name="action_Report_Compatibility"> 271 <action name="action_Report_Compatibility">
257 <property name="enabled"> 272 <property name="enabled">
258 <bool>false</bool> 273 <bool>false</bool>
@@ -264,6 +279,11 @@
264 <bool>false</bool> 279 <bool>false</bool>
265 </property> 280 </property>
266 </action> 281 </action>
282 <action name="action_Open_yuzu_Folder">
283 <property name="text">
284 <string>Open yuzu Folder</string>
285 </property>
286 </action>
267 </widget> 287 </widget>
268 <resources/> 288 <resources/>
269 <connections/> 289 <connections/>
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 2e617d52a..e80aebc0a 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include <vector> 9#include <vector>
9#include <QByteArray> 10#include <QByteArray>
10#include <QString> 11#include <QString>
@@ -59,9 +60,11 @@ struct Values {
59 60
60 // Game List 61 // Game List
61 bool show_unknown; 62 bool show_unknown;
63 bool show_add_ons;
62 uint32_t icon_size; 64 uint32_t icon_size;
63 uint8_t row_1_text_id; 65 uint8_t row_1_text_id;
64 uint8_t row_2_text_id; 66 uint8_t row_2_text_id;
67 std::atomic_bool is_game_list_reload_pending{false};
65}; 68};
66 69
67extern Values values; 70extern Values values;
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp
new file mode 100644
index 000000000..edd78e579
--- /dev/null
+++ b/src/yuzu/util/limitable_input_dialog.cpp
@@ -0,0 +1,59 @@
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 <QDialogButtonBox>
6#include <QLabel>
7#include <QLineEdit>
8#include <QPushButton>
9#include <QVBoxLayout>
10#include "yuzu/util/limitable_input_dialog.h"
11
12LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
13 CreateUI();
14 ConnectEvents();
15}
16
17LimitableInputDialog::~LimitableInputDialog() = default;
18
19void LimitableInputDialog::CreateUI() {
20 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
21
22 text_label = new QLabel(this);
23 text_entry = new QLineEdit(this);
24 buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
25
26 auto* const layout = new QVBoxLayout;
27 layout->addWidget(text_label);
28 layout->addWidget(text_entry);
29 layout->addWidget(buttons);
30
31 setLayout(layout);
32}
33
34void LimitableInputDialog::ConnectEvents() {
35 connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
36 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
37}
38
39QString LimitableInputDialog::GetText(QWidget* parent, const QString& title, const QString& text,
40 int min_character_limit, int max_character_limit) {
41 Q_ASSERT(min_character_limit <= max_character_limit);
42
43 LimitableInputDialog dialog{parent};
44 dialog.setWindowTitle(title);
45 dialog.text_label->setText(text);
46 dialog.text_entry->setMaxLength(max_character_limit);
47
48 auto* const ok_button = dialog.buttons->button(QDialogButtonBox::Ok);
49 ok_button->setEnabled(false);
50 connect(dialog.text_entry, &QLineEdit::textEdited, [&](const QString& new_text) {
51 ok_button->setEnabled(new_text.length() >= min_character_limit);
52 });
53
54 if (dialog.exec() != QDialog::Accepted) {
55 return {};
56 }
57
58 return dialog.text_entry->text();
59}
diff --git a/src/yuzu/util/limitable_input_dialog.h b/src/yuzu/util/limitable_input_dialog.h
new file mode 100644
index 000000000..164ad7301
--- /dev/null
+++ b/src/yuzu/util/limitable_input_dialog.h
@@ -0,0 +1,31 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDialog>
8
9class QDialogButtonBox;
10class QLabel;
11class QLineEdit;
12
13/// A QDialog that functions similarly to QInputDialog, however, it allows
14/// restricting the minimum and total number of characters that can be entered.
15class LimitableInputDialog final : public QDialog {
16 Q_OBJECT
17public:
18 explicit LimitableInputDialog(QWidget* parent = nullptr);
19 ~LimitableInputDialog() override;
20
21 static QString GetText(QWidget* parent, const QString& title, const QString& text,
22 int min_character_limit, int max_character_limit);
23
24private:
25 void CreateUI();
26 void ConnectEvents();
27
28 QLabel* text_label;
29 QLineEdit* text_entry;
30 QDialogButtonBox* buttons;
31};
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 5e42e48b2..96f1ef636 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -8,6 +8,7 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/param_package.h" 10#include "common/param_package.h"
11#include "core/hle/service/acc/profile_manager.h"
11#include "core/settings.h" 12#include "core/settings.h"
12#include "input_common/main.h" 13#include "input_common/main.h"
13#include "yuzu_cmd/config.h" 14#include "yuzu_cmd/config.h"
@@ -125,11 +126,21 @@ void Config::ReadValues() {
125 126
126 // System 127 // System
127 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 128 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
128 Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); 129 Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
129 if (Settings::values.username.empty()) { 130 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
130 Settings::values.username = "yuzu"; 131
132 Settings::values.current_user = std::clamp<int>(
133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
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;
131 } 140 }
132 141
142 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
143
133 // Miscellaneous 144 // Miscellaneous
134 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 145 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
135 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);
@@ -139,6 +150,7 @@ void Config::ReadValues() {
139 Settings::values.gdbstub_port = 150 Settings::values.gdbstub_port =
140 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 151 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
141 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);
142 154
143 // Web Service 155 // Web Service
144 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 a97b75f7b..ecf625e7b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -174,6 +174,15 @@ use_virtual_sd =
174# 1: Yes, 0 (default): No 174# 1: Yes, 0 (default): No
175use_docked_mode = 175use_docked_mode =
176 176
177# Allow the use of NFC in games
178# 1 (default): Yes, 0 : No
179enable_nfc =
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
177# Sets the account username, max length is 32 characters 186# Sets the account username, max length is 32 characters
178# yuzu (default) 187# yuzu (default)
179username = yuzu 188username = yuzu
@@ -197,6 +206,8 @@ log_filter = *:Trace
197# Port for listening to GDB connections. 206# Port for listening to GDB connections.
198use_gdbstub=false 207use_gdbstub=false
199gdbstub_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
200 211
201[WebService] 212[WebService]
202# 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