summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis/macos/upload.sh12
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/common/lz4_compression.h2
-rw-r--r--src/common/zstd_compression.h2
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/arm_interface.h14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp23
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h6
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h2
-rw-r--r--src/core/core.cpp83
-rw-r--r--src/core/core.h29
-rw-r--r--src/core/cpu_core_manager.cpp6
-rw-r--r--src/core/cpu_core_manager.h7
-rw-r--r--src/core/frontend/applets/error.cpp34
-rw-r--r--src/core/frontend/applets/error.h37
-rw-r--r--src/core/frontend/applets/general_frontend.cpp27
-rw-r--r--src/core/frontend/applets/general_frontend.h28
-rw-r--r--src/core/hle/kernel/kernel.cpp10
-rw-r--r--src/core/hle/kernel/process.cpp28
-rw-r--r--src/core/hle/kernel/process.h7
-rw-r--r--src/core/hle/kernel/svc.cpp170
-rw-r--r--src/core/hle/kernel/svc_wrap.h7
-rw-r--r--src/core/hle/kernel/thread.cpp14
-rw-r--r--src/core/hle/kernel/thread.h58
-rw-r--r--src/core/hle/kernel/vm_manager.cpp85
-rw-r--r--src/core/hle/kernel/vm_manager.h57
-rw-r--r--src/core/hle/kernel/wait_object.cpp31
-rw-r--r--src/core/hle/kernel/wait_object.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp4
-rw-r--r--src/core/hle/service/acc/acc_su.cpp6
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp8
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp6
-rw-r--r--src/core/hle/service/am/am.cpp43
-rw-r--r--src/core/hle/service/am/applet_ae.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.cpp82
-rw-r--r--src/core/hle/service/am/applets/applets.h55
-rw-r--r--src/core/hle/service/am/applets/error.cpp182
-rw-r--r--src/core/hle/service/am/applets/error.h47
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp (renamed from src/core/hle/service/am/applets/stub_applet.cpp)56
-rw-r--r--src/core/hle/service/am/applets/general_backend.h48
-rw-r--r--src/core/hle/service/am/applets/profile_select.cpp6
-rw-r--r--src/core/hle/service/am/applets/profile_select.h4
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp7
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h4
-rw-r--r--src/core/hle/service/am/applets/stub_applet.h24
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp4
-rw-r--r--src/core/hle/service/am/applets/web_browser.h4
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp4
-rw-r--r--src/core/hle/service/apm/interface.cpp2
-rw-r--r--src/core/hle/service/audio/audctl.cpp30
-rw-r--r--src/core/hle/service/audio/audctl.h4
-rw-r--r--src/core/hle/service/audio/audin_u.cpp1
-rw-r--r--src/core/hle/service/audio/audout_u.cpp5
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp3
-rw-r--r--src/core/hle/service/caps/caps.cpp89
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp7
-rw-r--r--src/core/hle/service/friend/friend.cpp5
-rw-r--r--src/core/hle/service/hid/hid.cpp15
-rw-r--r--src/core/hle/service/ldn/ldn.cpp54
-rw-r--r--src/core/hle/service/ldr/ldr.cpp1
-rw-r--r--src/core/hle/service/nifm/nifm.cpp4
-rw-r--r--src/core/hle/service/npns/npns.cpp3
-rw-r--r--src/core/hle/service/pctl/module.cpp7
-rw-r--r--src/core/hle/service/pm/pm.cpp21
-rw-r--r--src/core/hle/service/set/set.cpp4
-rw-r--r--src/core/hle/service/set/set_cal.cpp2
-rw-r--r--src/core/hle/service/set/set_sys.cpp27
-rw-r--r--src/core/hle/service/sockets/bsd.cpp5
-rw-r--r--src/core/hle/service/ssl/ssl.cpp2
-rw-r--r--src/core/hle/service/time/interface.cpp10
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp40
-rw-r--r--src/core/loader/deconstructed_rom_directory.h2
-rw-r--r--src/core/loader/elf.cpp15
-rw-r--r--src/core/loader/elf.h2
-rw-r--r--src/core/loader/loader.h8
-rw-r--r--src/core/loader/nax.cpp30
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.cpp26
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nro.cpp14
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp11
-rw-r--r--src/core/loader/nso.h2
-rw-r--r--src/core/loader/nsp.cpp38
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp28
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/memory.cpp16
-rw-r--r--src/core/memory.h5
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/dma_pusher.cpp6
-rw-r--r--src/video_core/engines/kepler_memory.cpp47
-rw-r--r--src/video_core/engines/kepler_memory.h24
-rw-r--r--src/video_core/engines/maxwell_3d.cpp43
-rw-r--r--src/video_core/engines/maxwell_3d.h11
-rw-r--r--src/video_core/engines/shader_bytecode.h85
-rw-r--r--src/video_core/gpu.h5
-rw-r--r--src/video_core/gpu_asynch.cpp6
-rw-r--r--src/video_core/gpu_asynch.h5
-rw-r--r--src/video_core/gpu_synch.cpp4
-rw-r--r--src/video_core/gpu_synch.h1
-rw-r--r--src/video_core/gpu_thread.cpp17
-rw-r--r--src/video_core/gpu_thread.h6
-rw-r--r--src/video_core/memory_manager.cpp64
-rw-r--r--src/video_core/memory_manager.h29
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp45
-rw-r--r--src/video_core/renderer_opengl/gl_device.h30
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp42
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.h16
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp143
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h40
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_sampler_cache.cpp52
-rw-r--r--src/video_core/renderer_opengl/gl_sampler_cache.h25
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp77
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp180
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h26
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp246
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h54
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h29
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp8
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h53
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.cpp40
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.h36
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp39
-rw-r--r--src/video_core/sampler_cache.cpp21
-rw-r--r--src/video_core/sampler_cache.h60
-rw-r--r--src/video_core/shader/decode.cpp4
-rw-r--r--src/video_core/shader/decode/arithmetic_half.cpp31
-rw-r--r--src/video_core/shader/decode/arithmetic_half_immediate.cpp17
-rw-r--r--src/video_core/shader/decode/conversion.cpp76
-rw-r--r--src/video_core/shader/decode/half_set.cpp16
-rw-r--r--src/video_core/shader/decode/half_set_predicate.cpp8
-rw-r--r--src/video_core/shader/decode/hfma2.cpp21
-rw-r--r--src/video_core/shader/decode/memory.cpp118
-rw-r--r--src/video_core/shader/decode/texture.cpp113
-rw-r--r--src/video_core/shader/decode/xmad.cpp5
-rw-r--r--src/video_core/shader/shader_ir.cpp60
-rw-r--r--src/video_core/shader/shader_ir.h106
-rw-r--r--src/video_core/surface.cpp86
-rw-r--r--src/video_core/textures/decoders.cpp23
-rw-r--r--src/video_core/textures/decoders.h4
-rw-r--r--src/video_core/video_core.cpp10
-rw-r--r--src/video_core/video_core.h7
-rw-r--r--src/yuzu/CMakeLists.txt8
-rw-r--r--src/yuzu/applets/error.cpp59
-rw-r--r--src/yuzu/applets/error.h33
-rw-r--r--src/yuzu/bootmanager.cpp13
-rw-r--r--src/yuzu/bootmanager.h1
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp19
-rw-r--r--src/yuzu/configuration/configure_hotkeys.h3
-rw-r--r--src/yuzu/debugger/wait_tree.cpp11
-rw-r--r--src/yuzu/game_list_p.h2
-rw-r--r--src/yuzu/main.cpp17
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/yuzu.cpp10
166 files changed, 3227 insertions, 1288 deletions
diff --git a/.travis/macos/upload.sh b/.travis/macos/upload.sh
index 66e3455ff..c2f43a906 100755
--- a/.travis/macos/upload.sh
+++ b/.travis/macos/upload.sh
@@ -19,18 +19,6 @@ $(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app" -executable="${R
19# move libs into folder for deployment 19# move libs into folder for deployment
20macpack "${REV_NAME}/yuzu-cmd" -d "libs" 20macpack "${REV_NAME}/yuzu-cmd" -d "libs"
21 21
22# Make the yuzu.app application launch a debugging terminal.
23# Store away the actual binary
24mv ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu-bin
25
26cat > ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu <<EOL
27#!/usr/bin/env bash
28cd "\`dirname "\$0"\`"
29chmod +x yuzu-bin
30open yuzu-bin --args "\$@"
31EOL
32# Content that will serve as the launching script for yuzu (within the .app folder)
33
34# Make the launching script executable 22# Make the launching script executable
35chmod +x ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu 23chmod +x ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu
36 24
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6c99dd5e2..9aea4af87 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,6 +18,9 @@ if (MSVC)
18 # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. 18 # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
19 add_definitions(-DWIN32_LEAN_AND_MEAN) 19 add_definitions(-DWIN32_LEAN_AND_MEAN)
20 20
21 # Ensure that projects build with Unicode support.
22 add_definitions(-DUNICODE -D_UNICODE)
23
21 # /W3 - Level 3 warnings 24 # /W3 - Level 3 warnings
22 # /MP - Multi-threaded compilation 25 # /MP - Multi-threaded compilation
23 # /Zi - Output debugging information 26 # /Zi - Output debugging information
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h
index fe2231a6c..4c16f6e03 100644
--- a/src/common/lz4_compression.h
+++ b/src/common/lz4_compression.h
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once
6
5#include <vector> 7#include <vector>
6 8
7#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h
index e0a64b035..e9de941c8 100644
--- a/src/common/zstd_compression.h
+++ b/src/common/zstd_compression.h
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once
6
5#include <vector> 7#include <vector>
6 8
7#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c59107102..2ace866ee 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -88,6 +88,10 @@ add_library(core STATIC
88 file_sys/vfs_vector.h 88 file_sys/vfs_vector.h
89 file_sys/xts_archive.cpp 89 file_sys/xts_archive.cpp
90 file_sys/xts_archive.h 90 file_sys/xts_archive.h
91 frontend/applets/error.cpp
92 frontend/applets/error.h
93 frontend/applets/general_frontend.cpp
94 frontend/applets/general_frontend.h
91 frontend/applets/profile_select.cpp 95 frontend/applets/profile_select.cpp
92 frontend/applets/profile_select.h 96 frontend/applets/profile_select.h
93 frontend/applets/software_keyboard.cpp 97 frontend/applets/software_keyboard.cpp
@@ -177,12 +181,14 @@ add_library(core STATIC
177 hle/service/am/applet_oe.h 181 hle/service/am/applet_oe.h
178 hle/service/am/applets/applets.cpp 182 hle/service/am/applets/applets.cpp
179 hle/service/am/applets/applets.h 183 hle/service/am/applets/applets.h
184 hle/service/am/applets/error.cpp
185 hle/service/am/applets/error.h
186 hle/service/am/applets/general_backend.cpp
187 hle/service/am/applets/general_backend.h
180 hle/service/am/applets/profile_select.cpp 188 hle/service/am/applets/profile_select.cpp
181 hle/service/am/applets/profile_select.h 189 hle/service/am/applets/profile_select.h
182 hle/service/am/applets/software_keyboard.cpp 190 hle/service/am/applets/software_keyboard.cpp
183 hle/service/am/applets/software_keyboard.h 191 hle/service/am/applets/software_keyboard.h
184 hle/service/am/applets/stub_applet.cpp
185 hle/service/am/applets/stub_applet.h
186 hle/service/am/applets/web_browser.cpp 192 hle/service/am/applets/web_browser.cpp
187 hle/service/am/applets/web_browser.h 193 hle/service/am/applets/web_browser.h
188 hle/service/am/idle.cpp 194 hle/service/am/idle.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 4dfd41b43..978b1518f 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -7,6 +7,10 @@
7#include <array> 7#include <array>
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace Common {
11struct PageTable;
12}
13
10namespace Kernel { 14namespace Kernel {
11enum class VMAPermission : u8; 15enum class VMAPermission : u8;
12} 16}
@@ -49,8 +53,14 @@ public:
49 /// Clear all instruction cache 53 /// Clear all instruction cache
50 virtual void ClearInstructionCache() = 0; 54 virtual void ClearInstructionCache() = 0;
51 55
52 /// Notify CPU emulation that page tables have changed 56 /// Notifies CPU emulation that the current page table has changed.
53 virtual void PageTableChanged() = 0; 57 ///
58 /// @param new_page_table The new page table.
59 /// @param new_address_space_size_in_bits The new usable size of the address space in bits.
60 /// This can be either 32, 36, or 39 on official software.
61 ///
62 virtual void PageTableChanged(Common::PageTable& new_page_table,
63 std::size_t new_address_space_size_in_bits) = 0;
54 64
55 /** 65 /**
56 * Set the Program Counter to an address 66 * Set the Program Counter to an address
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index dc96e35d5..44307fa19 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -14,7 +14,6 @@
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/core_timing_util.h" 15#include "core/core_timing_util.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/svc.h" 18#include "core/hle/kernel/svc.h"
20#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
@@ -129,18 +128,16 @@ public:
129 u64 tpidr_el0 = 0; 128 u64 tpidr_el0 = 0;
130}; 129};
131 130
132std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
133 auto* current_process = system.Kernel().CurrentProcess(); 132 std::size_t address_space_bits) const {
134 auto** const page_table = current_process->VMManager().page_table.pointers.data();
135
136 Dynarmic::A64::UserConfig config; 133 Dynarmic::A64::UserConfig config;
137 134
138 // Callbacks 135 // Callbacks
139 config.callbacks = cb.get(); 136 config.callbacks = cb.get();
140 137
141 // Memory 138 // Memory
142 config.page_table = reinterpret_cast<void**>(page_table); 139 config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
143 config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth(); 140 config.page_table_address_space_bits = address_space_bits;
144 config.silently_mirror_page_table = false; 141 config.silently_mirror_page_table = false;
145 142
146 // Multi-process state 143 // Multi-process state
@@ -176,12 +173,7 @@ ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
176 std::size_t core_index) 173 std::size_t core_index)
177 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, 174 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
178 core_index{core_index}, system{system}, 175 core_index{core_index}, system{system},
179 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { 176 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
180 ThreadContext ctx{};
181 inner_unicorn.SaveContext(ctx);
182 PageTableChanged();
183 LoadContext(ctx);
184}
185 177
186ARM_Dynarmic::~ARM_Dynarmic() = default; 178ARM_Dynarmic::~ARM_Dynarmic() = default;
187 179
@@ -276,8 +268,9 @@ void ARM_Dynarmic::ClearExclusiveState() {
276 jit->ClearExclusiveState(); 268 jit->ClearExclusiveState();
277} 269}
278 270
279void ARM_Dynarmic::PageTableChanged() { 271void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
280 jit = MakeJit(); 272 std::size_t new_address_space_size_in_bits) {
273 jit = MakeJit(page_table, new_address_space_size_in_bits);
281} 274}
282 275
283DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} 276DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index c1db254e8..b701e97a3 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -48,10 +48,12 @@ public:
48 void ClearExclusiveState() override; 48 void ClearExclusiveState() override;
49 49
50 void ClearInstructionCache() override; 50 void ClearInstructionCache() override;
51 void PageTableChanged() override; 51 void PageTableChanged(Common::PageTable& new_page_table,
52 std::size_t new_address_space_size_in_bits) override;
52 53
53private: 54private:
54 std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const; 55 std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
56 std::size_t address_space_bits) const;
55 57
56 friend class ARM_Dynarmic_Callbacks; 58 friend class ARM_Dynarmic_Callbacks;
57 std::unique_ptr<ARM_Dynarmic_Callbacks> cb; 59 std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 209fc16ad..34e974b4d 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -41,7 +41,7 @@ public:
41 void Run() override; 41 void Run() override;
42 void Step() override; 42 void Step() override;
43 void ClearInstructionCache() override; 43 void ClearInstructionCache() override;
44 void PageTableChanged() override{}; 44 void PageTableChanged(Common::PageTable&, std::size_t) override {}
45 void RecordBreak(GDBStub::BreakpointAddress bkpt); 45 void RecordBreak(GDBStub::BreakpointAddress bkpt);
46 46
47private: 47private:
diff --git a/src/core/core.cpp b/src/core/core.cpp
index bc9e887b6..7106151bd 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -3,9 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <map>
7#include <memory> 6#include <memory>
8#include <thread>
9#include <utility> 7#include <utility>
10 8
11#include "common/file_util.h" 9#include "common/file_util.h"
@@ -20,13 +18,18 @@
20#include "core/file_sys/registered_cache.h" 18#include "core/file_sys/registered_cache.h"
21#include "core/file_sys/vfs_concat.h" 19#include "core/file_sys/vfs_concat.h"
22#include "core/file_sys/vfs_real.h" 20#include "core/file_sys/vfs_real.h"
21#include "core/frontend/applets/error.h"
22#include "core/frontend/applets/general_frontend.h"
23#include "core/frontend/applets/profile_select.h"
24#include "core/frontend/applets/software_keyboard.h"
25#include "core/frontend/applets/web_browser.h"
23#include "core/gdbstub/gdbstub.h" 26#include "core/gdbstub/gdbstub.h"
24#include "core/hle/kernel/client_port.h" 27#include "core/hle/kernel/client_port.h"
25#include "core/hle/kernel/kernel.h" 28#include "core/hle/kernel/kernel.h"
26#include "core/hle/kernel/process.h" 29#include "core/hle/kernel/process.h"
27#include "core/hle/kernel/scheduler.h" 30#include "core/hle/kernel/scheduler.h"
28#include "core/hle/kernel/thread.h" 31#include "core/hle/kernel/thread.h"
29#include "core/hle/service/am/applets/software_keyboard.h" 32#include "core/hle/service/am/applets/applets.h"
30#include "core/hle/service/service.h" 33#include "core/hle/service/service.h"
31#include "core/hle/service/sm/sm.h" 34#include "core/hle/service/sm/sm.h"
32#include "core/loader/loader.h" 35#include "core/loader/loader.h"
@@ -38,8 +41,6 @@
38#include "frontend/applets/software_keyboard.h" 41#include "frontend/applets/software_keyboard.h"
39#include "frontend/applets/web_browser.h" 42#include "frontend/applets/web_browser.h"
40#include "video_core/debug_utils/debug_utils.h" 43#include "video_core/debug_utils/debug_utils.h"
41#include "video_core/gpu_asynch.h"
42#include "video_core/gpu_synch.h"
43#include "video_core/renderer_base.h" 44#include "video_core/renderer_base.h"
44#include "video_core/video_core.h" 45#include "video_core/video_core.h"
45 46
@@ -81,7 +82,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
81 return vfs->OpenFile(path, FileSys::Mode::Read); 82 return vfs->OpenFile(path, FileSys::Mode::Read);
82} 83}
83struct System::Impl { 84struct System::Impl {
84 explicit Impl(System& system) : kernel{system} {} 85 explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
85 86
86 Cpu& CurrentCpuCore() { 87 Cpu& CurrentCpuCore() {
87 return cpu_core_manager.GetCurrentCore(); 88 return cpu_core_manager.GetCurrentCore();
@@ -99,6 +100,7 @@ struct System::Impl {
99 LOG_DEBUG(HW_Memory, "initialized OK"); 100 LOG_DEBUG(HW_Memory, "initialized OK");
100 101
101 core_timing.Initialize(); 102 core_timing.Initialize();
103 cpu_core_manager.Initialize();
102 kernel.Initialize(); 104 kernel.Initialize();
103 105
104 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 106 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
@@ -113,15 +115,7 @@ struct System::Impl {
113 content_provider = std::make_unique<FileSys::ContentProviderUnion>(); 115 content_provider = std::make_unique<FileSys::ContentProviderUnion>();
114 116
115 /// Create default implementations of applets if one is not provided. 117 /// Create default implementations of applets if one is not provided.
116 if (profile_selector == nullptr) 118 applet_manager.SetDefaultAppletsIfMissing();
117 profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
118 if (software_keyboard == nullptr)
119 software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
120 if (web_browser == nullptr)
121 web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
122
123 auto main_process = Kernel::Process::Create(system, "main");
124 kernel.MakeCurrentProcess(main_process.get());
125 119
126 telemetry_session = std::make_unique<Core::TelemetrySession>(); 120 telemetry_session = std::make_unique<Core::TelemetrySession>();
127 service_manager = std::make_shared<Service::SM::ServiceManager>(); 121 service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -134,15 +128,9 @@ struct System::Impl {
134 return ResultStatus::ErrorVideoCore; 128 return ResultStatus::ErrorVideoCore;
135 } 129 }
136 130
137 is_powered_on = true; 131 gpu_core = VideoCore::CreateGPU(system);
138 132
139 if (Settings::values.use_asynchronous_gpu_emulation) { 133 is_powered_on = true;
140 gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
141 } else {
142 gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
143 }
144
145 cpu_core_manager.Initialize(system);
146 134
147 LOG_DEBUG(Core, "Initialized OK"); 135 LOG_DEBUG(Core, "Initialized OK");
148 136
@@ -179,7 +167,8 @@ struct System::Impl {
179 return init_result; 167 return init_result;
180 } 168 }
181 169
182 const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())}; 170 auto main_process = Kernel::Process::Create(system, "main");
171 const auto [load_result, load_parameters] = app_loader->Load(*main_process);
183 if (load_result != Loader::ResultStatus::Success) { 172 if (load_result != Loader::ResultStatus::Success) {
184 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 173 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
185 Shutdown(); 174 Shutdown();
@@ -187,6 +176,16 @@ struct System::Impl {
187 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + 176 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
188 static_cast<u32>(load_result)); 177 static_cast<u32>(load_result));
189 } 178 }
179 kernel.MakeCurrentProcess(main_process.get());
180
181 // Main process has been loaded and been made current.
182 // Begin GPU and CPU execution.
183 gpu_core->Start();
184 cpu_core_manager.StartThreads();
185
186 // All threads are started, begin main process execution, now that we're in the clear.
187 main_process->Run(load_parameters->main_thread_priority,
188 load_parameters->main_thread_stack_size);
190 189
191 status = ResultStatus::Success; 190 status = ResultStatus::Success;
192 return status; 191 return status;
@@ -224,9 +223,7 @@ struct System::Impl {
224 app_loader.reset(); 223 app_loader.reset();
225 224
226 // Clear all applets 225 // Clear all applets
227 profile_selector.reset(); 226 applet_manager.ClearAll();
228 software_keyboard.reset();
229 web_browser.reset();
230 227
231 LOG_DEBUG(Core, "Shutdown OK"); 228 LOG_DEBUG(Core, "Shutdown OK");
232 } 229 }
@@ -265,9 +262,7 @@ struct System::Impl {
265 std::unique_ptr<FileSys::CheatEngine> cheat_engine; 262 std::unique_ptr<FileSys::CheatEngine> cheat_engine;
266 263
267 /// Frontend applets 264 /// Frontend applets
268 std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; 265 Service::AM::Applets::AppletManager applet_manager;
269 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
270 std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
271 266
272 /// Service manager 267 /// Service manager
273 std::shared_ptr<Service::SM::ServiceManager> service_manager; 268 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -477,20 +472,20 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
477 return impl->virtual_filesystem; 472 return impl->virtual_filesystem;
478} 473}
479 474
480void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) { 475void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
481 impl->profile_selector = std::move(applet); 476 impl->applet_manager.SetAppletFrontendSet(std::move(set));
482} 477}
483 478
484const Frontend::ProfileSelectApplet& System::GetProfileSelector() const { 479void System::SetDefaultAppletFrontendSet() {
485 return *impl->profile_selector; 480 impl->applet_manager.SetDefaultAppletFrontendSet();
486} 481}
487 482
488void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) { 483Service::AM::Applets::AppletManager& System::GetAppletManager() {
489 impl->software_keyboard = std::move(applet); 484 return impl->applet_manager;
490} 485}
491 486
492const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const { 487const Service::AM::Applets::AppletManager& System::GetAppletManager() const {
493 return *impl->software_keyboard; 488 return impl->applet_manager;
494} 489}
495 490
496void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { 491void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
@@ -514,18 +509,6 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
514 impl->content_provider->ClearSlot(slot); 509 impl->content_provider->ClearSlot(slot);
515} 510}
516 511
517void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
518 impl->web_browser = std::move(applet);
519}
520
521Frontend::WebBrowserApplet& System::GetWebBrowser() {
522 return *impl->web_browser;
523}
524
525const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
526 return *impl->web_browser;
527}
528
529System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 512System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
530 return impl->Init(*this, emu_window); 513 return impl->Init(*this, emu_window);
531} 514}
diff --git a/src/core/core.h b/src/core/core.h
index 82b2e087e..a9a756a4c 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -14,9 +14,6 @@
14 14
15namespace Core::Frontend { 15namespace Core::Frontend {
16class EmuWindow; 16class EmuWindow;
17class ProfileSelectApplet;
18class SoftwareKeyboardApplet;
19class WebBrowserApplet;
20} // namespace Core::Frontend 17} // namespace Core::Frontend
21 18
22namespace FileSys { 19namespace FileSys {
@@ -38,9 +35,18 @@ class AppLoader;
38enum class ResultStatus : u16; 35enum class ResultStatus : u16;
39} // namespace Loader 36} // namespace Loader
40 37
41namespace Service::SM { 38namespace Service {
39
40namespace AM::Applets {
41struct AppletFrontendSet;
42class AppletManager;
43} // namespace AM::Applets
44
45namespace SM {
42class ServiceManager; 46class ServiceManager;
43} // namespace Service::SM 47} // namespace SM
48
49} // namespace Service
44 50
45namespace Tegra { 51namespace Tegra {
46class DebugContext; 52class DebugContext;
@@ -260,18 +266,13 @@ public:
260 void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, 266 void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
261 VAddr code_region_start, VAddr code_region_end); 267 VAddr code_region_start, VAddr code_region_end);
262 268
263 void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet); 269 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
264
265 const Frontend::ProfileSelectApplet& GetProfileSelector() const;
266
267 void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
268 270
269 const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; 271 void SetDefaultAppletFrontendSet();
270 272
271 void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet); 273 Service::AM::Applets::AppletManager& GetAppletManager();
272 274
273 Frontend::WebBrowserApplet& GetWebBrowser(); 275 const Service::AM::Applets::AppletManager& GetAppletManager() const;
274 const Frontend::WebBrowserApplet& GetWebBrowser() const;
275 276
276 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); 277 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
277 278
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
index 93bc5619c..8fcb4eeb1 100644
--- a/src/core/cpu_core_manager.cpp
+++ b/src/core/cpu_core_manager.cpp
@@ -19,17 +19,19 @@ void RunCpuCore(const System& system, Cpu& cpu_state) {
19} 19}
20} // Anonymous namespace 20} // Anonymous namespace
21 21
22CpuCoreManager::CpuCoreManager() = default; 22CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
23CpuCoreManager::~CpuCoreManager() = default; 23CpuCoreManager::~CpuCoreManager() = default;
24 24
25void CpuCoreManager::Initialize(System& system) { 25void CpuCoreManager::Initialize() {
26 barrier = std::make_unique<CpuBarrier>(); 26 barrier = std::make_unique<CpuBarrier>();
27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); 27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
28 28
29 for (std::size_t index = 0; index < cores.size(); ++index) { 29 for (std::size_t index = 0; index < cores.size(); ++index) {
30 cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); 30 cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
31 } 31 }
32}
32 33
34void CpuCoreManager::StartThreads() {
33 // Create threads for CPU cores 1-3, and build thread_to_cpu map 35 // Create threads for CPU cores 1-3, and build thread_to_cpu map
34 // CPU core 0 is run on the main thread 36 // CPU core 0 is run on the main thread
35 thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); 37 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
index a4d70ec56..2cbbf8216 100644
--- a/src/core/cpu_core_manager.h
+++ b/src/core/cpu_core_manager.h
@@ -18,7 +18,7 @@ class System;
18 18
19class CpuCoreManager { 19class CpuCoreManager {
20public: 20public:
21 CpuCoreManager(); 21 explicit CpuCoreManager(System& system);
22 CpuCoreManager(const CpuCoreManager&) = delete; 22 CpuCoreManager(const CpuCoreManager&) = delete;
23 CpuCoreManager(CpuCoreManager&&) = delete; 23 CpuCoreManager(CpuCoreManager&&) = delete;
24 24
@@ -27,7 +27,8 @@ public:
27 CpuCoreManager& operator=(const CpuCoreManager&) = delete; 27 CpuCoreManager& operator=(const CpuCoreManager&) = delete;
28 CpuCoreManager& operator=(CpuCoreManager&&) = delete; 28 CpuCoreManager& operator=(CpuCoreManager&&) = delete;
29 29
30 void Initialize(System& system); 30 void Initialize();
31 void StartThreads();
31 void Shutdown(); 32 void Shutdown();
32 33
33 Cpu& GetCore(std::size_t index); 34 Cpu& GetCore(std::size_t index);
@@ -54,6 +55,8 @@ private:
54 55
55 /// Map of guest threads to CPU cores 56 /// Map of guest threads to CPU cores
56 std::map<std::thread::id, Cpu*> thread_to_cpu; 57 std::map<std::thread::id, Cpu*> thread_to_cpu;
58
59 System& system;
57}; 60};
58 61
59} // namespace Core 62} // namespace Core
diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp
new file mode 100644
index 000000000..4002a9211
--- /dev/null
+++ b/src/core/frontend/applets/error.cpp
@@ -0,0 +1,34 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/applets/error.h"
6
7namespace Core::Frontend {
8
9ErrorApplet::~ErrorApplet() = default;
10
11void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
12 LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
13 static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
14}
15
16void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
17 std::function<void()> finished) const {
18 LOG_CRITICAL(
19 Service_Fatal,
20 "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
21 static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count());
22}
23
24void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
25 std::string detail_text,
26 std::function<void()> finished) const {
27 LOG_CRITICAL(Service_Fatal,
28 "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
29 static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
30 LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text);
31 LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text);
32}
33
34} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h
new file mode 100644
index 000000000..699df940d
--- /dev/null
+++ b/src/core/frontend/applets/error.h
@@ -0,0 +1,37 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <chrono>
8#include <functional>
9
10#include "core/hle/result.h"
11
12namespace Core::Frontend {
13
14class ErrorApplet {
15public:
16 virtual ~ErrorApplet();
17
18 virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
19
20 virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
21 std::function<void()> finished) const = 0;
22
23 virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
24 std::string fullscreen_text,
25 std::function<void()> finished) const = 0;
26};
27
28class DefaultErrorApplet final : public ErrorApplet {
29public:
30 void ShowError(ResultCode error, std::function<void()> finished) const override;
31 void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
32 std::function<void()> finished) const override;
33 void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
34 std::function<void()> finished) const override;
35};
36
37} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp
new file mode 100644
index 000000000..b974f2289
--- /dev/null
+++ b/src/core/frontend/applets/general_frontend.cpp
@@ -0,0 +1,27 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/frontend/applets/general_frontend.h"
7
8namespace Core::Frontend {
9
10PhotoViewerApplet::~PhotoViewerApplet() = default;
11
12DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {}
13
14void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
15 std::function<void()> finished) const {
16 LOG_INFO(Service_AM,
17 "Application requested frontend to display stored photos for title_id={:016X}",
18 title_id);
19 finished();
20}
21
22void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const {
23 LOG_INFO(Service_AM, "Application requested frontend to display all stored photos.");
24 finished();
25}
26
27} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h
new file mode 100644
index 000000000..d4506c999
--- /dev/null
+++ b/src/core/frontend/applets/general_frontend.h
@@ -0,0 +1,28 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include "common/common_types.h"
9
10namespace Core::Frontend {
11
12class PhotoViewerApplet {
13public:
14 virtual ~PhotoViewerApplet();
15
16 virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0;
17 virtual void ShowAllPhotos(std::function<void()> finished) const = 0;
18};
19
20class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
21public:
22 ~DefaultPhotoViewerApplet() override;
23
24 void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
25 void ShowAllPhotos(std::function<void()> finished) const override;
26};
27
28} // namespace Core::Frontend
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4d58e7c69..757e5f21f 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -46,8 +46,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
46 46
47 bool resume = true; 47 bool resume = true;
48 48
49 if (thread->GetStatus() == ThreadStatus::WaitSynchAny || 49 if (thread->GetStatus() == ThreadStatus::WaitSynch ||
50 thread->GetStatus() == ThreadStatus::WaitSynchAll ||
51 thread->GetStatus() == ThreadStatus::WaitHLEEvent) { 50 thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
52 // Remove the thread from each of its waiting objects' waitlists 51 // Remove the thread from each of its waiting objects' waitlists
53 for (const auto& object : thread->GetWaitObjects()) { 52 for (const auto& object : thread->GetWaitObjects()) {
@@ -182,7 +181,12 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
182 181
183void KernelCore::MakeCurrentProcess(Process* process) { 182void KernelCore::MakeCurrentProcess(Process* process) {
184 impl->current_process = process; 183 impl->current_process = process;
185 Memory::SetCurrentPageTable(&process->VMManager().page_table); 184
185 if (process == nullptr) {
186 return;
187 }
188
189 Memory::SetCurrentPageTable(*process);
186} 190}
187 191
188Process* KernelCore::CurrentProcess() { 192Process* KernelCore::CurrentProcess() {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 4e94048da..0775a89fb 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -28,21 +28,20 @@ namespace {
28 * 28 *
29 * @param owner_process The parent process for the main thread 29 * @param owner_process The parent process for the main thread
30 * @param kernel The kernel instance to create the main thread under. 30 * @param kernel The kernel instance to create the main thread under.
31 * @param entry_point The address at which the thread should start execution
32 * @param priority The priority to give the main thread 31 * @param priority The priority to give the main thread
33 */ 32 */
34void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { 33void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
35 // Initialize new "main" thread 34 const auto& vm_manager = owner_process.VMManager();
36 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 35 const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress();
36 const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress();
37 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, 37 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
38 owner_process.GetIdealCore(), stack_top, owner_process); 38 owner_process.GetIdealCore(), stack_top, owner_process);
39 39
40 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 40 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
41 41
42 // Register 1 must be a handle to the main thread 42 // Register 1 must be a handle to the main thread
43 const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); 43 const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
44 thread->SetGuestHandle(guest_handle); 44 thread->GetContext().cpu_registers[1] = thread_handle;
45 thread->GetContext().cpu_registers[1] = guest_handle;
46 45
47 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 46 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
48 thread->ResumeFromWait(); 47 thread->ResumeFromWait();
@@ -106,8 +105,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
106 is_64bit_process = metadata.Is64BitProgram(); 105 is_64bit_process = metadata.Is64BitProgram();
107 106
108 vm_manager.Reset(metadata.GetAddressSpaceType()); 107 vm_manager.Reset(metadata.GetAddressSpaceType());
109 // Ensure that the potentially resized page table is seen by CPU backends.
110 Memory::SetCurrentPageTable(&vm_manager.page_table);
111 108
112 const auto& caps = metadata.GetKernelCapabilities(); 109 const auto& caps = metadata.GetKernelCapabilities();
113 const auto capability_init_result = 110 const auto capability_init_result =
@@ -119,7 +116,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
119 return handle_table.SetSize(capabilities.GetHandleTableSize()); 116 return handle_table.SetSize(capabilities.GetHandleTableSize());
120} 117}
121 118
122void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) { 119void Process::Run(s32 main_thread_priority, u64 stack_size) {
123 // The kernel always ensures that the given stack size is page aligned. 120 // The kernel always ensures that the given stack size is page aligned.
124 main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); 121 main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
125 122
@@ -135,7 +132,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
135 vm_manager.LogLayout(); 132 vm_manager.LogLayout();
136 ChangeStatus(ProcessStatus::Running); 133 ChangeStatus(ProcessStatus::Running);
137 134
138 SetupMainThread(*this, kernel, entry_point, main_thread_priority); 135 SetupMainThread(*this, kernel, main_thread_priority);
139} 136}
140 137
141void Process::PrepareForTermination() { 138void Process::PrepareForTermination() {
@@ -150,8 +147,7 @@ void Process::PrepareForTermination() {
150 continue; 147 continue;
151 148
152 // TODO(Subv): When are the other running/ready threads terminated? 149 // TODO(Subv): When are the other running/ready threads terminated?
153 ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny || 150 ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch,
154 thread->GetStatus() == ThreadStatus::WaitSynchAll,
155 "Exiting processes with non-waiting threads is currently unimplemented"); 151 "Exiting processes with non-waiting threads is currently unimplemented");
156 152
157 thread->Stop(); 153 thread->Stop();
@@ -242,13 +238,11 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
242 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData); 238 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
243 239
244 code_memory_size += module_.memory.size(); 240 code_memory_size += module_.memory.size();
245
246 // Clear instruction cache in CPU JIT
247 system.InvalidateCpuInstructionCaches();
248} 241}
249 242
250Process::Process(Core::System& system) 243Process::Process(Core::System& system)
251 : WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} 244 : WaitObject{system.Kernel()}, vm_manager{system},
245 address_arbiter{system}, mutex{system}, system{system} {}
252 246
253Process::~Process() = default; 247Process::~Process() = default;
254 248
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index dda52f4c0..bf3b7eef3 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -225,9 +225,12 @@ public:
225 ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); 225 ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
226 226
227 /** 227 /**
228 * Applies address space changes and launches the process main thread. 228 * Starts the main application thread for this process.
229 *
230 * @param main_thread_priority The priority for the main thread.
231 * @param stack_size The stack size for the main thread in bytes.
229 */ 232 */
230 void Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size); 233 void Run(s32 main_thread_priority, u64 stack_size);
231 234
232 /** 235 /**
233 * Prepares a process for termination by stopping all of its threads 236 * Prepares a process for termination by stopping all of its threads
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e5d4d6b55..2dcf174c5 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -424,7 +424,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
424/// Default thread wakeup callback for WaitSynchronization 424/// Default thread wakeup callback for WaitSynchronization
425static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, 425static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
426 SharedPtr<WaitObject> object, std::size_t index) { 426 SharedPtr<WaitObject> object, std::size_t index) {
427 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); 427 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
428 428
429 if (reason == ThreadWakeupReason::Timeout) { 429 if (reason == ThreadWakeupReason::Timeout) {
430 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); 430 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
@@ -502,7 +502,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
502 } 502 }
503 503
504 thread->SetWaitObjects(std::move(objects)); 504 thread->SetWaitObjects(std::move(objects));
505 thread->SetStatus(ThreadStatus::WaitSynchAny); 505 thread->SetStatus(ThreadStatus::WaitSynch);
506 506
507 // Create an event to wake the thread up after the specified nanosecond delay has passed 507 // Create an event to wake the thread up after the specified nanosecond delay has passed
508 thread->WakeAfterDelay(nano_seconds); 508 thread->WakeAfterDelay(nano_seconds);
@@ -518,16 +518,14 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
518 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); 518 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
519 519
520 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 520 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
521 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 521 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
522 if (!thread) { 522 if (!thread) {
523 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", 523 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
524 thread_handle); 524 thread_handle);
525 return ERR_INVALID_HANDLE; 525 return ERR_INVALID_HANDLE;
526 } 526 }
527 527
528 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); 528 thread->CancelWait();
529 thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
530 thread->ResumeFromWait();
531 return RESULT_SUCCESS; 529 return RESULT_SUCCESS;
532} 530}
533 531
@@ -1189,6 +1187,142 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
1189 query_address); 1187 query_address);
1190} 1188}
1191 1189
1190static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1191 u64 src_address, u64 size) {
1192 LOG_DEBUG(Kernel_SVC,
1193 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1194 "src_address=0x{:016X}, size=0x{:016X}",
1195 process_handle, dst_address, src_address, size);
1196
1197 if (!Common::Is4KBAligned(src_address)) {
1198 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1199 src_address);
1200 return ERR_INVALID_ADDRESS;
1201 }
1202
1203 if (!Common::Is4KBAligned(dst_address)) {
1204 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1205 dst_address);
1206 return ERR_INVALID_ADDRESS;
1207 }
1208
1209 if (size == 0 || !Common::Is4KBAligned(size)) {
1210 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1211 return ERR_INVALID_SIZE;
1212 }
1213
1214 if (!IsValidAddressRange(dst_address, size)) {
1215 LOG_ERROR(Kernel_SVC,
1216 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1217 "size=0x{:016X}).",
1218 dst_address, size);
1219 return ERR_INVALID_ADDRESS_STATE;
1220 }
1221
1222 if (!IsValidAddressRange(src_address, size)) {
1223 LOG_ERROR(Kernel_SVC,
1224 "Source address range overflows the address space (src_address=0x{:016X}, "
1225 "size=0x{:016X}).",
1226 src_address, size);
1227 return ERR_INVALID_ADDRESS_STATE;
1228 }
1229
1230 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1231 auto process = handle_table.Get<Process>(process_handle);
1232 if (!process) {
1233 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1234 process_handle);
1235 return ERR_INVALID_HANDLE;
1236 }
1237
1238 auto& vm_manager = process->VMManager();
1239 if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
1240 LOG_ERROR(Kernel_SVC,
1241 "Source address range is not within the address space (src_address=0x{:016X}, "
1242 "size=0x{:016X}).",
1243 src_address, size);
1244 return ERR_INVALID_ADDRESS_STATE;
1245 }
1246
1247 if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
1248 LOG_ERROR(Kernel_SVC,
1249 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1250 "size=0x{:016X}).",
1251 dst_address, size);
1252 return ERR_INVALID_MEMORY_RANGE;
1253 }
1254
1255 return vm_manager.MapCodeMemory(dst_address, src_address, size);
1256}
1257
1258ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1259 u64 src_address, u64 size) {
1260 LOG_DEBUG(Kernel_SVC,
1261 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1262 "size=0x{:016X}",
1263 process_handle, dst_address, src_address, size);
1264
1265 if (!Common::Is4KBAligned(dst_address)) {
1266 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1267 dst_address);
1268 return ERR_INVALID_ADDRESS;
1269 }
1270
1271 if (!Common::Is4KBAligned(src_address)) {
1272 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1273 src_address);
1274 return ERR_INVALID_ADDRESS;
1275 }
1276
1277 if (size == 0 || Common::Is4KBAligned(size)) {
1278 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1279 return ERR_INVALID_SIZE;
1280 }
1281
1282 if (!IsValidAddressRange(dst_address, size)) {
1283 LOG_ERROR(Kernel_SVC,
1284 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1285 "size=0x{:016X}).",
1286 dst_address, size);
1287 return ERR_INVALID_ADDRESS_STATE;
1288 }
1289
1290 if (!IsValidAddressRange(src_address, size)) {
1291 LOG_ERROR(Kernel_SVC,
1292 "Source address range overflows the address space (src_address=0x{:016X}, "
1293 "size=0x{:016X}).",
1294 src_address, size);
1295 return ERR_INVALID_ADDRESS_STATE;
1296 }
1297
1298 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1299 auto process = handle_table.Get<Process>(process_handle);
1300 if (!process) {
1301 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1302 process_handle);
1303 return ERR_INVALID_HANDLE;
1304 }
1305
1306 auto& vm_manager = process->VMManager();
1307 if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
1308 LOG_ERROR(Kernel_SVC,
1309 "Source address range is not within the address space (src_address=0x{:016X}, "
1310 "size=0x{:016X}).",
1311 src_address, size);
1312 return ERR_INVALID_ADDRESS_STATE;
1313 }
1314
1315 if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
1316 LOG_ERROR(Kernel_SVC,
1317 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1318 "size=0x{:016X}).",
1319 dst_address, size);
1320 return ERR_INVALID_MEMORY_RANGE;
1321 }
1322
1323 return vm_manager.UnmapCodeMemory(dst_address, src_address, size);
1324}
1325
1192/// Exits the current process 1326/// Exits the current process
1193static void ExitProcess(Core::System& system) { 1327static void ExitProcess(Core::System& system) {
1194 auto* current_process = system.Kernel().CurrentProcess(); 1328 auto* current_process = system.Kernel().CurrentProcess();
@@ -1244,20 +1378,22 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
1244 return ERR_INVALID_THREAD_PRIORITY; 1378 return ERR_INVALID_THREAD_PRIORITY;
1245 } 1379 }
1246 1380
1247 const std::string name = fmt::format("thread-{:X}", entry_point);
1248 auto& kernel = system.Kernel(); 1381 auto& kernel = system.Kernel();
1249 CASCADE_RESULT(SharedPtr<Thread> thread, 1382 CASCADE_RESULT(SharedPtr<Thread> thread,
1250 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 1383 Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top,
1251 *current_process)); 1384 *current_process));
1252 1385
1253 const auto new_guest_handle = current_process->GetHandleTable().Create(thread); 1386 const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
1254 if (new_guest_handle.Failed()) { 1387 if (new_thread_handle.Failed()) {
1255 LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}", 1388 LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
1256 new_guest_handle.Code().raw); 1389 new_thread_handle.Code().raw);
1257 return new_guest_handle.Code(); 1390 return new_thread_handle.Code();
1258 } 1391 }
1259 thread->SetGuestHandle(*new_guest_handle); 1392 *out_handle = *new_thread_handle;
1260 *out_handle = *new_guest_handle; 1393
1394 // Set the thread name for debugging purposes.
1395 thread->SetName(
1396 fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
1261 1397
1262 system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); 1398 system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
1263 1399
@@ -2152,7 +2288,7 @@ static const FunctionDef SVC_Table[] = {
2152 {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, 2288 {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
2153 {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"}, 2289 {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
2154 {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"}, 2290 {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
2155 {0x36, nullptr, "Unknown"}, 2291 {0x36, nullptr, "SynchronizePreemptionState"},
2156 {0x37, nullptr, "Unknown"}, 2292 {0x37, nullptr, "Unknown"},
2157 {0x38, nullptr, "Unknown"}, 2293 {0x38, nullptr, "Unknown"},
2158 {0x39, nullptr, "Unknown"}, 2294 {0x39, nullptr, "Unknown"},
@@ -2217,8 +2353,8 @@ static const FunctionDef SVC_Table[] = {
2217 {0x74, nullptr, "MapProcessMemory"}, 2353 {0x74, nullptr, "MapProcessMemory"},
2218 {0x75, nullptr, "UnmapProcessMemory"}, 2354 {0x75, nullptr, "UnmapProcessMemory"},
2219 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, 2355 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
2220 {0x77, nullptr, "MapProcessCodeMemory"}, 2356 {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2221 {0x78, nullptr, "UnmapProcessCodeMemory"}, 2357 {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
2222 {0x79, nullptr, "CreateProcess"}, 2358 {0x79, nullptr, "CreateProcess"},
2223 {0x7A, nullptr, "StartProcess"}, 2359 {0x7A, nullptr, "StartProcess"},
2224 {0x7B, nullptr, "TerminateProcess"}, 2360 {0x7B, nullptr, "TerminateProcess"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index b3690b5f3..865473c6f 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -44,6 +44,13 @@ void SvcWrap(Core::System& system) {
44 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); 44 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
45} 45}
46 46
47template <ResultCode func(Core::System&, u32, u64, u64, u64)>
48void SvcWrap(Core::System& system) {
49 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
50 Param(system, 2), Param(system, 3))
51 .raw);
52}
53
47template <ResultCode func(Core::System&, u32*)> 54template <ResultCode func(Core::System&, u32*)>
48void SvcWrap(Core::System& system) { 55void SvcWrap(Core::System& system) {
49 u32 param = 0; 56 u32 param = 0;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 1b891f632..2abf9efca 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -101,8 +101,7 @@ void Thread::ResumeFromWait() {
101 ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); 101 ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
102 102
103 switch (status) { 103 switch (status) {
104 case ThreadStatus::WaitSynchAll: 104 case ThreadStatus::WaitSynch:
105 case ThreadStatus::WaitSynchAny:
106 case ThreadStatus::WaitHLEEvent: 105 case ThreadStatus::WaitHLEEvent:
107 case ThreadStatus::WaitSleep: 106 case ThreadStatus::WaitSleep:
108 case ThreadStatus::WaitIPC: 107 case ThreadStatus::WaitIPC:
@@ -142,6 +141,12 @@ void Thread::ResumeFromWait() {
142 ChangeScheduler(); 141 ChangeScheduler();
143} 142}
144 143
144void Thread::CancelWait() {
145 ASSERT(GetStatus() == ThreadStatus::WaitSynch);
146 SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
147 ResumeFromWait();
148}
149
145/** 150/**
146 * Resets a thread context, making it ready to be scheduled and run by the CPU 151 * Resets a thread context, making it ready to be scheduled and run by the CPU
147 * @param context Thread context to reset 152 * @param context Thread context to reset
@@ -220,11 +225,6 @@ void Thread::SetPriority(u32 priority) {
220 UpdatePriority(); 225 UpdatePriority();
221} 226}
222 227
223void Thread::BoostPriority(u32 priority) {
224 scheduler->SetThreadPriority(this, priority);
225 current_priority = priority;
226}
227
228void Thread::SetWaitSynchronizationResult(ResultCode result) { 228void Thread::SetWaitSynchronizationResult(ResultCode result) {
229 context.cpu_registers[0] = result.raw; 229 context.cpu_registers[0] = result.raw;
230} 230}
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 83c83e45a..f07332f02 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -49,8 +49,7 @@ enum class ThreadStatus {
49 WaitHLEEvent, ///< Waiting for hle event to finish 49 WaitHLEEvent, ///< Waiting for hle event to finish
50 WaitSleep, ///< Waiting due to a SleepThread SVC 50 WaitSleep, ///< Waiting due to a SleepThread SVC
51 WaitIPC, ///< Waiting for the reply from an IPC request 51 WaitIPC, ///< Waiting for the reply from an IPC request
52 WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false 52 WaitSynch, ///< Waiting due to WaitSynchronization
53 WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
54 WaitMutex, ///< Waiting due to an ArbitrateLock svc 53 WaitMutex, ///< Waiting due to an ArbitrateLock svc
55 WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc 54 WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
56 WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc 55 WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
@@ -102,6 +101,11 @@ public:
102 std::string GetName() const override { 101 std::string GetName() const override {
103 return name; 102 return name;
104 } 103 }
104
105 void SetName(std::string new_name) {
106 name = std::move(new_name);
107 }
108
105 std::string GetTypeName() const override { 109 std::string GetTypeName() const override {
106 return "Thread"; 110 return "Thread";
107 } 111 }
@@ -136,12 +140,6 @@ public:
136 */ 140 */
137 void SetPriority(u32 priority); 141 void SetPriority(u32 priority);
138 142
139 /**
140 * Temporarily boosts the thread's priority until the next time it is scheduled
141 * @param priority The new priority
142 */
143 void BoostPriority(u32 priority);
144
145 /// Adds a thread to the list of threads that are waiting for a lock held by this thread. 143 /// Adds a thread to the list of threads that are waiting for a lock held by this thread.
146 void AddMutexWaiter(SharedPtr<Thread> thread); 144 void AddMutexWaiter(SharedPtr<Thread> thread);
147 145
@@ -170,11 +168,17 @@ public:
170 return tls_memory; 168 return tls_memory;
171 } 169 }
172 170
173 /** 171 /// Resumes a thread from waiting
174 * Resumes a thread from waiting
175 */
176 void ResumeFromWait(); 172 void ResumeFromWait();
177 173
174 /// Cancels a waiting operation that this thread may or may not be within.
175 ///
176 /// When the thread is within a waiting state, this will set the thread's
177 /// waiting result to signal a canceled wait. The function will then resume
178 /// this thread.
179 ///
180 void CancelWait();
181
178 /** 182 /**
179 * Schedules an event to wake up the specified thread after the specified delay 183 * Schedules an event to wake up the specified thread after the specified delay
180 * @param nanoseconds The time this thread will be allowed to sleep for 184 * @param nanoseconds The time this thread will be allowed to sleep for
@@ -185,24 +189,27 @@ public:
185 void CancelWakeupTimer(); 189 void CancelWakeupTimer();
186 190
187 /** 191 /**
188 * Sets the result after the thread awakens (from either WaitSynchronization SVC) 192 * Sets the result after the thread awakens (from svcWaitSynchronization)
189 * @param result Value to set to the returned result 193 * @param result Value to set to the returned result
190 */ 194 */
191 void SetWaitSynchronizationResult(ResultCode result); 195 void SetWaitSynchronizationResult(ResultCode result);
192 196
193 /** 197 /**
194 * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only) 198 * Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
195 * @param output Value to set to the output parameter 199 * @param output Value to set to the output parameter
196 */ 200 */
197 void SetWaitSynchronizationOutput(s32 output); 201 void SetWaitSynchronizationOutput(s32 output);
198 202
199 /** 203 /**
200 * Retrieves the index that this particular object occupies in the list of objects 204 * Retrieves the index that this particular object occupies in the list of objects
201 * that the thread passed to WaitSynchronizationN, starting the search from the last element. 205 * that the thread passed to WaitSynchronization, starting the search from the last element.
202 * It is used to set the output value of WaitSynchronizationN when the thread is awakened. 206 *
207 * It is used to set the output index of WaitSynchronization when the thread is awakened.
208 *
203 * When a thread wakes up due to an object signal, the kernel will use the index of the last 209 * When a thread wakes up due to an object signal, the kernel will use the index of the last
204 * matching object in the wait objects list in case of having multiple instances of the same 210 * matching object in the wait objects list in case of having multiple instances of the same
205 * object in the list. 211 * object in the list.
212 *
206 * @param object Object to query the index of. 213 * @param object Object to query the index of.
207 */ 214 */
208 s32 GetWaitObjectIndex(const WaitObject* object) const; 215 s32 GetWaitObjectIndex(const WaitObject* object) const;
@@ -239,13 +246,9 @@ public:
239 */ 246 */
240 VAddr GetCommandBufferAddress() const; 247 VAddr GetCommandBufferAddress() const;
241 248
242 /** 249 /// Returns whether this thread is waiting on objects from a WaitSynchronization call.
243 * Returns whether this thread is waiting for all the objects in 250 bool IsSleepingOnWait() const {
244 * its wait list to become ready, as a result of a WaitSynchronizationN call 251 return status == ThreadStatus::WaitSynch;
245 * with wait_all = true.
246 */
247 bool IsSleepingOnWaitAll() const {
248 return status == ThreadStatus::WaitSynchAll;
249 } 252 }
250 253
251 ThreadContext& GetContext() { 254 ThreadContext& GetContext() {
@@ -345,10 +348,6 @@ public:
345 arb_wait_address = address; 348 arb_wait_address = address;
346 } 349 }
347 350
348 void SetGuestHandle(Handle handle) {
349 guest_handle = handle;
350 }
351
352 bool HasWakeupCallback() const { 351 bool HasWakeupCallback() const {
353 return wakeup_callback != nullptr; 352 return wakeup_callback != nullptr;
354 } 353 }
@@ -423,7 +422,7 @@ private:
423 Process* owner_process; 422 Process* owner_process;
424 423
425 /// Objects that the thread is waiting on, in the same order as they were 424 /// Objects that the thread is waiting on, in the same order as they were
426 /// passed to WaitSynchronization1/N. 425 /// passed to WaitSynchronization.
427 ThreadWaitObjects wait_objects; 426 ThreadWaitObjects wait_objects;
428 427
429 /// List of threads that are waiting for a mutex that is held by this thread. 428 /// List of threads that are waiting for a mutex that is held by this thread.
@@ -442,14 +441,11 @@ private:
442 /// If waiting for an AddressArbiter, this is the address being waited on. 441 /// If waiting for an AddressArbiter, this is the address being waited on.
443 VAddr arb_wait_address{0}; 442 VAddr arb_wait_address{0};
444 443
445 /// Handle used by guest emulated application to access this thread
446 Handle guest_handle = 0;
447
448 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 444 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
449 Handle callback_handle = 0; 445 Handle callback_handle = 0;
450 446
451 /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread 447 /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
452 /// was waiting via WaitSynchronizationN then the object will be the last object that became 448 /// was waiting via WaitSynchronization then the object will be the last object that became
453 /// available. In case of a timeout, the object will be nullptr. 449 /// available. In case of a timeout, the object will be nullptr.
454 WakeupCallback wakeup_callback; 450 WakeupCallback wakeup_callback;
455 451
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index ec0a480ce..48b13cfdd 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -62,7 +62,7 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
62 return true; 62 return true;
63} 63}
64 64
65VMManager::VMManager() { 65VMManager::VMManager(Core::System& system) : system{system} {
66 // Default to assuming a 39-bit address space. This way we have a sane 66 // Default to assuming a 39-bit address space. This way we have a sane
67 // starting point with executables that don't provide metadata. 67 // starting point with executables that don't provide metadata.
68 Reset(FileSys::ProgramAddressSpaceType::Is39Bit); 68 Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
@@ -111,7 +111,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
111 VirtualMemoryArea& final_vma = vma_handle->second; 111 VirtualMemoryArea& final_vma = vma_handle->second;
112 ASSERT(final_vma.size == size); 112 ASSERT(final_vma.size == size);
113 113
114 auto& system = Core::System::GetInstance();
115 system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset, 114 system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
116 VMAPermission::ReadWriteExecute); 115 VMAPermission::ReadWriteExecute);
117 system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset, 116 system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
@@ -140,7 +139,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
140 VirtualMemoryArea& final_vma = vma_handle->second; 139 VirtualMemoryArea& final_vma = vma_handle->second;
141 ASSERT(final_vma.size == size); 140 ASSERT(final_vma.size == size);
142 141
143 auto& system = Core::System::GetInstance();
144 system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); 142 system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
145 system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); 143 system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
146 system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); 144 system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
@@ -223,7 +221,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
223 221
224 ASSERT(FindVMA(target)->second.size >= size); 222 ASSERT(FindVMA(target)->second.size >= size);
225 223
226 auto& system = Core::System::GetInstance();
227 system.ArmInterface(0).UnmapMemory(target, size); 224 system.ArmInterface(0).UnmapMemory(target, size);
228 system.ArmInterface(1).UnmapMemory(target, size); 225 system.ArmInterface(1).UnmapMemory(target, size);
229 system.ArmInterface(2).UnmapMemory(target, size); 226 system.ArmInterface(2).UnmapMemory(target, size);
@@ -302,6 +299,86 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
302 return MakeResult<VAddr>(heap_region_base); 299 return MakeResult<VAddr>(heap_region_base);
303} 300}
304 301
302ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
303 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
304 const auto src_check_result = CheckRangeState(
305 src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All,
306 VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
307
308 if (src_check_result.Failed()) {
309 return src_check_result.Code();
310 }
311
312 const auto mirror_result =
313 MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode);
314 if (mirror_result.IsError()) {
315 return mirror_result;
316 }
317
318 // Ensure we lock the source memory region.
319 const auto src_vma_result = CarveVMARange(src_address, size);
320 if (src_vma_result.Failed()) {
321 return src_vma_result.Code();
322 }
323 auto src_vma_iter = *src_vma_result;
324 src_vma_iter->second.attribute = MemoryAttribute::Locked;
325 Reprotect(src_vma_iter, VMAPermission::Read);
326
327 // The destination memory region is fine as is, however we need to make it read-only.
328 return ReprotectRange(dst_address, size, VMAPermission::Read);
329}
330
331ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
332 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
333 const auto src_check_result = CheckRangeState(
334 src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None,
335 VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute);
336
337 if (src_check_result.Failed()) {
338 return src_check_result.Code();
339 }
340
341 // Yes, the kernel only checks the first page of the region.
342 const auto dst_check_result =
343 CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule,
344 MemoryState::FlagModule, VMAPermission::None, VMAPermission::None,
345 MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
346
347 if (dst_check_result.Failed()) {
348 return dst_check_result.Code();
349 }
350
351 const auto dst_memory_state = std::get<MemoryState>(*dst_check_result);
352 const auto dst_contiguous_check_result = CheckRangeState(
353 dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None,
354 VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
355
356 if (dst_contiguous_check_result.Failed()) {
357 return dst_contiguous_check_result.Code();
358 }
359
360 const auto unmap_result = UnmapRange(dst_address, size);
361 if (unmap_result.IsError()) {
362 return unmap_result;
363 }
364
365 // With the mirrored portion unmapped, restore the original region's traits.
366 const auto src_vma_result = CarveVMARange(src_address, size);
367 if (src_vma_result.Failed()) {
368 return src_vma_result.Code();
369 }
370 auto src_vma_iter = *src_vma_result;
371 src_vma_iter->second.state = MemoryState::Heap;
372 src_vma_iter->second.attribute = MemoryAttribute::None;
373 Reprotect(src_vma_iter, VMAPermission::ReadWrite);
374
375 if (dst_memory_state == MemoryState::ModuleCode) {
376 system.InvalidateCpuInstructionCaches();
377 }
378
379 return unmap_result;
380}
381
305MemoryInfo VMManager::QueryMemory(VAddr address) const { 382MemoryInfo VMManager::QueryMemory(VAddr address) const {
306 const auto vma = FindVMA(address); 383 const auto vma = FindVMA(address);
307 MemoryInfo memory_info{}; 384 MemoryInfo memory_info{};
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 6f484b7bf..ec84d9a70 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -14,6 +14,10 @@
14#include "core/hle/result.h" 14#include "core/hle/result.h"
15#include "core/memory.h" 15#include "core/memory.h"
16 16
17namespace Core {
18class System;
19}
20
17namespace FileSys { 21namespace FileSys {
18enum class ProgramAddressSpaceType : u8; 22enum class ProgramAddressSpaceType : u8;
19} 23}
@@ -43,6 +47,9 @@ enum class VMAPermission : u8 {
43 ReadExecute = Read | Execute, 47 ReadExecute = Read | Execute,
44 WriteExecute = Write | Execute, 48 WriteExecute = Write | Execute,
45 ReadWriteExecute = Read | Write | Execute, 49 ReadWriteExecute = Read | Write | Execute,
50
51 // Used as a wildcard when checking permissions across memory ranges
52 All = 0xFF,
46}; 53};
47 54
48constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { 55constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
@@ -152,6 +159,9 @@ enum class MemoryState : u32 {
152 FlagUncached = 1U << 24, 159 FlagUncached = 1U << 24,
153 FlagCodeMemory = 1U << 25, 160 FlagCodeMemory = 1U << 25,
154 161
162 // Wildcard used in range checking to indicate all states.
163 All = 0xFFFFFFFF,
164
155 // Convenience flag sets to reduce repetition 165 // Convenience flag sets to reduce repetition
156 IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, 166 IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
157 167
@@ -315,7 +325,7 @@ class VMManager final {
315public: 325public:
316 using VMAHandle = VMAMap::const_iterator; 326 using VMAHandle = VMAMap::const_iterator;
317 327
318 VMManager(); 328 explicit VMManager(Core::System& system);
319 ~VMManager(); 329 ~VMManager();
320 330
321 /// Clears the address space map, re-initializing with a single free area. 331 /// Clears the address space map, re-initializing with a single free area.
@@ -415,6 +425,49 @@ public:
415 /// 425 ///
416 ResultVal<VAddr> SetHeapSize(u64 size); 426 ResultVal<VAddr> SetHeapSize(u64 size);
417 427
428 /// Maps a region of memory as code memory.
429 ///
430 /// @param dst_address The base address of the region to create the aliasing memory region.
431 /// @param src_address The base address of the region to be aliased.
432 /// @param size The total amount of memory to map in bytes.
433 ///
434 /// @pre Both memory regions lie within the actual addressable address space.
435 ///
436 /// @post After this function finishes execution, assuming success, then the address range
437 /// [dst_address, dst_address+size) will alias the memory region,
438 /// [src_address, src_address+size).
439 /// <p>
440 /// What this also entails is as follows:
441 /// 1. The aliased region gains the Locked memory attribute.
442 /// 2. The aliased region becomes read-only.
443 /// 3. The aliasing region becomes read-only.
444 /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule.
445 ///
446 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
447
448 /// Unmaps a region of memory designated as code module memory.
449 ///
450 /// @param dst_address The base address of the memory region aliasing the source memory region.
451 /// @param src_address The base address of the memory region being aliased.
452 /// @param size The size of the memory region to unmap in bytes.
453 ///
454 /// @pre Both memory ranges lie within the actual addressable address space.
455 ///
456 /// @pre The memory region being unmapped has been previously been mapped
457 /// by a call to MapCodeMemory.
458 ///
459 /// @post After execution of the function, if successful. the aliasing memory region
460 /// will be unmapped and the aliased region will have various traits about it
461 /// restored to what they were prior to the original mapping call preceding
462 /// this function call.
463 /// <p>
464 /// What this also entails is as follows:
465 /// 1. The state of the memory region will now indicate a general heap region.
466 /// 2. All memory attributes for the memory region are cleared.
467 /// 3. Memory permissions for the region are restored to user read/write.
468 ///
469 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
470
418 /// Queries the memory manager for information about the given address. 471 /// Queries the memory manager for information about the given address.
419 /// 472 ///
420 /// @param address The address to query the memory manager about for information. 473 /// @param address The address to query the memory manager about for information.
@@ -663,5 +716,7 @@ private:
663 // The end of the currently allocated heap. This is not an inclusive 716 // The end of the currently allocated heap. This is not an inclusive
664 // end of the range. This is essentially 'base_address + current_size'. 717 // end of the range. This is essentially 'base_address + current_size'.
665 VAddr heap_end = 0; 718 VAddr heap_end = 0;
719
720 Core::System& system;
666}; 721};
667} // namespace Kernel 722} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index 90580ed93..0e96ba872 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -30,7 +30,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) {
30 waiting_threads.erase(itr); 30 waiting_threads.erase(itr);
31} 31}
32 32
33SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { 33SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
34 Thread* candidate = nullptr; 34 Thread* candidate = nullptr;
35 u32 candidate_priority = THREADPRIO_LOWEST + 1; 35 u32 candidate_priority = THREADPRIO_LOWEST + 1;
36 36
@@ -38,8 +38,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
38 const ThreadStatus thread_status = thread->GetStatus(); 38 const ThreadStatus thread_status = thread->GetStatus();
39 39
40 // The list of waiting threads must not contain threads that are not waiting to be awakened. 40 // The list of waiting threads must not contain threads that are not waiting to be awakened.
41 ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny || 41 ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
42 thread_status == ThreadStatus::WaitSynchAll ||
43 thread_status == ThreadStatus::WaitHLEEvent, 42 thread_status == ThreadStatus::WaitHLEEvent,
44 "Inconsistent thread statuses in waiting_threads"); 43 "Inconsistent thread statuses in waiting_threads");
45 44
@@ -49,10 +48,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
49 if (ShouldWait(thread.get())) 48 if (ShouldWait(thread.get()))
50 continue; 49 continue;
51 50
52 // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or 51 // A thread is ready to run if it's either in ThreadStatus::WaitSynch
53 // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready. 52 // and the rest of the objects it is waiting on are ready.
54 bool ready_to_run = true; 53 bool ready_to_run = true;
55 if (thread_status == ThreadStatus::WaitSynchAll) { 54 if (thread_status == ThreadStatus::WaitSynch) {
56 ready_to_run = thread->AllWaitObjectsReady(); 55 ready_to_run = thread->AllWaitObjectsReady();
57 } 56 }
58 57
@@ -68,33 +67,35 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
68void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { 67void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
69 ASSERT(!ShouldWait(thread.get())); 68 ASSERT(!ShouldWait(thread.get()));
70 69
71 if (!thread) 70 if (!thread) {
72 return; 71 return;
72 }
73 73
74 if (!thread->IsSleepingOnWaitAll()) { 74 if (thread->IsSleepingOnWait()) {
75 Acquire(thread.get());
76 } else {
77 for (const auto& object : thread->GetWaitObjects()) { 75 for (const auto& object : thread->GetWaitObjects()) {
78 ASSERT(!object->ShouldWait(thread.get())); 76 ASSERT(!object->ShouldWait(thread.get()));
79 object->Acquire(thread.get()); 77 object->Acquire(thread.get());
80 } 78 }
79 } else {
80 Acquire(thread.get());
81 } 81 }
82 82
83 const std::size_t index = thread->GetWaitObjectIndex(this); 83 const std::size_t index = thread->GetWaitObjectIndex(this);
84 84
85 for (const auto& object : thread->GetWaitObjects()) 85 for (const auto& object : thread->GetWaitObjects()) {
86 object->RemoveWaitingThread(thread.get()); 86 object->RemoveWaitingThread(thread.get());
87 }
87 thread->ClearWaitObjects(); 88 thread->ClearWaitObjects();
88 89
89 thread->CancelWakeupTimer(); 90 thread->CancelWakeupTimer();
90 91
91 bool resume = true; 92 bool resume = true;
92 93 if (thread->HasWakeupCallback()) {
93 if (thread->HasWakeupCallback())
94 resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index); 94 resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
95 95 }
96 if (resume) 96 if (resume) {
97 thread->ResumeFromWait(); 97 thread->ResumeFromWait();
98 }
98} 99}
99 100
100void WaitObject::WakeupAllWaitingThreads() { 101void WaitObject::WakeupAllWaitingThreads() {
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
index 04464a51a..3271a30a7 100644
--- a/src/core/hle/kernel/wait_object.h
+++ b/src/core/hle/kernel/wait_object.h
@@ -54,7 +54,7 @@ public:
54 void WakeupWaitingThread(SharedPtr<Thread> thread); 54 void WakeupWaitingThread(SharedPtr<Thread> thread);
55 55
56 /// Obtains the highest priority thread that is ready to run from this object's waiting list. 56 /// Obtains the highest priority thread that is ready to run from this object's waiting list.
57 SharedPtr<Thread> GetHighestPriorityReadyThread(); 57 SharedPtr<Thread> GetHighestPriorityReadyThread() const;
58 58
59 /// Get a const reference to the waiting threads list for debug use 59 /// Get a const reference to the waiting threads list for debug use
60 const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; 60 const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 1f8ed265e..ba7d7acbd 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -137,6 +137,7 @@ private:
137class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { 137class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
138public: 138public:
139 IManagerForApplication() : ServiceFramework("IManagerForApplication") { 139 IManagerForApplication() : ServiceFramework("IManagerForApplication") {
140 // clang-format off
140 static const FunctionInfo functions[] = { 141 static const FunctionInfo functions[] = {
141 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, 142 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
142 {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, 143 {1, &IManagerForApplication::GetAccountId, "GetAccountId"},
@@ -145,7 +146,10 @@ public:
145 {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, 146 {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
146 {150, nullptr, "CreateAuthorizationRequest"}, 147 {150, nullptr, "CreateAuthorizationRequest"},
147 {160, nullptr, "StoreOpenContext"}, 148 {160, nullptr, "StoreOpenContext"},
149 {170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
148 }; 150 };
151 // clang-format on
152
149 RegisterHandlers(functions); 153 RegisterHandlers(functions);
150 } 154 }
151 155
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index 5e2030355..d66233cad 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -8,6 +8,7 @@ namespace Service::Account {
8 8
9ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) 9ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") { 10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &ACC_SU::GetUserCount, "GetUserCount"}, 13 {0, &ACC_SU::GetUserCount, "GetUserCount"},
13 {1, &ACC_SU::GetUserExistence, "GetUserExistence"}, 14 {1, &ACC_SU::GetUserExistence, "GetUserExistence"},
@@ -19,6 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
19 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 20 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 21 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 22 {60, nullptr, "ListOpenContextStoredUsers"},
23 {99, nullptr, "DebugActivateOpenContextRetention"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 24 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 25 {101, nullptr, "GetUserStateChangeNotifier"},
24 {102, nullptr, "GetBaasAccountManagerForSystemService"}, 26 {102, nullptr, "GetBaasAccountManagerForSystemService"},
@@ -29,6 +31,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
29 {111, nullptr, "ClearSaveDataThumbnail"}, 31 {111, nullptr, "ClearSaveDataThumbnail"},
30 {112, nullptr, "LoadSaveDataThumbnail"}, 32 {112, nullptr, "LoadSaveDataThumbnail"},
31 {113, nullptr, "GetSaveDataThumbnailExistence"}, 33 {113, nullptr, "GetSaveDataThumbnailExistence"},
34 {130, nullptr, "ActivateOpenContextRetention"},
35 {140, nullptr, "ListQualifiedUsers"},
32 {190, nullptr, "GetUserLastOpenedApplication"}, 36 {190, nullptr, "GetUserLastOpenedApplication"},
33 {191, nullptr, "ActivateOpenContextHolder"}, 37 {191, nullptr, "ActivateOpenContextHolder"},
34 {200, nullptr, "BeginUserRegistration"}, 38 {200, nullptr, "BeginUserRegistration"},
@@ -48,6 +52,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
48 {998, nullptr, "DebugSetUserStateClose"}, 52 {998, nullptr, "DebugSetUserStateClose"},
49 {999, nullptr, "DebugSetUserStateOpen"}, 53 {999, nullptr, "DebugSetUserStateOpen"},
50 }; 54 };
55 // clang-format on
56
51 RegisterHandlers(functions); 57 RegisterHandlers(functions);
52} 58}
53 59
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index a4d705b45..182f7c7e5 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -8,6 +8,7 @@ namespace Service::Account {
8 8
9ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) 9ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") { 10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &ACC_U0::GetUserCount, "GetUserCount"}, 13 {0, &ACC_U0::GetUserCount, "GetUserCount"},
13 {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, 14 {1, &ACC_U0::GetUserExistence, "GetUserExistence"},
@@ -19,6 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
19 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 20 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 21 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 22 {60, nullptr, "ListOpenContextStoredUsers"},
23 {99, nullptr, "DebugActivateOpenContextRetention"},
22 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, 24 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
23 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, 25 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
24 {102, nullptr, "AuthenticateApplicationAsync"}, 26 {102, nullptr, "AuthenticateApplicationAsync"},
@@ -27,7 +29,13 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
27 {111, nullptr, "ClearSaveDataThumbnail"}, 29 {111, nullptr, "ClearSaveDataThumbnail"},
28 {120, nullptr, "CreateGuestLoginRequest"}, 30 {120, nullptr, "CreateGuestLoginRequest"},
29 {130, nullptr, "LoadOpenContext"}, 31 {130, nullptr, "LoadOpenContext"},
32 {131, nullptr, "ListOpenContextStoredUsers"},
33 {140, nullptr, "InitializeApplicationInfo"},
34 {141, nullptr, "ListQualifiedUsers"},
35 {150, nullptr, "IsUserAccountSwitchLocked"},
30 }; 36 };
37 // clang-format on
38
31 RegisterHandlers(functions); 39 RegisterHandlers(functions);
32} 40}
33 41
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 8fffc93b5..2dd17d935 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -8,6 +8,7 @@ namespace Service::Account {
8 8
9ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) 9ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") { 10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &ACC_U1::GetUserCount, "GetUserCount"}, 13 {0, &ACC_U1::GetUserCount, "GetUserCount"},
13 {1, &ACC_U1::GetUserExistence, "GetUserExistence"}, 14 {1, &ACC_U1::GetUserExistence, "GetUserExistence"},
@@ -19,6 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
19 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 20 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 21 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 22 {60, nullptr, "ListOpenContextStoredUsers"},
23 {99, nullptr, "DebugActivateOpenContextRetention"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 24 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 25 {101, nullptr, "GetUserStateChangeNotifier"},
24 {102, nullptr, "GetBaasAccountManagerForSystemService"}, 26 {102, nullptr, "GetBaasAccountManagerForSystemService"},
@@ -29,12 +31,16 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
29 {111, nullptr, "ClearSaveDataThumbnail"}, 31 {111, nullptr, "ClearSaveDataThumbnail"},
30 {112, nullptr, "LoadSaveDataThumbnail"}, 32 {112, nullptr, "LoadSaveDataThumbnail"},
31 {113, nullptr, "GetSaveDataThumbnailExistence"}, 33 {113, nullptr, "GetSaveDataThumbnailExistence"},
34 {130, nullptr, "ActivateOpenContextRetention"},
35 {140, nullptr, "ListQualifiedUsers"},
32 {190, nullptr, "GetUserLastOpenedApplication"}, 36 {190, nullptr, "GetUserLastOpenedApplication"},
33 {191, nullptr, "ActivateOpenContextHolder"}, 37 {191, nullptr, "ActivateOpenContextHolder"},
34 {997, nullptr, "DebugInvalidateTokenCacheForUser"}, 38 {997, nullptr, "DebugInvalidateTokenCacheForUser"},
35 {998, nullptr, "DebugSetUserStateClose"}, 39 {998, nullptr, "DebugSetUserStateClose"},
36 {999, nullptr, "DebugSetUserStateOpen"}, 40 {999, nullptr, "DebugSetUserStateOpen"},
37 }; 41 };
42 // clang-format on
43
38 RegisterHandlers(functions); 44 RegisterHandlers(functions);
39} 45}
40 46
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 85271d418..26a665bfd 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -22,7 +22,6 @@
22#include "core/hle/service/am/applets/applets.h" 22#include "core/hle/service/am/applets/applets.h"
23#include "core/hle/service/am/applets/profile_select.h" 23#include "core/hle/service/am/applets/profile_select.h"
24#include "core/hle/service/am/applets/software_keyboard.h" 24#include "core/hle/service/am/applets/software_keyboard.h"
25#include "core/hle/service/am/applets/stub_applet.h"
26#include "core/hle/service/am/applets/web_browser.h" 25#include "core/hle/service/am/applets/web_browser.h"
27#include "core/hle/service/am/idle.h" 26#include "core/hle/service/am/idle.h"
28#include "core/hle/service/am/omm.h" 27#include "core/hle/service/am/omm.h"
@@ -42,12 +41,6 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
42constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; 41constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
43constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; 42constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
44 43
45enum class AppletId : u32 {
46 ProfileSelect = 0x10,
47 SoftwareKeyboard = 0x11,
48 LibAppletOff = 0x17,
49};
50
51constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; 44constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
52 45
53struct LaunchParameters { 46struct LaunchParameters {
@@ -224,6 +217,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
224 {20, nullptr, "InvalidateTransitionLayer"}, 217 {20, nullptr, "InvalidateTransitionLayer"},
225 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, 218 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
226 {40, nullptr, "GetAppletResourceUsageInfo"}, 219 {40, nullptr, "GetAppletResourceUsageInfo"},
220 {41, nullptr, "SetCpuBoostModeForApplet"},
227 }; 221 };
228 // clang-format on 222 // clang-format on
229 223
@@ -256,6 +250,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
256 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, 250 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
257 {41, nullptr, "IsSystemBufferSharingEnabled"}, 251 {41, nullptr, "IsSystemBufferSharingEnabled"},
258 {42, nullptr, "GetSystemSharedLayerHandle"}, 252 {42, nullptr, "GetSystemSharedLayerHandle"},
253 {43, nullptr, "GetSystemSharedBufferHandle"},
259 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, 254 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
260 {51, nullptr, "ApproveToDisplay"}, 255 {51, nullptr, "ApproveToDisplay"},
261 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, 256 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
@@ -269,9 +264,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
269 {68, nullptr, "SetAutoSleepDisabled"}, 264 {68, nullptr, "SetAutoSleepDisabled"},
270 {69, nullptr, "IsAutoSleepDisabled"}, 265 {69, nullptr, "IsAutoSleepDisabled"},
271 {70, nullptr, "ReportMultimediaError"}, 266 {70, nullptr, "ReportMultimediaError"},
267 {71, nullptr, "GetCurrentIlluminanceEx"},
272 {80, nullptr, "SetWirelessPriorityMode"}, 268 {80, nullptr, "SetWirelessPriorityMode"},
273 {90, nullptr, "GetAccumulatedSuspendedTickValue"}, 269 {90, nullptr, "GetAccumulatedSuspendedTickValue"},
274 {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, 270 {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
271 {100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
275 {1000, nullptr, "GetDebugStorageChannel"}, 272 {1000, nullptr, "GetDebugStorageChannel"},
276 }; 273 };
277 // clang-format on 274 // clang-format on
@@ -516,11 +513,20 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q
516 {50, nullptr, "IsVrModeEnabled"}, 513 {50, nullptr, "IsVrModeEnabled"},
517 {51, nullptr, "SetVrModeEnabled"}, 514 {51, nullptr, "SetVrModeEnabled"},
518 {52, nullptr, "SwitchLcdBacklight"}, 515 {52, nullptr, "SwitchLcdBacklight"},
516 {53, nullptr, "BeginVrModeEx"},
517 {54, nullptr, "EndVrModeEx"},
519 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 518 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
520 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, 519 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
521 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, 520 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
522 {62, nullptr, "GetHdcpAuthenticationState"}, 521 {62, nullptr, "GetHdcpAuthenticationState"},
523 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, 522 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
523 {64, nullptr, "SetTvPowerStateMatchingMode"},
524 {65, nullptr, "GetApplicationIdByContentActionName"},
525 {66, nullptr, "SetCpuBoostMode"},
526 {80, nullptr, "PerformSystemButtonPressingIfInFocus"},
527 {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
528 {91, nullptr, "GetCurrentPerformanceConfiguration"},
529 {200, nullptr, "GetOperationModeSystemInfo"},
524 }; 530 };
525 // clang-format on 531 // clang-format on
526 532
@@ -873,30 +879,16 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
873 879
874ILibraryAppletCreator::~ILibraryAppletCreator() = default; 880ILibraryAppletCreator::~ILibraryAppletCreator() = default;
875 881
876static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
877 switch (id) {
878 case AppletId::ProfileSelect:
879 return std::make_shared<Applets::ProfileSelect>();
880 case AppletId::SoftwareKeyboard:
881 return std::make_shared<Applets::SoftwareKeyboard>();
882 case AppletId::LibAppletOff:
883 return std::make_shared<Applets::WebBrowser>();
884 default:
885 LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
886 static_cast<u32>(id));
887 return std::make_shared<Applets::StubApplet>();
888 }
889}
890
891void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { 882void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
892 IPC::RequestParser rp{ctx}; 883 IPC::RequestParser rp{ctx};
893 const auto applet_id = rp.PopRaw<AppletId>(); 884 const auto applet_id = rp.PopRaw<Applets::AppletId>();
894 const auto applet_mode = rp.PopRaw<u32>(); 885 const auto applet_mode = rp.PopRaw<u32>();
895 886
896 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", 887 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
897 static_cast<u32>(applet_id), applet_mode); 888 static_cast<u32>(applet_id), applet_mode);
898 889
899 const auto applet = GetAppletFromId(applet_id); 890 const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
891 const auto applet = applet_manager.GetApplet(applet_id);
900 892
901 if (applet == nullptr) { 893 if (applet == nullptr) {
902 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id)); 894 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
@@ -960,6 +952,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
960 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, 952 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
961 {12, nullptr, "CreateApplicationAndRequestToStart"}, 953 {12, nullptr, "CreateApplicationAndRequestToStart"},
962 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, 954 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
955 {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
956 {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
963 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, 957 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
964 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, 958 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
965 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, 959 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -1233,6 +1227,7 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
1233 {2, nullptr, "StartSleepSequence"}, 1227 {2, nullptr, "StartSleepSequence"},
1234 {3, nullptr, "StartShutdownSequence"}, 1228 {3, nullptr, "StartShutdownSequence"},
1235 {4, nullptr, "StartRebootSequence"}, 1229 {4, nullptr, "StartRebootSequence"},
1230 {9, nullptr, "IsAutoPowerDownRequested"},
1236 {10, nullptr, "LoadAndApplyIdlePolicySettings"}, 1231 {10, nullptr, "LoadAndApplyIdlePolicySettings"},
1237 {11, nullptr, "NotifyCecSettingsChanged"}, 1232 {11, nullptr, "NotifyCecSettingsChanged"},
1238 {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, 1233 {12, nullptr, "SetDefaultHomeButtonLongPressTime"},
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index b888f861d..488add8e7 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -16,6 +16,7 @@ public:
16 std::shared_ptr<AppletMessageQueue> msg_queue) 16 std::shared_ptr<AppletMessageQueue> msg_queue)
17 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), 17 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) { 18 msg_queue(std::move(msg_queue)) {
19 // clang-format off
19 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
20 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 21 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
21 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, 22 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
@@ -25,8 +26,11 @@ public:
25 {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, 26 {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
26 {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, 27 {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
27 {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"}, 28 {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"},
29 {21, nullptr, "GetAppletCommonFunctions"},
28 {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, 30 {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
29 }; 31 };
32 // clang-format on
33
30 RegisterHandlers(functions); 34 RegisterHandlers(functions);
31 } 35 }
32 36
@@ -113,6 +117,7 @@ public:
113 std::shared_ptr<AppletMessageQueue> msg_queue) 117 std::shared_ptr<AppletMessageQueue> msg_queue)
114 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), 118 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
115 msg_queue(std::move(msg_queue)) { 119 msg_queue(std::move(msg_queue)) {
120 // clang-format off
116 static const FunctionInfo functions[] = { 121 static const FunctionInfo functions[] = {
117 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 122 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
118 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, 123 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
@@ -124,8 +129,11 @@ public:
124 {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, 129 {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
125 {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, 130 {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
126 {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, 131 {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
132 {23, nullptr, "GetAppletCommonFunctions"},
127 {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, 133 {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
128 }; 134 };
135 // clang-format on
136
129 RegisterHandlers(functions); 137 RegisterHandlers(functions);
130 } 138 }
131 139
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index a6064c63f..7f70b10df 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,11 +5,21 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/applets/error.h"
9#include "core/frontend/applets/general_frontend.h"
10#include "core/frontend/applets/profile_select.h"
11#include "core/frontend/applets/software_keyboard.h"
12#include "core/frontend/applets/web_browser.h"
8#include "core/hle/kernel/readable_event.h" 13#include "core/hle/kernel/readable_event.h"
9#include "core/hle/kernel/server_session.h" 14#include "core/hle/kernel/server_session.h"
10#include "core/hle/kernel/writable_event.h" 15#include "core/hle/kernel/writable_event.h"
11#include "core/hle/service/am/am.h" 16#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/applets.h" 17#include "core/hle/service/am/applets/applets.h"
18#include "core/hle/service/am/applets/error.h"
19#include "core/hle/service/am/applets/general_backend.h"
20#include "core/hle/service/am/applets/profile_select.h"
21#include "core/hle/service/am/applets/software_keyboard.h"
22#include "core/hle/service/am/applets/web_browser.h"
13 23
14namespace Service::AM::Applets { 24namespace Service::AM::Applets {
15 25
@@ -111,4 +121,76 @@ void Applet::Initialize() {
111 initialized = true; 121 initialized = true;
112} 122}
113 123
124AppletManager::AppletManager() = default;
125
126AppletManager::~AppletManager() = default;
127
128void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
129 if (set.error != nullptr)
130 frontend.error = std::move(set.error);
131 if (set.photo_viewer != nullptr)
132 frontend.photo_viewer = std::move(set.photo_viewer);
133 if (set.profile_select != nullptr)
134 frontend.profile_select = std::move(set.profile_select);
135 if (set.software_keyboard != nullptr)
136 frontend.software_keyboard = std::move(set.software_keyboard);
137 if (set.web_browser != nullptr)
138 frontend.web_browser = std::move(set.web_browser);
139}
140
141void AppletManager::SetDefaultAppletFrontendSet() {
142 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
143 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
144 frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
145 frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
146 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
147}
148
149void AppletManager::SetDefaultAppletsIfMissing() {
150 if (frontend.error == nullptr) {
151 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
152 }
153
154 if (frontend.photo_viewer == nullptr) {
155 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
156 }
157
158 if (frontend.profile_select == nullptr) {
159 frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
160 }
161
162 if (frontend.software_keyboard == nullptr) {
163 frontend.software_keyboard =
164 std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
165 }
166
167 if (frontend.web_browser == nullptr) {
168 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
169 }
170}
171
172void AppletManager::ClearAll() {
173 frontend = {};
174}
175
176std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
177 switch (id) {
178 case AppletId::Error:
179 return std::make_shared<Error>(*frontend.error);
180 case AppletId::ProfileSelect:
181 return std::make_shared<ProfileSelect>(*frontend.profile_select);
182 case AppletId::SoftwareKeyboard:
183 return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard);
184 case AppletId::PhotoViewer:
185 return std::make_shared<PhotoViewer>(*frontend.photo_viewer);
186 case AppletId::LibAppletOff:
187 return std::make_shared<WebBrowser>(*frontend.web_browser);
188 default:
189 UNIMPLEMENTED_MSG(
190 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
191 static_cast<u8>(id));
192 return std::make_shared<StubApplet>();
193 }
194}
195
114} // namespace Service::AM::Applets 196} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 37424c379..7f932672c 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -12,12 +12,43 @@
12 12
13union ResultCode; 13union ResultCode;
14 14
15namespace Core::Frontend {
16class ErrorApplet;
17class PhotoViewerApplet;
18class ProfileSelectApplet;
19class SoftwareKeyboardApplet;
20class WebBrowserApplet;
21} // namespace Core::Frontend
22
15namespace Service::AM { 23namespace Service::AM {
16 24
17class IStorage; 25class IStorage;
18 26
19namespace Applets { 27namespace Applets {
20 28
29enum class AppletId : u32 {
30 OverlayDisplay = 0x02,
31 QLaunch = 0x03,
32 Starter = 0x04,
33 Auth = 0x0A,
34 Cabinet = 0x0B,
35 Controller = 0x0C,
36 DataErase = 0x0D,
37 Error = 0x0E,
38 NetConnect = 0x0F,
39 ProfileSelect = 0x10,
40 SoftwareKeyboard = 0x11,
41 MiiEdit = 0x12,
42 LibAppletWeb = 0x13,
43 LibAppletShop = 0x14,
44 PhotoViewer = 0x15,
45 Settings = 0x16,
46 LibAppletOff = 0x17,
47 LibAppletWhitelisted = 0x18,
48 LibAppletAuth = 0x19,
49 MyPage = 0x1A,
50};
51
21class AppletDataBroker final { 52class AppletDataBroker final {
22public: 53public:
23 AppletDataBroker(); 54 AppletDataBroker();
@@ -105,5 +136,29 @@ protected:
105 bool initialized = false; 136 bool initialized = false;
106}; 137};
107 138
139struct AppletFrontendSet {
140 std::unique_ptr<Core::Frontend::ErrorApplet> error;
141 std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer;
142 std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select;
143 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
144 std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
145};
146
147class AppletManager {
148public:
149 AppletManager();
150 ~AppletManager();
151
152 void SetAppletFrontendSet(AppletFrontendSet set);
153 void SetDefaultAppletFrontendSet();
154 void SetDefaultAppletsIfMissing();
155 void ClearAll();
156
157 std::shared_ptr<Applet> GetApplet(AppletId id) const;
158
159private:
160 AppletFrontendSet frontend;
161};
162
108} // namespace Applets 163} // namespace Applets
109} // namespace Service::AM 164} // namespace Service::AM
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
new file mode 100644
index 000000000..04774bedc
--- /dev/null
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -0,0 +1,182 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstring>
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/string_util.h"
10#include "core/core.h"
11#include "core/frontend/applets/error.h"
12#include "core/hle/service/am/am.h"
13#include "core/hle/service/am/applets/error.h"
14
15namespace Service::AM::Applets {
16
17#pragma pack(push, 4)
18struct ShowError {
19 u8 mode;
20 bool jump;
21 INSERT_PADDING_BYTES(4);
22 bool use_64bit_error_code;
23 INSERT_PADDING_BYTES(1);
24 u64 error_code_64;
25 u32 error_code_32;
26};
27static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size.");
28#pragma pack(pop)
29
30struct ShowErrorRecord {
31 u8 mode;
32 bool jump;
33 INSERT_PADDING_BYTES(6);
34 u64 error_code_64;
35 u64 posix_time;
36};
37static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size.");
38
39struct SystemErrorArg {
40 u8 mode;
41 bool jump;
42 INSERT_PADDING_BYTES(6);
43 u64 error_code_64;
44 std::array<char, 8> language_code;
45 std::array<char, 0x800> main_text;
46 std::array<char, 0x800> detail_text;
47};
48static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size.");
49
50struct ApplicationErrorArg {
51 u8 mode;
52 bool jump;
53 INSERT_PADDING_BYTES(6);
54 u32 error_code;
55 std::array<char, 8> language_code;
56 std::array<char, 0x800> main_text;
57 std::array<char, 0x800> detail_text;
58};
59static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size.");
60
61union Error::ErrorArguments {
62 ShowError error;
63 ShowErrorRecord error_record;
64 SystemErrorArg system_error;
65 ApplicationErrorArg application_error;
66};
67
68namespace {
69template <typename T>
70void CopyArgumentData(const std::vector<u8>& data, T& variable) {
71 ASSERT(data.size() >= sizeof(T));
72 std::memcpy(&variable, data.data(), sizeof(T));
73}
74
75ResultCode Decode64BitError(u64 error) {
76 const auto description = (error >> 32) & 0x1FFF;
77 auto module = error & 0x3FF;
78 if (module >= 2000)
79 module -= 2000;
80 module &= 0x1FF;
81 return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
82}
83
84} // Anonymous namespace
85
86Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {}
87
88Error::~Error() = default;
89
90void Error::Initialize() {
91 Applet::Initialize();
92 args = std::make_unique<ErrorArguments>();
93 complete = false;
94
95 const auto storage = broker.PopNormalDataToApplet();
96 ASSERT(storage != nullptr);
97 const auto data = storage->GetData();
98
99 ASSERT(!data.empty());
100 std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode));
101
102 switch (mode) {
103 case ErrorAppletMode::ShowError:
104 CopyArgumentData(data, args->error);
105 if (args->error.use_64bit_error_code) {
106 error_code = Decode64BitError(args->error.error_code_64);
107 } else {
108 error_code = ResultCode(args->error.error_code_32);
109 }
110 break;
111 case ErrorAppletMode::ShowSystemError:
112 CopyArgumentData(data, args->system_error);
113 error_code = ResultCode(Decode64BitError(args->system_error.error_code_64));
114 break;
115 case ErrorAppletMode::ShowApplicationError:
116 CopyArgumentData(data, args->application_error);
117 error_code = ResultCode(args->application_error.error_code);
118 break;
119 case ErrorAppletMode::ShowErrorRecord:
120 CopyArgumentData(data, args->error_record);
121 error_code = Decode64BitError(args->error_record.error_code_64);
122 break;
123 default:
124 UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode));
125 }
126}
127
128bool Error::TransactionComplete() const {
129 return complete;
130}
131
132ResultCode Error::GetStatus() const {
133 return RESULT_SUCCESS;
134}
135
136void Error::ExecuteInteractive() {
137 UNREACHABLE_MSG("Unexpected interactive applet data!");
138}
139
140void Error::Execute() {
141 if (complete) {
142 return;
143 }
144
145 const auto callback = [this] { DisplayCompleted(); };
146
147 switch (mode) {
148 case ErrorAppletMode::ShowError:
149 frontend.ShowError(error_code, callback);
150 break;
151 case ErrorAppletMode::ShowSystemError:
152 case ErrorAppletMode::ShowApplicationError: {
153 const auto system = mode == ErrorAppletMode::ShowSystemError;
154 const auto& main_text =
155 system ? args->system_error.main_text : args->application_error.main_text;
156 const auto& detail_text =
157 system ? args->system_error.detail_text : args->application_error.detail_text;
158
159 frontend.ShowCustomErrorText(
160 error_code,
161 Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()),
162 Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()),
163 callback);
164 break;
165 }
166 case ErrorAppletMode::ShowErrorRecord:
167 frontend.ShowErrorWithTimestamp(
168 error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
169 break;
170 default:
171 UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode));
172 DisplayCompleted();
173 }
174}
175
176void Error::DisplayCompleted() {
177 complete = true;
178 broker.PushNormalDataFromApplet(IStorage{{}});
179 broker.SignalStateChanged();
180}
181
182} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h
new file mode 100644
index 000000000..a3590d181
--- /dev/null
+++ b/src/core/hle/service/am/applets/error.h
@@ -0,0 +1,47 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8#include "core/hle/service/am/applets/applets.h"
9
10namespace Service::AM::Applets {
11
12enum class ErrorAppletMode : u8 {
13 ShowError = 0,
14 ShowSystemError = 1,
15 ShowApplicationError = 2,
16 ShowEula = 3,
17 ShowErrorPctl = 4,
18 ShowErrorRecord = 5,
19 ShowUpdateEula = 8,
20};
21
22class Error final : public Applet {
23public:
24 explicit Error(const Core::Frontend::ErrorApplet& frontend);
25 ~Error() override;
26
27 void Initialize() override;
28
29 bool TransactionComplete() const override;
30 ResultCode GetStatus() const override;
31 void ExecuteInteractive() override;
32 void Execute() override;
33
34 void DisplayCompleted();
35
36private:
37 union ErrorArguments;
38
39 const Core::Frontend::ErrorApplet& frontend;
40 ResultCode error_code = RESULT_SUCCESS;
41 ErrorAppletMode mode = ErrorAppletMode::ShowError;
42 std::unique_ptr<ErrorArguments> args;
43
44 bool complete = false;
45};
46
47} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index ed166b87d..c591b9ac2 100644
--- a/src/core/hle/service/am/applets/stub_applet.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -4,11 +4,15 @@
4 4
5#include <string> 5#include <string>
6 6
7#include "common/assert.h"
7#include "common/hex_util.h" 8#include "common/hex_util.h"
8#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
11#include "core/frontend/applets/general_frontend.h"
12#include "core/hle/kernel/process.h"
9#include "core/hle/result.h" 13#include "core/hle/result.h"
10#include "core/hle/service/am/am.h" 14#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/stub_applet.h" 15#include "core/hle/service/am/applets/general_backend.h"
12 16
13namespace Service::AM::Applets { 17namespace Service::AM::Applets {
14 18
@@ -30,6 +34,55 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
30 } 34 }
31} 35}
32 36
37PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
38
39PhotoViewer::~PhotoViewer() = default;
40
41void PhotoViewer::Initialize() {
42 Applet::Initialize();
43 complete = false;
44
45 const auto storage = broker.PopNormalDataToApplet();
46 ASSERT(storage != nullptr);
47 const auto data = storage->GetData();
48 ASSERT(!data.empty());
49 mode = static_cast<PhotoViewerAppletMode>(data[0]);
50}
51
52bool PhotoViewer::TransactionComplete() const {
53 return complete;
54}
55
56ResultCode PhotoViewer::GetStatus() const {
57 return RESULT_SUCCESS;
58}
59
60void PhotoViewer::ExecuteInteractive() {
61 UNREACHABLE_MSG("Unexpected interactive applet data.");
62}
63
64void PhotoViewer::Execute() {
65 if (complete)
66 return;
67
68 const auto callback = [this] { ViewFinished(); };
69 switch (mode) {
70 case PhotoViewerAppletMode::CurrentApp:
71 frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback);
72 break;
73 case PhotoViewerAppletMode::AllApps:
74 frontend.ShowAllPhotos(callback);
75 break;
76 default:
77 UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", static_cast<u8>(mode));
78 }
79}
80
81void PhotoViewer::ViewFinished() {
82 broker.PushNormalDataFromApplet(IStorage{{}});
83 broker.SignalStateChanged();
84}
85
33StubApplet::StubApplet() = default; 86StubApplet::StubApplet() = default;
34 87
35StubApplet::~StubApplet() = default; 88StubApplet::~StubApplet() = default;
@@ -67,4 +120,5 @@ void StubApplet::Execute() {
67 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); 120 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
68 broker.SignalStateChanged(); 121 broker.SignalStateChanged();
69} 122}
123
70} // namespace Service::AM::Applets 124} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h
new file mode 100644
index 000000000..2dd255d7c
--- /dev/null
+++ b/src/core/hle/service/am/applets/general_backend.h
@@ -0,0 +1,48 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/am/applets/applets.h"
8
9namespace Service::AM::Applets {
10
11enum class PhotoViewerAppletMode : u8 {
12 CurrentApp = 0,
13 AllApps = 1,
14};
15
16class PhotoViewer final : public Applet {
17public:
18 explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend);
19 ~PhotoViewer() override;
20
21 void Initialize() override;
22 bool TransactionComplete() const override;
23 ResultCode GetStatus() const override;
24 void ExecuteInteractive() override;
25 void Execute() override;
26
27 void ViewFinished();
28
29private:
30 const Core::Frontend::PhotoViewerApplet& frontend;
31 bool complete = false;
32 PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
33};
34
35class StubApplet final : public Applet {
36public:
37 StubApplet();
38 ~StubApplet() override;
39
40 void Initialize() override;
41
42 bool TransactionComplete() const override;
43 ResultCode GetStatus() const override;
44 void ExecuteInteractive() override;
45 void Execute() override;
46};
47
48} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp
index 14e2a1fee..d113bd2eb 100644
--- a/src/core/hle/service/am/applets/profile_select.cpp
+++ b/src/core/hle/service/am/applets/profile_select.cpp
@@ -15,7 +15,9 @@ namespace Service::AM::Applets {
15 15
16constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; 16constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
17 17
18ProfileSelect::ProfileSelect() = default; 18ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend)
19 : frontend(frontend) {}
20
19ProfileSelect::~ProfileSelect() = default; 21ProfileSelect::~ProfileSelect() = default;
20 22
21void ProfileSelect::Initialize() { 23void ProfileSelect::Initialize() {
@@ -51,8 +53,6 @@ void ProfileSelect::Execute() {
51 return; 53 return;
52 } 54 }
53 55
54 const auto& frontend{Core::System::GetInstance().GetProfileSelector()};
55
56 frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); }); 56 frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
57} 57}
58 58
diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h
index 787485f22..a2ac6cf50 100644
--- a/src/core/hle/service/am/applets/profile_select.h
+++ b/src/core/hle/service/am/applets/profile_select.h
@@ -28,7 +28,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco
28 28
29class ProfileSelect final : public Applet { 29class ProfileSelect final : public Applet {
30public: 30public:
31 ProfileSelect(); 31 explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend);
32 ~ProfileSelect() override; 32 ~ProfileSelect() override;
33 33
34 void Initialize() override; 34 void Initialize() override;
@@ -41,6 +41,8 @@ public:
41 void SelectionComplete(std::optional<Account::UUID> uuid); 41 void SelectionComplete(std::optional<Account::UUID> uuid);
42 42
43private: 43private:
44 const Core::Frontend::ProfileSelectApplet& frontend;
45
44 UserSelectionConfig config; 46 UserSelectionConfig config;
45 bool complete = false; 47 bool complete = false;
46 ResultCode status = RESULT_SUCCESS; 48 ResultCode status = RESULT_SUCCESS;
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 8c5bd6059..e197990f7 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -39,7 +39,8 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
39 return params; 39 return params;
40} 40}
41 41
42SoftwareKeyboard::SoftwareKeyboard() = default; 42SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend)
43 : frontend(frontend) {}
43 44
44SoftwareKeyboard::~SoftwareKeyboard() = default; 45SoftwareKeyboard::~SoftwareKeyboard() = default;
45 46
@@ -90,8 +91,6 @@ void SoftwareKeyboard::ExecuteInteractive() {
90 if (status == INTERACTIVE_STATUS_OK) { 91 if (status == INTERACTIVE_STATUS_OK) {
91 complete = true; 92 complete = true;
92 } else { 93 } else {
93 const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
94
95 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; 94 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
96 std::memcpy(string.data(), data.data() + 4, string.size() * 2); 95 std::memcpy(string.data(), data.data() + 4, string.size() * 2);
97 frontend.SendTextCheckDialog( 96 frontend.SendTextCheckDialog(
@@ -106,8 +105,6 @@ void SoftwareKeyboard::Execute() {
106 return; 105 return;
107 } 106 }
108 107
109 const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
110
111 const auto parameters = ConvertToFrontendParameters(config, initial_text); 108 const auto parameters = ConvertToFrontendParameters(config, initial_text);
112 109
113 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, 110 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index b93a30d28..0fbc43e51 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -55,7 +55,7 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz
55 55
56class SoftwareKeyboard final : public Applet { 56class SoftwareKeyboard final : public Applet {
57public: 57public:
58 SoftwareKeyboard(); 58 explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend);
59 ~SoftwareKeyboard() override; 59 ~SoftwareKeyboard() override;
60 60
61 void Initialize() override; 61 void Initialize() override;
@@ -68,6 +68,8 @@ public:
68 void WriteText(std::optional<std::u16string> text); 68 void WriteText(std::optional<std::u16string> text);
69 69
70private: 70private:
71 const Core::Frontend::SoftwareKeyboardApplet& frontend;
72
71 KeyboardConfig config; 73 KeyboardConfig config;
72 std::u16string initial_text; 74 std::u16string initial_text;
73 bool complete = false; 75 bool complete = false;
diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h
deleted file mode 100644
index 7d8dc968d..000000000
--- a/src/core/hle/service/am/applets/stub_applet.h
+++ /dev/null
@@ -1,24 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/am/applets/applets.h"
8
9namespace Service::AM::Applets {
10
11class StubApplet final : public Applet {
12public:
13 StubApplet();
14 ~StubApplet() override;
15
16 void Initialize() override;
17
18 bool TransactionComplete() const override;
19 ResultCode GetStatus() const override;
20 void ExecuteInteractive() override;
21 void Execute() override;
22};
23
24} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 7e17df98a..7878f5136 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -95,7 +95,7 @@ static FileSys::VirtualFile GetManualRomFS() {
95 return nullptr; 95 return nullptr;
96} 96}
97 97
98WebBrowser::WebBrowser() = default; 98WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {}
99 99
100WebBrowser::~WebBrowser() = default; 100WebBrowser::~WebBrowser() = default;
101 101
@@ -152,8 +152,6 @@ void WebBrowser::Execute() {
152 return; 152 return;
153 } 153 }
154 154
155 auto& frontend{Core::System::GetInstance().GetWebBrowser()};
156
157 frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 155 frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
158} 156}
159 157
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h
index b9e228fac..7e0f34c7d 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -12,7 +12,7 @@ namespace Service::AM::Applets {
12 12
13class WebBrowser final : public Applet { 13class WebBrowser final : public Applet {
14public: 14public:
15 WebBrowser(); 15 WebBrowser(Core::Frontend::WebBrowserApplet& frontend);
16 ~WebBrowser() override; 16 ~WebBrowser() override;
17 17
18 void Initialize() override; 18 void Initialize() override;
@@ -32,6 +32,8 @@ public:
32 void Finalize(); 32 void Finalize();
33 33
34private: 34private:
35 Core::Frontend::WebBrowserApplet& frontend;
36
35 bool complete = false; 37 bool complete = false;
36 bool unpacked = false; 38 bool unpacked = false;
37 ResultCode status = RESULT_SUCCESS; 39 ResultCode status = RESULT_SUCCESS;
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 2d768d9fc..51d8c26b4 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -50,6 +50,7 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
50} 50}
51 51
52AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { 52AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) {
53 // clang-format off
53 static const FunctionInfo functions[] = { 54 static const FunctionInfo functions[] = {
54 {0, nullptr, "CountAddOnContentByApplicationId"}, 55 {0, nullptr, "CountAddOnContentByApplicationId"},
55 {1, nullptr, "ListAddOnContentByApplicationId"}, 56 {1, nullptr, "ListAddOnContentByApplicationId"},
@@ -60,7 +61,10 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
60 {6, nullptr, "PrepareAddOnContentByApplicationId"}, 61 {6, nullptr, "PrepareAddOnContentByApplicationId"},
61 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, 62 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
62 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, 63 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
64 {100, nullptr, "CreateEcPurchasedEventManager"},
63 }; 65 };
66 // clang-format on
67
64 RegisterHandlers(functions); 68 RegisterHandlers(functions);
65 69
66 auto& kernel = Core::System::GetInstance().Kernel(); 70 auto& kernel = Core::System::GetInstance().Kernel();
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index fcacbab72..d058c0245 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -87,6 +87,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
87 {3, nullptr, "GetLastThrottlingState"}, 87 {3, nullptr, "GetLastThrottlingState"},
88 {4, nullptr, "ClearLastThrottlingState"}, 88 {4, nullptr, "ClearLastThrottlingState"},
89 {5, nullptr, "LoadAndApplySettings"}, 89 {5, nullptr, "LoadAndApplySettings"},
90 {6, nullptr, "SetCpuBoostMode"},
91 {7, nullptr, "GetCurrentPerformanceConfiguration"},
90 }; 92 };
91 // clang-format on 93 // clang-format on
92 94
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
index b6b71f966..f43e512e9 100644
--- a/src/core/hle/service/audio/audctl.cpp
+++ b/src/core/hle/service/audio/audctl.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
5#include "core/hle/service/audio/audctl.h" 7#include "core/hle/service/audio/audctl.h"
6 8
7namespace Service::Audio { 9namespace Service::Audio {
@@ -11,8 +13,8 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} {
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetTargetVolume"}, 14 {0, nullptr, "GetTargetVolume"},
13 {1, nullptr, "SetTargetVolume"}, 15 {1, nullptr, "SetTargetVolume"},
14 {2, nullptr, "GetTargetVolumeMin"}, 16 {2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"},
15 {3, nullptr, "GetTargetVolumeMax"}, 17 {3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"},
16 {4, nullptr, "IsTargetMute"}, 18 {4, nullptr, "IsTargetMute"},
17 {5, nullptr, "SetTargetMute"}, 19 {5, nullptr, "SetTargetMute"},
18 {6, nullptr, "IsTargetConnected"}, 20 {6, nullptr, "IsTargetConnected"},
@@ -44,4 +46,28 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} {
44 46
45AudCtl::~AudCtl() = default; 47AudCtl::~AudCtl() = default;
46 48
49void AudCtl::GetTargetVolumeMin(Kernel::HLERequestContext& ctx) {
50 LOG_DEBUG(Audio, "called.");
51
52 // This service function is currently hardcoded on the
53 // actual console to this value (as of 6.0.0).
54 constexpr s32 target_min_volume = 0;
55
56 IPC::ResponseBuilder rb{ctx, 3};
57 rb.Push(RESULT_SUCCESS);
58 rb.Push(target_min_volume);
59}
60
61void AudCtl::GetTargetVolumeMax(Kernel::HLERequestContext& ctx) {
62 LOG_DEBUG(Audio, "called.");
63
64 // This service function is currently hardcoded on the
65 // actual console to this value (as of 6.0.0).
66 constexpr s32 target_max_volume = 15;
67
68 IPC::ResponseBuilder rb{ctx, 3};
69 rb.Push(RESULT_SUCCESS);
70 rb.Push(target_max_volume);
71}
72
47} // namespace Service::Audio 73} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h
index 9d2d9e83b..c7fafc02e 100644
--- a/src/core/hle/service/audio/audctl.h
+++ b/src/core/hle/service/audio/audctl.h
@@ -12,6 +12,10 @@ class AudCtl final : public ServiceFramework<AudCtl> {
12public: 12public:
13 explicit AudCtl(); 13 explicit AudCtl();
14 ~AudCtl() override; 14 ~AudCtl() override;
15
16private:
17 void GetTargetVolumeMin(Kernel::HLERequestContext& ctx);
18 void GetTargetVolumeMax(Kernel::HLERequestContext& ctx);
15}; 19};
16 20
17} // namespace Service::Audio 21} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index e5daefdde..d7f1d348d 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -25,6 +25,7 @@ public:
25 {11, nullptr, "GetAudioInBufferCount"}, 25 {11, nullptr, "GetAudioInBufferCount"},
26 {12, nullptr, "SetAudioInDeviceGain"}, 26 {12, nullptr, "SetAudioInDeviceGain"},
27 {13, nullptr, "GetAudioInDeviceGain"}, 27 {13, nullptr, "GetAudioInDeviceGain"},
28 {14, nullptr, "FlushAudioInBuffers"},
28 }; 29 };
29 // clang-format on 30 // clang-format on
30 31
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 39acb7b23..12875fb42 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -44,7 +44,7 @@ public:
44 std::string&& unique_name) 44 std::string&& unique_name)
45 : ServiceFramework("IAudioOut"), audio_core(audio_core), 45 : ServiceFramework("IAudioOut"), audio_core(audio_core),
46 device_name(std::move(device_name)), audio_params(audio_params) { 46 device_name(std::move(device_name)), audio_params(audio_params) {
47 47 // clang-format off
48 static const FunctionInfo functions[] = { 48 static const FunctionInfo functions[] = {
49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
50 {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, 50 {1, &IAudioOut::StartAudioOut, "StartAudioOut"},
@@ -58,7 +58,10 @@ public:
58 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, 58 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
59 {10, nullptr, "GetAudioOutPlayedSampleCount"}, 59 {10, nullptr, "GetAudioOutPlayedSampleCount"},
60 {11, nullptr, "FlushAudioOutBuffers"}, 60 {11, nullptr, "FlushAudioOutBuffers"},
61 {12, nullptr, "SetAudioOutVolume"},
62 {13, nullptr, "GetAudioOutVolume"},
61 }; 63 };
64 // clang-format on
62 RegisterHandlers(functions); 65 RegisterHandlers(functions);
63 66
64 // This is the event handle used to check if the audio buffer was released 67 // This is the event handle used to check if the audio buffer was released
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 59ef603e1..974ff8e1a 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -154,7 +154,8 @@ public:
154 {96, nullptr, "GetLeHidEventInfo"}, 154 {96, nullptr, "GetLeHidEventInfo"},
155 {97, nullptr, "RegisterBleHidEvent"}, 155 {97, nullptr, "RegisterBleHidEvent"},
156 {98, nullptr, "SetLeScanParameter"}, 156 {98, nullptr, "SetLeScanParameter"},
157 {256, nullptr, "GetIsManufacturingMode"} 157 {256, nullptr, "GetIsManufacturingMode"},
158 {257, nullptr, "EmulateBluetoothCrash"},
158 }; 159 };
159 // clang-format on 160 // clang-format on
160 161
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
index ae7b0720b..907f464ab 100644
--- a/src/core/hle/service/caps/caps.cpp
+++ b/src/core/hle/service/caps/caps.cpp
@@ -15,32 +15,41 @@ public:
15 explicit CAPS_A() : ServiceFramework{"caps:a"} { 15 explicit CAPS_A() : ServiceFramework{"caps:a"} {
16 // clang-format off 16 // clang-format off
17 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
18 {0, nullptr, "Unknown1"}, 18 {0, nullptr, "GetAlbumFileCount"},
19 {1, nullptr, "Unknown2"}, 19 {1, nullptr, "GetAlbumFileList"},
20 {2, nullptr, "Unknown3"}, 20 {2, nullptr, "LoadAlbumFile"},
21 {3, nullptr, "Unknown4"}, 21 {3, nullptr, "DeleteAlbumFile"},
22 {4, nullptr, "Unknown5"}, 22 {4, nullptr, "StorageCopyAlbumFile"},
23 {5, nullptr, "Unknown6"}, 23 {5, nullptr, "IsAlbumMounted"},
24 {6, nullptr, "Unknown7"}, 24 {6, nullptr, "GetAlbumUsage"},
25 {7, nullptr, "Unknown8"}, 25 {7, nullptr, "GetAlbumFileSize"},
26 {8, nullptr, "Unknown9"}, 26 {8, nullptr, "LoadAlbumFileThumbnail"},
27 {9, nullptr, "Unknown10"}, 27 {9, nullptr, "LoadAlbumScreenShotImage"},
28 {10, nullptr, "Unknown11"}, 28 {10, nullptr, "LoadAlbumScreenShotThumbnailImage"},
29 {11, nullptr, "Unknown12"}, 29 {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"},
30 {12, nullptr, "Unknown13"}, 30 {12, nullptr, "Unknown12"},
31 {13, nullptr, "Unknown14"}, 31 {13, nullptr, "Unknown13"},
32 {14, nullptr, "Unknown15"}, 32 {14, nullptr, "Unknown14"},
33 {301, nullptr, "Unknown16"}, 33 {15, nullptr, "Unknown15"},
34 {401, nullptr, "Unknown17"}, 34 {16, nullptr, "Unknown16"},
35 {501, nullptr, "Unknown18"}, 35 {17, nullptr, "Unknown17"},
36 {1001, nullptr, "Unknown19"}, 36 {18, nullptr, "Unknown18"},
37 {1002, nullptr, "Unknown20"}, 37 {202, nullptr, "SaveEditedScreenShot"},
38 {8001, nullptr, "Unknown21"}, 38 {301, nullptr, "GetLastThumbnail"},
39 {8002, nullptr, "Unknown22"}, 39 {401, nullptr, "GetAutoSavingStorage"},
40 {8011, nullptr, "Unknown23"}, 40 {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
41 {8012, nullptr, "Unknown24"}, 41 {1001, nullptr, "Unknown1001"},
42 {8021, nullptr, "Unknown25"}, 42 {1002, nullptr, "Unknown1002"},
43 {10011, nullptr, "Unknown26"}, 43 {1003, nullptr, "Unknown1003"},
44 {8001, nullptr, "ForceAlbumUnmounted"},
45 {8002, nullptr, "ResetAlbumMountStatus"},
46 {8011, nullptr, "RefreshAlbumCache"},
47 {8012, nullptr, "GetAlbumCache"},
48 {8013, nullptr, "Unknown8013"},
49 {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
50 {10011, nullptr, "SetInternalErrorConversionEnabled"},
51 {50000, nullptr, "Unknown50000"},
52 {60002, nullptr, "Unknown60002"},
44 }; 53 };
45 // clang-format on 54 // clang-format on
46 55
@@ -53,16 +62,17 @@ public:
53 explicit CAPS_C() : ServiceFramework{"caps:c"} { 62 explicit CAPS_C() : ServiceFramework{"caps:c"} {
54 // clang-format off 63 // clang-format off
55 static const FunctionInfo functions[] = { 64 static const FunctionInfo functions[] = {
56 {2001, nullptr, "Unknown1"}, 65 {33, nullptr, "Unknown33"},
57 {2002, nullptr, "Unknown2"}, 66 {2001, nullptr, "Unknown2001"},
58 {2011, nullptr, "Unknown3"}, 67 {2002, nullptr, "Unknown2002"},
59 {2012, nullptr, "Unknown4"}, 68 {2011, nullptr, "Unknown2011"},
60 {2013, nullptr, "Unknown5"}, 69 {2012, nullptr, "Unknown2012"},
61 {2014, nullptr, "Unknown6"}, 70 {2013, nullptr, "Unknown2013"},
62 {2101, nullptr, "Unknown7"}, 71 {2014, nullptr, "Unknown2014"},
63 {2102, nullptr, "Unknown8"}, 72 {2101, nullptr, "Unknown2101"},
64 {2201, nullptr, "Unknown9"}, 73 {2102, nullptr, "Unknown2102"},
65 {2301, nullptr, "Unknown10"}, 74 {2201, nullptr, "Unknown2201"},
75 {2301, nullptr, "Unknown2301"},
66 }; 76 };
67 // clang-format on 77 // clang-format on
68 78
@@ -127,11 +137,18 @@ public:
127 explicit CAPS_U() : ServiceFramework{"caps:u"} { 137 explicit CAPS_U() : ServiceFramework{"caps:u"} {
128 // clang-format off 138 // clang-format off
129 static const FunctionInfo functions[] = { 139 static const FunctionInfo functions[] = {
140 {32, nullptr, "SetShimLibraryVersion"},
130 {102, nullptr, "GetAlbumFileListByAruid"}, 141 {102, nullptr, "GetAlbumFileListByAruid"},
131 {103, nullptr, "DeleteAlbumFileByAruid"}, 142 {103, nullptr, "DeleteAlbumFileByAruid"},
132 {104, nullptr, "GetAlbumFileSizeByAruid"}, 143 {104, nullptr, "GetAlbumFileSizeByAruid"},
144 {105, nullptr, "DeleteAlbumFileByAruidForDebug"},
133 {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, 145 {110, nullptr, "LoadAlbumScreenShotImageByAruid"},
134 {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, 146 {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
147 {130, nullptr, "PrecheckToCreateContentsByAruid"},
148 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
149 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
150 {142, nullptr, "GetAlbumFileList3AaeAruid"},
151 {143, nullptr, "GetAlbumFileList4AaeUidAruid"},
135 {60002, nullptr, "OpenAccessorSessionForApplication"}, 152 {60002, nullptr, "OpenAccessorSessionForApplication"},
136 }; 153 };
137 // clang-format on 154 // clang-format on
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 0249b6992..e7df8fd98 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -664,10 +664,13 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
664 {100, nullptr, "OpenImageDirectoryFileSystem"}, 664 {100, nullptr, "OpenImageDirectoryFileSystem"},
665 {110, nullptr, "OpenContentStorageFileSystem"}, 665 {110, nullptr, "OpenContentStorageFileSystem"},
666 {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, 666 {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
667 {130, nullptr, "OpenCustomStorageFileSystem"},
667 {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, 668 {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
668 {201, nullptr, "OpenDataStorageByProgramId"}, 669 {201, nullptr, "OpenDataStorageByProgramId"},
669 {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"}, 670 {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
670 {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"}, 671 {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
672 {204, nullptr, "OpenDataFileSystemByProgramIndex"},
673 {205, nullptr, "OpenDataStorageByProgramIndex"},
671 {400, nullptr, "OpenDeviceOperator"}, 674 {400, nullptr, "OpenDeviceOperator"},
672 {500, nullptr, "OpenSdCardDetectionEventNotifier"}, 675 {500, nullptr, "OpenSdCardDetectionEventNotifier"},
673 {501, nullptr, "OpenGameCardDetectionEventNotifier"}, 676 {501, nullptr, "OpenGameCardDetectionEventNotifier"},
@@ -691,6 +694,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
691 {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, 694 {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"},
692 {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, 695 {615, nullptr, "QuerySaveDataInternalStorageTotalSize"},
693 {616, nullptr, "GetSaveDataCommitId"}, 696 {616, nullptr, "GetSaveDataCommitId"},
697 {617, nullptr, "UnregisterExternalKey"},
694 {620, nullptr, "SetSdCardEncryptionSeed"}, 698 {620, nullptr, "SetSdCardEncryptionSeed"},
695 {630, nullptr, "SetSdCardAccessibility"}, 699 {630, nullptr, "SetSdCardAccessibility"},
696 {631, nullptr, "IsSdCardAccessible"}, 700 {631, nullptr, "IsSdCardAccessible"},
@@ -701,6 +705,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
701 {710, nullptr, "ResolveAccessFailure"}, 705 {710, nullptr, "ResolveAccessFailure"},
702 {720, nullptr, "AbandonAccessFailure"}, 706 {720, nullptr, "AbandonAccessFailure"},
703 {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, 707 {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
708 {810, nullptr, "RegisterProgramIndexMapInfo"},
704 {1000, nullptr, "SetBisRootForHost"}, 709 {1000, nullptr, "SetBisRootForHost"},
705 {1001, nullptr, "SetSaveDataSize"}, 710 {1001, nullptr, "SetSaveDataSize"},
706 {1002, nullptr, "SetSaveDataRootPath"}, 711 {1002, nullptr, "SetSaveDataRootPath"},
@@ -711,6 +716,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
711 {1007, nullptr, "RegisterUpdatePartition"}, 716 {1007, nullptr, "RegisterUpdatePartition"},
712 {1008, nullptr, "OpenRegisteredUpdatePartition"}, 717 {1008, nullptr, "OpenRegisteredUpdatePartition"},
713 {1009, nullptr, "GetAndClearMemoryReportInfo"}, 718 {1009, nullptr, "GetAndClearMemoryReportInfo"},
719 {1010, nullptr, "SetDataStorageRedirectTarget"},
720 {1011, nullptr, "OutputAccessLogToSdCard2"},
714 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 721 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
715 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, 722 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
716 {1200, nullptr, "OpenMultiCommitManager"}, 723 {1200, nullptr, "OpenMultiCommitManager"},
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index d9225d624..5100e376c 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -12,6 +12,7 @@ namespace Service::Friend {
12class IFriendService final : public ServiceFramework<IFriendService> { 12class IFriendService final : public ServiceFramework<IFriendService> {
13public: 13public:
14 IFriendService() : ServiceFramework("IFriendService") { 14 IFriendService() : ServiceFramework("IFriendService") {
15 // clang-format off
15 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
16 {0, nullptr, "GetCompletionEvent"}, 17 {0, nullptr, "GetCompletionEvent"},
17 {1, nullptr, "Cancel"}, 18 {1, nullptr, "Cancel"},
@@ -24,8 +25,7 @@ public:
24 {10400, nullptr, "GetBlockedUserListIds"}, 25 {10400, nullptr, "GetBlockedUserListIds"},
25 {10500, nullptr, "GetProfileList"}, 26 {10500, nullptr, "GetProfileList"},
26 {10600, nullptr, "DeclareOpenOnlinePlaySession"}, 27 {10600, nullptr, "DeclareOpenOnlinePlaySession"},
27 {10601, &IFriendService::DeclareCloseOnlinePlaySession, 28 {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
28 "DeclareCloseOnlinePlaySession"},
29 {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, 29 {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"},
30 {10700, nullptr, "GetPlayHistoryRegistrationKey"}, 30 {10700, nullptr, "GetPlayHistoryRegistrationKey"},
31 {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, 31 {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"},
@@ -88,6 +88,7 @@ public:
88 {30830, nullptr, "ClearPlayLog"}, 88 {30830, nullptr, "ClearPlayLog"},
89 {49900, nullptr, "DeleteNetworkServiceAccountCache"}, 89 {49900, nullptr, "DeleteNetworkServiceAccountCache"},
90 }; 90 };
91 // clang-format on
91 92
92 RegisterHandlers(functions); 93 RegisterHandlers(functions);
93 } 94 }
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 63b55758b..a4ad95d96 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -210,6 +210,7 @@ Hid::Hid() : ServiceFramework("hid") {
210 {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, 210 {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
211 {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, 211 {132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
212 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, 212 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
213 {134, nullptr, "SetNpadAnalogStickUseCenterClamp"},
213 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, 214 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
214 {201, &Hid::SendVibrationValue, "SendVibrationValue"}, 215 {201, &Hid::SendVibrationValue, "SendVibrationValue"},
215 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, 216 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -221,6 +222,7 @@ Hid::Hid() : ServiceFramework("hid") {
221 {208, nullptr, "GetActualVibrationGcErmCommand"}, 222 {208, nullptr, "GetActualVibrationGcErmCommand"},
222 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, 223 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
223 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, 224 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
225 {211, nullptr, "IsVibrationDeviceMounted"},
224 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 226 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
225 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 227 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
226 {302, nullptr, "StopConsoleSixAxisSensor"}, 228 {302, nullptr, "StopConsoleSixAxisSensor"},
@@ -265,6 +267,7 @@ Hid::Hid() : ServiceFramework("hid") {
265 {523, nullptr, "SetIsPalmaPairedConnectable"}, 267 {523, nullptr, "SetIsPalmaPairedConnectable"},
266 {524, nullptr, "PairPalma"}, 268 {524, nullptr, "PairPalma"},
267 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, 269 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
270 {526, nullptr, "CancelWritePalmaWaveEntry"},
268 {1000, nullptr, "SetNpadCommunicationMode"}, 271 {1000, nullptr, "SetNpadCommunicationMode"},
269 {1001, nullptr, "GetNpadCommunicationMode"}, 272 {1001, nullptr, "GetNpadCommunicationMode"},
270 }; 273 };
@@ -797,12 +800,22 @@ public:
797 {232, nullptr, "EnableShipmentMode"}, 800 {232, nullptr, "EnableShipmentMode"},
798 {233, nullptr, "ClearPairingInfo"}, 801 {233, nullptr, "ClearPairingInfo"},
799 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, 802 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
803 {235, nullptr, "EnableAnalogStickPower"},
800 {301, nullptr, "GetAbstractedPadHandles"}, 804 {301, nullptr, "GetAbstractedPadHandles"},
801 {302, nullptr, "GetAbstractedPadState"}, 805 {302, nullptr, "GetAbstractedPadState"},
802 {303, nullptr, "GetAbstractedPadsState"}, 806 {303, nullptr, "GetAbstractedPadsState"},
803 {321, nullptr, "SetAutoPilotVirtualPadState"}, 807 {321, nullptr, "SetAutoPilotVirtualPadState"},
804 {322, nullptr, "UnsetAutoPilotVirtualPadState"}, 808 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
805 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, 809 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
810 {324, nullptr, "AttachHdlsWorkBuffer"},
811 {325, nullptr, "ReleaseHdlsWorkBuffer"},
812 {326, nullptr, "DumpHdlsNpadAssignmentState"},
813 {327, nullptr, "DumpHdlsStates"},
814 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
815 {329, nullptr, "ApplyHdlsStateList"},
816 {330, nullptr, "AttachHdlsVirtualDevice"},
817 {331, nullptr, "DetachHdlsVirtualDevice"},
818 {332, nullptr, "SetHdlsState"},
806 {350, nullptr, "AddRegisteredDevice"}, 819 {350, nullptr, "AddRegisteredDevice"},
807 {400, nullptr, "DisableExternalMcuOnNxDevice"}, 820 {400, nullptr, "DisableExternalMcuOnNxDevice"},
808 {401, nullptr, "DisableRailDeviceFiltering"}, 821 {401, nullptr, "DisableRailDeviceFiltering"},
@@ -825,6 +838,7 @@ public:
825 {131, nullptr, "ActivateSleepButton"}, 838 {131, nullptr, "ActivateSleepButton"},
826 {141, nullptr, "AcquireCaptureButtonEventHandle"}, 839 {141, nullptr, "AcquireCaptureButtonEventHandle"},
827 {151, nullptr, "ActivateCaptureButton"}, 840 {151, nullptr, "ActivateCaptureButton"},
841 {161, nullptr, "GetPlatformConfig"},
828 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, 842 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
829 {211, nullptr, "GetNpadsWithNfc"}, 843 {211, nullptr, "GetNpadsWithNfc"},
830 {212, nullptr, "AcquireNfcActivateEventHandle"}, 844 {212, nullptr, "AcquireNfcActivateEventHandle"},
@@ -894,6 +908,7 @@ public:
894 {827, nullptr, "IsAnalogStickButtonPressed"}, 908 {827, nullptr, "IsAnalogStickButtonPressed"},
895 {828, nullptr, "IsAnalogStickInReleasePosition"}, 909 {828, nullptr, "IsAnalogStickInReleasePosition"},
896 {829, nullptr, "IsAnalogStickInCircumference"}, 910 {829, nullptr, "IsAnalogStickInCircumference"},
911 {830, nullptr, "SetNotificationLedPattern"},
897 {850, nullptr, "IsUsbFullKeyControllerEnabled"}, 912 {850, nullptr, "IsUsbFullKeyControllerEnabled"},
898 {851, nullptr, "EnableUsbFullKeyController"}, 913 {851, nullptr, "EnableUsbFullKeyController"},
899 {852, nullptr, "IsUsbConnected"}, 914 {852, nullptr, "IsUsbConnected"},
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index e250595e3..ed5059047 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -52,9 +52,11 @@ public:
52 } 52 }
53}; 53};
54 54
55class ILocalCommunicationService final : public ServiceFramework<ILocalCommunicationService> { 55class ISystemLocalCommunicationService final
56 : public ServiceFramework<ISystemLocalCommunicationService> {
56public: 57public:
57 explicit ILocalCommunicationService(const char* name) : ServiceFramework{name} { 58 explicit ISystemLocalCommunicationService()
59 : ServiceFramework{"ISystemLocalCommunicationService"} {
58 // clang-format off 60 // clang-format off
59 static const FunctionInfo functions[] = { 61 static const FunctionInfo functions[] = {
60 {0, nullptr, "GetState"}, 62 {0, nullptr, "GetState"},
@@ -84,6 +86,50 @@ public:
84 {304, nullptr, "Disconnect"}, 86 {304, nullptr, "Disconnect"},
85 {400, nullptr, "InitializeSystem"}, 87 {400, nullptr, "InitializeSystem"},
86 {401, nullptr, "FinalizeSystem"}, 88 {401, nullptr, "FinalizeSystem"},
89 {402, nullptr, "SetOperationMode"},
90 {403, nullptr, "InitializeSystem2"},
91 };
92 // clang-format on
93
94 RegisterHandlers(functions);
95 }
96};
97
98class IUserLocalCommunicationService final
99 : public ServiceFramework<IUserLocalCommunicationService> {
100public:
101 explicit IUserLocalCommunicationService() : ServiceFramework{"IUserLocalCommunicationService"} {
102 // clang-format off
103 static const FunctionInfo functions[] = {
104 {0, nullptr, "GetState"},
105 {1, nullptr, "GetNetworkInfo"},
106 {2, nullptr, "GetIpv4Address"},
107 {3, nullptr, "GetDisconnectReason"},
108 {4, nullptr, "GetSecurityParameter"},
109 {5, nullptr, "GetNetworkConfig"},
110 {100, nullptr, "AttachStateChangeEvent"},
111 {101, nullptr, "GetNetworkInfoLatestUpdate"},
112 {102, nullptr, "Scan"},
113 {103, nullptr, "ScanPrivate"},
114 {104, nullptr, "SetWirelessControllerRestriction"},
115 {200, nullptr, "OpenAccessPoint"},
116 {201, nullptr, "CloseAccessPoint"},
117 {202, nullptr, "CreateNetwork"},
118 {203, nullptr, "CreateNetworkPrivate"},
119 {204, nullptr, "DestroyNetwork"},
120 {205, nullptr, "Reject"},
121 {206, nullptr, "SetAdvertiseData"},
122 {207, nullptr, "SetStationAcceptPolicy"},
123 {208, nullptr, "AddAcceptFilterEntry"},
124 {209, nullptr, "ClearAcceptFilter"},
125 {300, nullptr, "OpenStation"},
126 {301, nullptr, "CloseStation"},
127 {302, nullptr, "Connect"},
128 {303, nullptr, "ConnectPrivate"},
129 {304, nullptr, "Disconnect"},
130 {400, nullptr, "Initialize"},
131 {401, nullptr, "Finalize"},
132 {402, nullptr, "SetOperationMode"},
87 }; 133 };
88 // clang-format on 134 // clang-format on
89 135
@@ -108,7 +154,7 @@ public:
108 154
109 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
110 rb.Push(RESULT_SUCCESS); 156 rb.Push(RESULT_SUCCESS);
111 rb.PushIpcInterface<ILocalCommunicationService>("ISystemLocalCommunicationService"); 157 rb.PushIpcInterface<ISystemLocalCommunicationService>();
112 } 158 }
113}; 159};
114 160
@@ -129,7 +175,7 @@ public:
129 175
130 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 176 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
131 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
132 rb.PushIpcInterface<ILocalCommunicationService>("IUserLocalCommunicationService"); 178 rb.PushIpcInterface<IUserLocalCommunicationService>();
133 } 179 }
134}; 180};
135 181
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 609102f2c..5af925515 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -86,6 +86,7 @@ public:
86 {2, &RelocatableObject::LoadNrr, "LoadNrr"}, 86 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
87 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, 87 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
88 {4, &RelocatableObject::Initialize, "Initialize"}, 88 {4, &RelocatableObject::Initialize, "Initialize"},
89 {10, nullptr, "LoadNrrEx"},
89 }; 90 };
90 // clang-format on 91 // clang-format on
91 92
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 60479bb45..f92571008 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -15,12 +15,16 @@ namespace Service::NIFM {
15class IScanRequest final : public ServiceFramework<IScanRequest> { 15class IScanRequest final : public ServiceFramework<IScanRequest> {
16public: 16public:
17 explicit IScanRequest() : ServiceFramework("IScanRequest") { 17 explicit IScanRequest() : ServiceFramework("IScanRequest") {
18 // clang-format off
18 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
19 {0, nullptr, "Submit"}, 20 {0, nullptr, "Submit"},
20 {1, nullptr, "IsProcessing"}, 21 {1, nullptr, "IsProcessing"},
21 {2, nullptr, "GetResult"}, 22 {2, nullptr, "GetResult"},
22 {3, nullptr, "GetSystemEventReadableHandle"}, 23 {3, nullptr, "GetSystemEventReadableHandle"},
24 {4, nullptr, "SetChannels"},
23 }; 25 };
26 // clang-format on
27
24 RegisterHandlers(functions); 28 RegisterHandlers(functions);
25 } 29 }
26}; 30};
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
index ccb6f9da9..8751522ca 100644
--- a/src/core/hle/service/npns/npns.cpp
+++ b/src/core/hle/service/npns/npns.cpp
@@ -45,7 +45,7 @@ public:
45 {114, nullptr, "AttachJid"}, 45 {114, nullptr, "AttachJid"},
46 {115, nullptr, "DetachJid"}, 46 {115, nullptr, "DetachJid"},
47 {201, nullptr, "RequestChangeStateForceTimed"}, 47 {201, nullptr, "RequestChangeStateForceTimed"},
48 {102, nullptr, "RequestChangeStateForceAsync"}, 48 {202, nullptr, "RequestChangeStateForceAsync"},
49 }; 49 };
50 // clang-format on 50 // clang-format on
51 51
@@ -73,6 +73,7 @@ public:
73 {103, nullptr, "GetState"}, 73 {103, nullptr, "GetState"},
74 {104, nullptr, "GetStatistics"}, 74 {104, nullptr, "GetStatistics"},
75 {111, nullptr, "GetJid"}, 75 {111, nullptr, "GetJid"},
76 {120, nullptr, "CreateNotificationReceiver"},
76 }; 77 };
77 // clang-format on 78 // clang-format on
78 79
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index 6081f41e1..c75b4ee34 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -12,10 +12,10 @@ namespace Service::PCTL {
12class IParentalControlService final : public ServiceFramework<IParentalControlService> { 12class IParentalControlService final : public ServiceFramework<IParentalControlService> {
13public: 13public:
14 IParentalControlService() : ServiceFramework("IParentalControlService") { 14 IParentalControlService() : ServiceFramework("IParentalControlService") {
15 // clang-format off
15 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
16 {1, &IParentalControlService::Initialize, "Initialize"}, 17 {1, &IParentalControlService::Initialize, "Initialize"},
17 {1001, &IParentalControlService::CheckFreeCommunicationPermission, 18 {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"},
18 "CheckFreeCommunicationPermission"},
19 {1002, nullptr, "ConfirmLaunchApplicationPermission"}, 19 {1002, nullptr, "ConfirmLaunchApplicationPermission"},
20 {1003, nullptr, "ConfirmResumeApplicationPermission"}, 20 {1003, nullptr, "ConfirmResumeApplicationPermission"},
21 {1004, nullptr, "ConfirmSnsPostPermission"}, 21 {1004, nullptr, "ConfirmSnsPostPermission"},
@@ -30,6 +30,7 @@ public:
30 {1013, nullptr, "ConfirmStereoVisionPermission"}, 30 {1013, nullptr, "ConfirmStereoVisionPermission"},
31 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, 31 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
32 {1015, nullptr, "ConfirmPlayableApplicationVideo"}, 32 {1015, nullptr, "ConfirmPlayableApplicationVideo"},
33 {1016, nullptr, "ConfirmShowNewsPermission"},
33 {1031, nullptr, "IsRestrictionEnabled"}, 34 {1031, nullptr, "IsRestrictionEnabled"},
34 {1032, nullptr, "GetSafetyLevel"}, 35 {1032, nullptr, "GetSafetyLevel"},
35 {1033, nullptr, "SetSafetyLevel"}, 36 {1033, nullptr, "SetSafetyLevel"},
@@ -45,6 +46,7 @@ public:
45 {1045, nullptr, "UpdateFreeCommunicationApplicationList"}, 46 {1045, nullptr, "UpdateFreeCommunicationApplicationList"},
46 {1046, nullptr, "DisableFeaturesForReset"}, 47 {1046, nullptr, "DisableFeaturesForReset"},
47 {1047, nullptr, "NotifyApplicationDownloadStarted"}, 48 {1047, nullptr, "NotifyApplicationDownloadStarted"},
49 {1048, nullptr, "NotifyNetworkProfileCreated"},
48 {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"}, 50 {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"},
49 {1062, nullptr, "GetStereoVisionRestriction"}, 51 {1062, nullptr, "GetStereoVisionRestriction"},
50 {1063, nullptr, "SetStereoVisionRestriction"}, 52 {1063, nullptr, "SetStereoVisionRestriction"},
@@ -63,6 +65,7 @@ public:
63 {1411, nullptr, "GetPairingAccountInfo"}, 65 {1411, nullptr, "GetPairingAccountInfo"},
64 {1421, nullptr, "GetAccountNickname"}, 66 {1421, nullptr, "GetAccountNickname"},
65 {1424, nullptr, "GetAccountState"}, 67 {1424, nullptr, "GetAccountState"},
68 {1425, nullptr, "RequestPostEvents"},
66 {1432, nullptr, "GetSynchronizationEvent"}, 69 {1432, nullptr, "GetSynchronizationEvent"},
67 {1451, nullptr, "StartPlayTimer"}, 70 {1451, nullptr, "StartPlayTimer"},
68 {1452, nullptr, "StopPlayTimer"}, 71 {1452, nullptr, "StopPlayTimer"},
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 6b27dc4a3..ebcc41a43 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -42,15 +42,18 @@ private:
42class DebugMonitor final : public ServiceFramework<DebugMonitor> { 42class DebugMonitor final : public ServiceFramework<DebugMonitor> {
43public: 43public:
44 explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { 44 explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} {
45 // clang-format off
45 static const FunctionInfo functions[] = { 46 static const FunctionInfo functions[] = {
46 {0, nullptr, "IsDebugMode"}, 47 {0, nullptr, "GetDebugProcesses"},
47 {1, nullptr, "GetDebugProcesses"}, 48 {1, nullptr, "StartDebugProcess"},
48 {2, nullptr, "StartDebugProcess"}, 49 {2, nullptr, "GetTitlePid"},
49 {3, nullptr, "GetTitlePid"}, 50 {3, nullptr, "EnableDebugForTitleId"},
50 {4, nullptr, "EnableDebugForTitleId"}, 51 {4, nullptr, "GetApplicationPid"},
51 {5, nullptr, "GetApplicationPid"}, 52 {5, nullptr, "EnableDebugForApplication"},
52 {6, nullptr, "EnableDebugForApplication"}, 53 {6, nullptr, "DisableDebug"},
53 }; 54 };
55 // clang-format on
56
54 RegisterHandlers(functions); 57 RegisterHandlers(functions);
55 } 58 }
56}; 59};
@@ -68,6 +71,7 @@ public:
68class Shell final : public ServiceFramework<Shell> { 71class Shell final : public ServiceFramework<Shell> {
69public: 72public:
70 explicit Shell() : ServiceFramework{"pm:shell"} { 73 explicit Shell() : ServiceFramework{"pm:shell"} {
74 // clang-format off
71 static const FunctionInfo functions[] = { 75 static const FunctionInfo functions[] = {
72 {0, nullptr, "LaunchProcess"}, 76 {0, nullptr, "LaunchProcess"},
73 {1, nullptr, "TerminateProcessByPid"}, 77 {1, nullptr, "TerminateProcessByPid"},
@@ -77,7 +81,10 @@ public:
77 {5, nullptr, "NotifyBootFinished"}, 81 {5, nullptr, "NotifyBootFinished"},
78 {6, nullptr, "GetApplicationPid"}, 82 {6, nullptr, "GetApplicationPid"},
79 {7, nullptr, "BoostSystemMemoryResourceLimit"}, 83 {7, nullptr, "BoostSystemMemoryResourceLimit"},
84 {8, nullptr, "EnableAdditionalSystemThreads"},
80 }; 85 };
86 // clang-format on
87
81 RegisterHandlers(functions); 88 RegisterHandlers(functions);
82 } 89 }
83}; 90};
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 1afc43f75..4ecb6bcef 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -116,6 +116,7 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
116} 116}
117 117
118SET::SET() : ServiceFramework("set") { 118SET::SET() : ServiceFramework("set") {
119 // clang-format off
119 static const FunctionInfo functions[] = { 120 static const FunctionInfo functions[] = {
120 {0, &SET::GetLanguageCode, "GetLanguageCode"}, 121 {0, &SET::GetLanguageCode, "GetLanguageCode"},
121 {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, 122 {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
@@ -126,7 +127,10 @@ SET::SET() : ServiceFramework("set") {
126 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, 127 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
127 {7, nullptr, "GetKeyCodeMap"}, 128 {7, nullptr, "GetKeyCodeMap"},
128 {8, nullptr, "GetQuestFlag"}, 129 {8, nullptr, "GetQuestFlag"},
130 {9, nullptr, "GetKeyCodeMap2"},
129 }; 131 };
132 // clang-format on
133
130 RegisterHandlers(functions); 134 RegisterHandlers(functions);
131} 135}
132 136
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 34654bb07..5981c575c 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -40,7 +40,7 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, 40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"},
41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, 41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
42 {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, 42 {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
43 {33, nullptr, "GetBatteryVersion"}, 43 {41, nullptr, "GetBatteryVersion"},
44 }; 44 };
45 RegisterHandlers(functions); 45 RegisterHandlers(functions);
46} 46}
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index ecee554bf..98d0cfdfd 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -104,6 +104,7 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
104} 104}
105 105
106SET_SYS::SET_SYS() : ServiceFramework("set:sys") { 106SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
107 // clang-format off
107 static const FunctionInfo functions[] = { 108 static const FunctionInfo functions[] = {
108 {0, nullptr, "SetLanguageCode"}, 109 {0, nullptr, "SetLanguageCode"},
109 {1, nullptr, "SetNetworkSettings"}, 110 {1, nullptr, "SetNetworkSettings"},
@@ -252,7 +253,33 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
252 {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, 253 {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
253 {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, 254 {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
254 {149, nullptr, "GetRebootlessSystemUpdateVersion"}, 255 {149, nullptr, "GetRebootlessSystemUpdateVersion"},
256 {150, nullptr, "GetDeviceTimeZoneLocationUpdatedTime"},
257 {151, nullptr, "SetDeviceTimeZoneLocationUpdatedTime"},
258 {152, nullptr, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
259 {153, nullptr, "SetUserSystemClockAutomaticCorrectionUpdatedTime"},
260 {154, nullptr, "GetAccountOnlineStorageSettings"},
261 {155, nullptr, "SetAccountOnlineStorageSettings"},
262 {156, nullptr, "GetPctlReadyFlag"},
263 {157, nullptr, "SetPctlReadyFlag"},
264 {162, nullptr, "GetPtmBatteryVersion"},
265 {163, nullptr, "SetPtmBatteryVersion"},
266 {164, nullptr, "GetUsb30HostEnableFlag"},
267 {165, nullptr, "SetUsb30HostEnableFlag"},
268 {166, nullptr, "GetUsb30DeviceEnableFlag"},
269 {167, nullptr, "SetUsb30DeviceEnableFlag"},
270 {168, nullptr, "GetThemeId"},
271 {169, nullptr, "SetThemeId"},
272 {170, nullptr, "GetChineseTraditionalInputMethod"},
273 {171, nullptr, "SetChineseTraditionalInputMethod"},
274 {172, nullptr, "GetPtmCycleCountReliability"},
275 {173, nullptr, "SetPtmCycleCountReliability"},
276 {175, nullptr, "GetThemeSettings"},
277 {176, nullptr, "SetThemeSettings"},
278 {177, nullptr, "GetThemeKey"},
279 {178, nullptr, "SetThemeKey"},
255 }; 280 };
281 // clang-format on
282
256 RegisterHandlers(functions); 283 RegisterHandlers(functions);
257} 284}
258 285
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 4342f3b2d..884ad173b 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -73,6 +73,7 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
73} 73}
74 74
75BSD::BSD(const char* name) : ServiceFramework(name) { 75BSD::BSD(const char* name) : ServiceFramework(name) {
76 // clang-format off
76 static const FunctionInfo functions[] = { 77 static const FunctionInfo functions[] = {
77 {0, &BSD::RegisterClient, "RegisterClient"}, 78 {0, &BSD::RegisterClient, "RegisterClient"},
78 {1, &BSD::StartMonitoring, "StartMonitoring"}, 79 {1, &BSD::StartMonitoring, "StartMonitoring"},
@@ -105,7 +106,11 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
105 {28, nullptr, "GetResourceStatistics"}, 106 {28, nullptr, "GetResourceStatistics"},
106 {29, nullptr, "RecvMMsg"}, 107 {29, nullptr, "RecvMMsg"},
107 {30, nullptr, "SendMMsg"}, 108 {30, nullptr, "SendMMsg"},
109 {31, nullptr, "EventFd"},
110 {32, nullptr, "RegisterResourceStatisticsName"},
108 }; 111 };
112 // clang-format on
113
109 RegisterHandlers(functions); 114 RegisterHandlers(functions);
110} 115}
111 116
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index f7f87a958..65040c077 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -103,6 +103,8 @@ public:
103 {4, nullptr, "DebugIoctl"}, 103 {4, nullptr, "DebugIoctl"},
104 {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"}, 104 {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
105 {6, nullptr, "FlushSessionCache"}, 105 {6, nullptr, "FlushSessionCache"},
106 {7, nullptr, "SetDebugOption"},
107 {8, nullptr, "GetDebugOption"},
106 }; 108 };
107 // clang-format on 109 // clang-format on
108 110
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index b3a196f65..8d122ae33 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -8,6 +8,7 @@ namespace Service::Time {
8 8
9Time::Time(std::shared_ptr<Module> time, const char* name) 9Time::Time(std::shared_ptr<Module> time, const char* name)
10 : Module::Interface(std::move(time), name) { 10 : Module::Interface(std::move(time), name) {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, 13 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
13 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, 14 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
@@ -15,18 +16,23 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
15 {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, 16 {3, &Time::GetTimeZoneService, "GetTimeZoneService"},
16 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, 17 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
17 {5, nullptr, "GetEphemeralNetworkSystemClock"}, 18 {5, nullptr, "GetEphemeralNetworkSystemClock"},
19 {20, nullptr, "GetSharedMemoryNativeHandle"},
20 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
21 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
18 {50, nullptr, "SetStandardSteadyClockInternalOffset"}, 22 {50, nullptr, "SetStandardSteadyClockInternalOffset"},
19 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, 23 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
20 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, 24 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
21 {102, nullptr, "GetStandardUserSystemClockInitialYear"}, 25 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, 26 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
27 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 28 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, 29 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 30 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, 31 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
27 "CalculateStandardUserSystemClockDifferenceByUser"},
28 {501, nullptr, "CalculateSpanBetween"}, 32 {501, nullptr, "CalculateSpanBetween"},
29 }; 33 };
34 // clang-format on
35
30 RegisterHandlers(functions); 36 RegisterHandlers(functions);
31} 37}
32 38
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 07aa7a1cd..10b13fb1d 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -86,25 +86,29 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
86 return FileType::Error; 86 return FileType::Error;
87} 87}
88 88
89ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) { 89AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
90 Kernel::Process& process) {
90 if (is_loaded) { 91 if (is_loaded) {
91 return ResultStatus::ErrorAlreadyLoaded; 92 return {ResultStatus::ErrorAlreadyLoaded, {}};
92 } 93 }
93 94
94 if (dir == nullptr) { 95 if (dir == nullptr) {
95 if (file == nullptr) 96 if (file == nullptr) {
96 return ResultStatus::ErrorNullFile; 97 return {ResultStatus::ErrorNullFile, {}};
98 }
99
97 dir = file->GetContainingDirectory(); 100 dir = file->GetContainingDirectory();
98 } 101 }
99 102
100 // Read meta to determine title ID 103 // Read meta to determine title ID
101 FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); 104 FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
102 if (npdm == nullptr) 105 if (npdm == nullptr) {
103 return ResultStatus::ErrorMissingNPDM; 106 return {ResultStatus::ErrorMissingNPDM, {}};
107 }
104 108
105 ResultStatus result = metadata.Load(npdm); 109 const ResultStatus result = metadata.Load(npdm);
106 if (result != ResultStatus::Success) { 110 if (result != ResultStatus::Success) {
107 return result; 111 return {result, {}};
108 } 112 }
109 113
110 if (override_update) { 114 if (override_update) {
@@ -114,23 +118,24 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
114 118
115 // Reread in case PatchExeFS affected the main.npdm 119 // Reread in case PatchExeFS affected the main.npdm
116 npdm = dir->GetFile("main.npdm"); 120 npdm = dir->GetFile("main.npdm");
117 if (npdm == nullptr) 121 if (npdm == nullptr) {
118 return ResultStatus::ErrorMissingNPDM; 122 return {ResultStatus::ErrorMissingNPDM, {}};
123 }
119 124
120 ResultStatus result2 = metadata.Load(npdm); 125 const ResultStatus result2 = metadata.Load(npdm);
121 if (result2 != ResultStatus::Success) { 126 if (result2 != ResultStatus::Success) {
122 return result2; 127 return {result2, {}};
123 } 128 }
124 metadata.Print(); 129 metadata.Print();
125 130
126 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; 131 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
127 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit || 132 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
128 arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) { 133 arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
129 return ResultStatus::Error32BitISA; 134 return {ResultStatus::Error32BitISA, {}};
130 } 135 }
131 136
132 if (process.LoadFromMetadata(metadata).IsError()) { 137 if (process.LoadFromMetadata(metadata).IsError()) {
133 return ResultStatus::ErrorUnableToParseKernelMetadata; 138 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
134 } 139 }
135 140
136 const FileSys::PatchManager pm(metadata.GetTitleID()); 141 const FileSys::PatchManager pm(metadata.GetTitleID());
@@ -150,7 +155,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
150 const auto tentative_next_load_addr = 155 const auto tentative_next_load_addr =
151 AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm); 156 AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
152 if (!tentative_next_load_addr) { 157 if (!tentative_next_load_addr) {
153 return ResultStatus::ErrorLoadingNSO; 158 return {ResultStatus::ErrorLoadingNSO, {}};
154 } 159 }
155 160
156 next_load_addr = *tentative_next_load_addr; 161 next_load_addr = *tentative_next_load_addr;
@@ -159,8 +164,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
159 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); 164 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
160 } 165 }
161 166
162 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
163
164 // Find the RomFS by searching for a ".romfs" file in this directory 167 // Find the RomFS by searching for a ".romfs" file in this directory
165 const auto& files = dir->GetFiles(); 168 const auto& files = dir->GetFiles();
166 const auto romfs_iter = 169 const auto romfs_iter =
@@ -175,7 +178,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
175 } 178 }
176 179
177 is_loaded = true; 180 is_loaded = true;
178 return ResultStatus::Success; 181 return {ResultStatus::Success,
182 LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
179} 183}
180 184
181ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) { 185ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1615cb5a8..1a65c16a4 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -37,7 +37,7 @@ public:
37 return IdentifyType(file); 37 return IdentifyType(file);
38 } 38 }
39 39
40 ResultStatus Load(Kernel::Process& process) override; 40 LoadResult Load(Kernel::Process& process) override;
41 41
42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
43 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 43 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 46ac372f6..6d4b02375 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -382,13 +382,15 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
382 return FileType::Error; 382 return FileType::Error;
383} 383}
384 384
385ResultStatus AppLoader_ELF::Load(Kernel::Process& process) { 385AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
386 if (is_loaded) 386 if (is_loaded) {
387 return ResultStatus::ErrorAlreadyLoaded; 387 return {ResultStatus::ErrorAlreadyLoaded, {}};
388 }
388 389
389 std::vector<u8> buffer = file->ReadAllBytes(); 390 std::vector<u8> buffer = file->ReadAllBytes();
390 if (buffer.size() != file->GetSize()) 391 if (buffer.size() != file->GetSize()) {
391 return ResultStatus::ErrorIncorrectELFFileSize; 392 return {ResultStatus::ErrorIncorrectELFFileSize, {}};
393 }
392 394
393 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 395 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
394 ElfReader elf_reader(&buffer[0]); 396 ElfReader elf_reader(&buffer[0]);
@@ -396,10 +398,9 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
396 const VAddr entry_point = codeset.entrypoint; 398 const VAddr entry_point = codeset.entrypoint;
397 399
398 process.LoadModule(std::move(codeset), entry_point); 400 process.LoadModule(std::move(codeset), entry_point);
399 process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
400 401
401 is_loaded = true; 402 is_loaded = true;
402 return ResultStatus::Success; 403 return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}};
403} 404}
404 405
405} // namespace Loader 406} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index a2d33021c..7ef7770a6 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -26,7 +26,7 @@ public:
26 return IdentifyType(file); 26 return IdentifyType(file);
27 } 27 }
28 28
29 ResultStatus Load(Kernel::Process& process) override; 29 LoadResult Load(Kernel::Process& process) override;
30}; 30};
31 31
32} // namespace Loader 32} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index bb925f4a6..f7846db52 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -131,6 +131,12 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
131/// Interface for loading an application 131/// Interface for loading an application
132class AppLoader : NonCopyable { 132class AppLoader : NonCopyable {
133public: 133public:
134 struct LoadParameters {
135 s32 main_thread_priority;
136 u64 main_thread_stack_size;
137 };
138 using LoadResult = std::pair<ResultStatus, std::optional<LoadParameters>>;
139
134 explicit AppLoader(FileSys::VirtualFile file); 140 explicit AppLoader(FileSys::VirtualFile file);
135 virtual ~AppLoader(); 141 virtual ~AppLoader();
136 142
@@ -145,7 +151,7 @@ public:
145 * @param process The newly created process. 151 * @param process The newly created process.
146 * @return The status result of the operation. 152 * @return The status result of the operation.
147 */ 153 */
148 virtual ResultStatus Load(Kernel::Process& process) = 0; 154 virtual LoadResult Load(Kernel::Process& process) = 0;
149 155
150 /** 156 /**
151 * Loads the system mode that this application needs. 157 * Loads the system mode that this application needs.
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 93a970d10..34efef09a 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,31 +41,37 @@ FileType AppLoader_NAX::GetFileType() const {
41 return IdentifyTypeImpl(*nax); 41 return IdentifyTypeImpl(*nax);
42} 42}
43 43
44ResultStatus AppLoader_NAX::Load(Kernel::Process& process) { 44AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
45 if (is_loaded) { 45 if (is_loaded) {
46 return ResultStatus::ErrorAlreadyLoaded; 46 return {ResultStatus::ErrorAlreadyLoaded, {}};
47 } 47 }
48 48
49 if (nax->GetStatus() != ResultStatus::Success) 49 const auto nax_status = nax->GetStatus();
50 return nax->GetStatus(); 50 if (nax_status != ResultStatus::Success) {
51 return {nax_status, {}};
52 }
51 53
52 const auto nca = nax->AsNCA(); 54 const auto nca = nax->AsNCA();
53 if (nca == nullptr) { 55 if (nca == nullptr) {
54 if (!Core::Crypto::KeyManager::KeyFileExists(false)) 56 if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
55 return ResultStatus::ErrorMissingProductionKeyFile; 57 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
56 return ResultStatus::ErrorNAXInconvertibleToNCA; 58 }
59
60 return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
57 } 61 }
58 62
59 if (nca->GetStatus() != ResultStatus::Success) 63 const auto nca_status = nca->GetStatus();
60 return nca->GetStatus(); 64 if (nca_status != ResultStatus::Success) {
65 return {nca_status, {}};
66 }
61 67
62 const auto result = nca_loader->Load(process); 68 const auto result = nca_loader->Load(process);
63 if (result != ResultStatus::Success) 69 if (result.first != ResultStatus::Success) {
64 return result; 70 return result;
71 }
65 72
66 is_loaded = true; 73 is_loaded = true;
67 74 return result;
68 return ResultStatus::Success;
69} 75}
70 76
71ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) { 77ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index f40079574..00f1659c1 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -33,7 +33,7 @@ public:
33 33
34 FileType GetFileType() const override; 34 FileType GetFileType() const override;
35 35
36 ResultStatus Load(Kernel::Process& process) override; 36 LoadResult Load(Kernel::Process& process) override;
37 37
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 39 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index ce8196fcf..b3f8f1083 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -30,36 +30,38 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
30 return FileType::Error; 30 return FileType::Error;
31} 31}
32 32
33ResultStatus AppLoader_NCA::Load(Kernel::Process& process) { 33AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
34 if (is_loaded) { 34 if (is_loaded) {
35 return ResultStatus::ErrorAlreadyLoaded; 35 return {ResultStatus::ErrorAlreadyLoaded, {}};
36 } 36 }
37 37
38 const auto result = nca->GetStatus(); 38 const auto result = nca->GetStatus();
39 if (result != ResultStatus::Success) { 39 if (result != ResultStatus::Success) {
40 return result; 40 return {result, {}};
41 } 41 }
42 42
43 if (nca->GetType() != FileSys::NCAContentType::Program) 43 if (nca->GetType() != FileSys::NCAContentType::Program) {
44 return ResultStatus::ErrorNCANotProgram; 44 return {ResultStatus::ErrorNCANotProgram, {}};
45 }
45 46
46 const auto exefs = nca->GetExeFS(); 47 const auto exefs = nca->GetExeFS();
47 48 if (exefs == nullptr) {
48 if (exefs == nullptr) 49 return {ResultStatus::ErrorNoExeFS, {}};
49 return ResultStatus::ErrorNoExeFS; 50 }
50 51
51 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); 52 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
52 53
53 const auto load_result = directory_loader->Load(process); 54 const auto load_result = directory_loader->Load(process);
54 if (load_result != ResultStatus::Success) 55 if (load_result.first != ResultStatus::Success) {
55 return load_result; 56 return load_result;
57 }
56 58
57 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) 59 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
58 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 60 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
61 }
59 62
60 is_loaded = true; 63 is_loaded = true;
61 64 return load_result;
62 return ResultStatus::Success;
63} 65}
64 66
65ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { 67ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index b9f077468..94f0ed677 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -33,7 +33,7 @@ public:
33 return IdentifyType(file); 33 return IdentifyType(file);
34 } 34 }
35 35
36 ResultStatus Load(Kernel::Process& process) override; 36 LoadResult Load(Kernel::Process& process) override;
37 37
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 39 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 31e4a0c84..6a0ca389b 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -201,25 +201,25 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
201 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base); 201 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
202} 202}
203 203
204ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { 204AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
205 if (is_loaded) { 205 if (is_loaded) {
206 return ResultStatus::ErrorAlreadyLoaded; 206 return {ResultStatus::ErrorAlreadyLoaded, {}};
207 } 207 }
208 208
209 // Load NRO 209 // Load NRO
210 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 210 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
211 211
212 if (!LoadNro(process, *file, base_address)) { 212 if (!LoadNro(process, *file, base_address)) {
213 return ResultStatus::ErrorLoadingNRO; 213 return {ResultStatus::ErrorLoadingNRO, {}};
214 } 214 }
215 215
216 if (romfs != nullptr) 216 if (romfs != nullptr) {
217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
218 218 }
219 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
220 219
221 is_loaded = true; 220 is_loaded = true;
222 return ResultStatus::Success; 221 return {ResultStatus::Success,
222 LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
223} 223}
224 224
225ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { 225ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 85b0ed644..1ffdae805 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -37,7 +37,7 @@ public:
37 return IdentifyType(file); 37 return IdentifyType(file);
38 } 38 }
39 39
40 ResultStatus Load(Kernel::Process& process) override; 40 LoadResult Load(Kernel::Process& process) override;
41 41
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 42 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index d7c47c197..a86653204 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -169,22 +169,21 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
169 return load_base + image_size; 169 return load_base + image_size;
170} 170}
171 171
172ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { 172AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
173 if (is_loaded) { 173 if (is_loaded) {
174 return ResultStatus::ErrorAlreadyLoaded; 174 return {ResultStatus::ErrorAlreadyLoaded, {}};
175 } 175 }
176 176
177 // Load module 177 // Load module
178 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 178 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
179 if (!LoadModule(process, *file, base_address, true)) { 179 if (!LoadModule(process, *file, base_address, true)) {
180 return ResultStatus::ErrorLoadingNSO; 180 return {ResultStatus::ErrorLoadingNSO, {}};
181 } 181 }
182 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 182 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
183 183
184 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
185
186 is_loaded = true; 184 is_loaded = true;
187 return ResultStatus::Success; 185 return {ResultStatus::Success,
186 LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
188} 187}
189 188
190} // namespace Loader 189} // namespace Loader
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 4674c3724..fdce9191c 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -84,7 +84,7 @@ public:
84 VAddr load_base, bool should_pass_arguments, 84 VAddr load_base, bool should_pass_arguments,
85 std::optional<FileSys::PatchManager> pm = {}); 85 std::optional<FileSys::PatchManager> pm = {});
86 86
87 ResultStatus Load(Kernel::Process& process) override; 87 LoadResult Load(Kernel::Process& process) override;
88}; 88};
89 89
90} // namespace Loader 90} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 7da1f8960..ad56bbb38 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -72,37 +72,45 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
72 return FileType::Error; 72 return FileType::Error;
73} 73}
74 74
75ResultStatus AppLoader_NSP::Load(Kernel::Process& process) { 75AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
76 if (is_loaded) { 76 if (is_loaded) {
77 return ResultStatus::ErrorAlreadyLoaded; 77 return {ResultStatus::ErrorAlreadyLoaded, {}};
78 } 78 }
79 79
80 if (title_id == 0) 80 if (title_id == 0) {
81 return ResultStatus::ErrorNSPMissingProgramNCA; 81 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
82 }
82 83
83 if (nsp->GetStatus() != ResultStatus::Success) 84 const auto nsp_status = nsp->GetStatus();
84 return nsp->GetStatus(); 85 if (nsp_status != ResultStatus::Success) {
86 return {nsp_status, {}};
87 }
85 88
86 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) 89 const auto nsp_program_status = nsp->GetProgramStatus(title_id);
87 return nsp->GetProgramStatus(title_id); 90 if (nsp_program_status != ResultStatus::Success) {
91 return {nsp_program_status, {}};
92 }
88 93
89 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { 94 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
90 if (!Core::Crypto::KeyManager::KeyFileExists(false)) 95 if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
91 return ResultStatus::ErrorMissingProductionKeyFile; 96 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
92 return ResultStatus::ErrorNSPMissingProgramNCA; 97 }
98
99 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
93 } 100 }
94 101
95 const auto result = secondary_loader->Load(process); 102 const auto result = secondary_loader->Load(process);
96 if (result != ResultStatus::Success) 103 if (result.first != ResultStatus::Success) {
97 return result; 104 return result;
105 }
98 106
99 FileSys::VirtualFile update_raw; 107 FileSys::VirtualFile update_raw;
100 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) 108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
101 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 109 Service::FileSystem::SetPackedUpdate(std::move(update_raw));
110 }
102 111
103 is_loaded = true; 112 is_loaded = true;
104 113 return result;
105 return ResultStatus::Success;
106} 114}
107 115
108ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) { 116ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 953a1b508..85e870bdf 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -35,7 +35,7 @@ public:
35 return IdentifyType(file); 35 return IdentifyType(file);
36 } 36 }
37 37
38 ResultStatus Load(Kernel::Process& process) override; 38 LoadResult Load(Kernel::Process& process) override;
39 39
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 41 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 89f7bbf77..1e285a053 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -48,31 +48,35 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
48 return FileType::Error; 48 return FileType::Error;
49} 49}
50 50
51ResultStatus AppLoader_XCI::Load(Kernel::Process& process) { 51AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
52 if (is_loaded) { 52 if (is_loaded) {
53 return ResultStatus::ErrorAlreadyLoaded; 53 return {ResultStatus::ErrorAlreadyLoaded, {}};
54 } 54 }
55 55
56 if (xci->GetStatus() != ResultStatus::Success) 56 if (xci->GetStatus() != ResultStatus::Success) {
57 return xci->GetStatus(); 57 return {xci->GetStatus(), {}};
58 }
58 59
59 if (xci->GetProgramNCAStatus() != ResultStatus::Success) 60 if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
60 return xci->GetProgramNCAStatus(); 61 return {xci->GetProgramNCAStatus(), {}};
62 }
61 63
62 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) 64 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
63 return ResultStatus::ErrorMissingProductionKeyFile; 65 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
66 }
64 67
65 const auto result = nca_loader->Load(process); 68 const auto result = nca_loader->Load(process);
66 if (result != ResultStatus::Success) 69 if (result.first != ResultStatus::Success) {
67 return result; 70 return result;
71 }
68 72
69 FileSys::VirtualFile update_raw; 73 FileSys::VirtualFile update_raw;
70 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) 74 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
71 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 75 Service::FileSystem::SetPackedUpdate(std::move(update_raw));
76 }
72 77
73 is_loaded = true; 78 is_loaded = true;
74 79 return result;
75 return ResultStatus::Success;
76} 80}
77 81
78ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) { 82ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 436f7387c..ae7145b14 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -35,7 +35,7 @@ public:
35 return IdentifyType(file); 35 return IdentifyType(file);
36 } 36 }
37 37
38 ResultStatus Load(Kernel::Process& process) override; 38 LoadResult Load(Kernel::Process& process) override;
39 39
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 41 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4e0538bc2..f18f6226b 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -26,16 +26,16 @@ namespace Memory {
26 26
27static Common::PageTable* current_page_table = nullptr; 27static Common::PageTable* current_page_table = nullptr;
28 28
29void SetCurrentPageTable(Common::PageTable* page_table) { 29void SetCurrentPageTable(Kernel::Process& process) {
30 current_page_table = page_table; 30 current_page_table = &process.VMManager().page_table;
31
32 const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
31 33
32 auto& system = Core::System::GetInstance(); 34 auto& system = Core::System::GetInstance();
33 if (system.IsPoweredOn()) { 35 system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
34 system.ArmInterface(0).PageTableChanged(); 36 system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
35 system.ArmInterface(1).PageTableChanged(); 37 system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
36 system.ArmInterface(2).PageTableChanged(); 38 system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
37 system.ArmInterface(3).PageTableChanged();
38 }
39} 39}
40 40
41static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, 41static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
diff --git a/src/core/memory.h b/src/core/memory.h
index 6845f5fe1..b9fa18b1d 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -40,8 +40,9 @@ enum : VAddr {
40 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, 40 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
41}; 41};
42 42
43/// Changes the currently active page table. 43/// Changes the currently active page table to that of
44void SetCurrentPageTable(Common::PageTable* page_table); 44/// the given process instance.
45void SetCurrentPageTable(Kernel::Process& process);
45 46
46/// Determines if the given VAddr is valid for the specified process. 47/// Determines if the given VAddr is valid for the specified process.
47bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); 48bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 6d32ebea3..c1365879b 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -90,6 +90,7 @@ void LogSettings() {
90 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); 90 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
91 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 91 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
92 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); 92 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
93 LogSetting("Renderer_UseCompatibilityProfile", Settings::values.use_compatibility_profile);
93 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); 94 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
94 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); 95 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
95 LogSetting("Renderer_UseAsynchronousGpuEmulation", 96 LogSetting("Renderer_UseAsynchronousGpuEmulation",
diff --git a/src/core/settings.h b/src/core/settings.h
index b84390745..5ff3634aa 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -390,6 +390,7 @@ struct Values {
390 float resolution_factor; 390 float resolution_factor;
391 bool use_frame_limit; 391 bool use_frame_limit;
392 u16 frame_limit; 392 u16 frame_limit;
393 bool use_compatibility_profile;
393 bool use_disk_shader_cache; 394 bool use_disk_shader_cache;
394 bool use_accurate_gpu_emulation; 395 bool use_accurate_gpu_emulation;
395 bool use_asynchronous_gpu_emulation; 396 bool use_asynchronous_gpu_emulation;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 114bed20d..6821f275d 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -36,6 +36,8 @@ add_library(video_core STATIC
36 renderer_base.h 36 renderer_base.h
37 renderer_opengl/gl_buffer_cache.cpp 37 renderer_opengl/gl_buffer_cache.cpp
38 renderer_opengl/gl_buffer_cache.h 38 renderer_opengl/gl_buffer_cache.h
39 renderer_opengl/gl_device.cpp
40 renderer_opengl/gl_device.h
39 renderer_opengl/gl_global_cache.cpp 41 renderer_opengl/gl_global_cache.cpp
40 renderer_opengl/gl_global_cache.h 42 renderer_opengl/gl_global_cache.h
41 renderer_opengl/gl_primitive_assembler.cpp 43 renderer_opengl/gl_primitive_assembler.cpp
@@ -46,6 +48,8 @@ add_library(video_core STATIC
46 renderer_opengl/gl_rasterizer_cache.h 48 renderer_opengl/gl_rasterizer_cache.h
47 renderer_opengl/gl_resource_manager.cpp 49 renderer_opengl/gl_resource_manager.cpp
48 renderer_opengl/gl_resource_manager.h 50 renderer_opengl/gl_resource_manager.h
51 renderer_opengl/gl_sampler_cache.cpp
52 renderer_opengl/gl_sampler_cache.h
49 renderer_opengl/gl_shader_cache.cpp 53 renderer_opengl/gl_shader_cache.cpp
50 renderer_opengl/gl_shader_cache.h 54 renderer_opengl/gl_shader_cache.h
51 renderer_opengl/gl_shader_decompiler.cpp 55 renderer_opengl/gl_shader_decompiler.cpp
@@ -67,6 +71,8 @@ add_library(video_core STATIC
67 renderer_opengl/renderer_opengl.h 71 renderer_opengl/renderer_opengl.h
68 renderer_opengl/utils.cpp 72 renderer_opengl/utils.cpp
69 renderer_opengl/utils.h 73 renderer_opengl/utils.h
74 sampler_cache.cpp
75 sampler_cache.h
70 shader/decode/arithmetic.cpp 76 shader/decode/arithmetic.cpp
71 shader/decode/arithmetic_immediate.cpp 77 shader/decode/arithmetic_immediate.cpp
72 shader/decode/bfe.cpp 78 shader/decode/bfe.cpp
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 046d047cb..036e66f05 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -57,8 +57,8 @@ bool DmaPusher::Step() {
57 57
58 // Push buffer non-empty, read a word 58 // Push buffer non-empty, read a word
59 command_headers.resize(command_list_header.size); 59 command_headers.resize(command_list_header.size);
60 gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), 60 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
61 command_list_header.size * sizeof(u32)); 61 command_list_header.size * sizeof(u32));
62 62
63 for (const CommandHeader& command_header : command_headers) { 63 for (const CommandHeader& command_header : command_headers) {
64 64
@@ -105,6 +105,8 @@ bool DmaPusher::Step() {
105 dma_state.non_incrementing = false; 105 dma_state.non_incrementing = false;
106 dma_increment_once = true; 106 dma_increment_once = true;
107 break; 107 break;
108 default:
109 break;
108 } 110 }
109 } 111 }
110 } 112 }
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index cd51a31d7..7387886a3 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -10,6 +10,7 @@
10#include "video_core/memory_manager.h" 10#include "video_core/memory_manager.h"
11#include "video_core/rasterizer_interface.h" 11#include "video_core/rasterizer_interface.h"
12#include "video_core/renderer_base.h" 12#include "video_core/renderer_base.h"
13#include "video_core/textures/decoders.h"
13 14
14namespace Tegra::Engines { 15namespace Tegra::Engines {
15 16
@@ -27,30 +28,46 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
27 28
28 switch (method_call.method) { 29 switch (method_call.method) {
29 case KEPLERMEMORY_REG_INDEX(exec): { 30 case KEPLERMEMORY_REG_INDEX(exec): {
30 state.write_offset = 0; 31 ProcessExec();
31 break; 32 break;
32 } 33 }
33 case KEPLERMEMORY_REG_INDEX(data): { 34 case KEPLERMEMORY_REG_INDEX(data): {
34 ProcessData(method_call.argument); 35 ProcessData(method_call.argument, method_call.IsLastCall());
35 break; 36 break;
36 } 37 }
37 } 38 }
38} 39}
39 40
40void KeplerMemory::ProcessData(u32 data) { 41void KeplerMemory::ProcessExec() {
41 ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported"); 42 state.write_offset = 0;
42 ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0); 43 state.copy_size = regs.line_length_in * regs.line_count;
43 44 state.inner_buffer.resize(state.copy_size);
44 // We have to invalidate the destination region to evict any outdated surfaces from the cache. 45}
45 // We do this before actually writing the new data because the destination address might
46 // contain a dirty surface that will have to be written back to memory.
47 const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)};
48 rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32));
49 memory_manager.Write<u32>(address, data);
50
51 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
52 46
53 state.write_offset++; 47void KeplerMemory::ProcessData(u32 data, bool is_last_call) {
48 const u32 sub_copy_size = std::min(4U, state.copy_size - state.write_offset);
49 std::memcpy(&state.inner_buffer[state.write_offset], &regs.data, sub_copy_size);
50 state.write_offset += sub_copy_size;
51 if (is_last_call) {
52 const GPUVAddr address{regs.dest.Address()};
53 if (regs.exec.linear != 0) {
54 memory_manager.WriteBlock(address, state.inner_buffer.data(), state.copy_size);
55 } else {
56 UNIMPLEMENTED_IF(regs.dest.z != 0);
57 UNIMPLEMENTED_IF(regs.dest.depth != 1);
58 UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 1);
59 UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 1);
60 const std::size_t dst_size = Tegra::Texture::CalculateSize(
61 true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 1);
62 std::vector<u8> tmp_buffer(dst_size);
63 memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size);
64 Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x,
65 regs.dest.y, regs.dest.BlockHeight(), state.copy_size,
66 state.inner_buffer.data(), tmp_buffer.data());
67 memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size);
68 }
69 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
70 }
54} 71}
55 72
56} // namespace Tegra::Engines 73} // namespace Tegra::Engines
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 78b6c3e45..5f892ddad 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <vector>
9#include "common/bit_field.h" 10#include "common/bit_field.h"
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -51,7 +52,11 @@ public:
51 u32 address_high; 52 u32 address_high;
52 u32 address_low; 53 u32 address_low;
53 u32 pitch; 54 u32 pitch;
54 u32 block_dimensions; 55 union {
56 BitField<0, 4, u32> block_width;
57 BitField<4, 4, u32> block_height;
58 BitField<8, 4, u32> block_depth;
59 };
55 u32 width; 60 u32 width;
56 u32 height; 61 u32 height;
57 u32 depth; 62 u32 depth;
@@ -63,6 +68,18 @@ public:
63 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 68 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
64 address_low); 69 address_low);
65 } 70 }
71
72 u32 BlockWidth() const {
73 return 1U << block_width.Value();
74 }
75
76 u32 BlockHeight() const {
77 return 1U << block_height.Value();
78 }
79
80 u32 BlockDepth() const {
81 return 1U << block_depth.Value();
82 }
66 } dest; 83 } dest;
67 84
68 struct { 85 struct {
@@ -81,6 +98,8 @@ public:
81 98
82 struct { 99 struct {
83 u32 write_offset = 0; 100 u32 write_offset = 0;
101 u32 copy_size = 0;
102 std::vector<u8> inner_buffer;
84 } state{}; 103 } state{};
85 104
86private: 105private:
@@ -88,7 +107,8 @@ private:
88 VideoCore::RasterizerInterface& rasterizer; 107 VideoCore::RasterizerInterface& rasterizer;
89 MemoryManager& memory_manager; 108 MemoryManager& memory_manager;
90 109
91 void ProcessData(u32 data); 110 void ProcessExec();
111 void ProcessData(u32 data, bool is_last_call);
92}; 112};
93 113
94#define ASSERT_REG_POSITION(field_name, position) \ 114#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 74403eed4..9780417f2 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -418,7 +418,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
418 const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)}; 418 const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)};
419 419
420 Texture::TICEntry tic_entry; 420 Texture::TICEntry tic_entry;
421 memory_manager.ReadBlock(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); 421 memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
422 422
423 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || 423 ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear ||
424 tic_entry.header_version == Texture::TICHeaderVersion::Pitch, 424 tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
@@ -439,7 +439,7 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
439 const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)}; 439 const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)};
440 440
441 Texture::TSCEntry tsc_entry; 441 Texture::TSCEntry tsc_entry;
442 memory_manager.ReadBlock(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); 442 memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
443 return tsc_entry; 443 return tsc_entry;
444} 444}
445 445
@@ -482,19 +482,8 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
482 return textures; 482 return textures;
483} 483}
484 484
485Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, 485Texture::FullTextureInfo Maxwell3D::GetTextureInfo(const Texture::TextureHandle tex_handle,
486 std::size_t offset) const { 486 std::size_t offset) const {
487 auto& shader = state.shader_stages[static_cast<std::size_t>(stage)];
488 auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index];
489 ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
490
491 const GPUVAddr tex_info_address =
492 tex_info_buffer.address + offset * sizeof(Texture::TextureHandle);
493
494 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
495
496 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
497
498 Texture::FullTextureInfo tex_info{}; 487 Texture::FullTextureInfo tex_info{};
499 tex_info.index = static_cast<u32>(offset); 488 tex_info.index = static_cast<u32>(offset);
500 489
@@ -511,6 +500,22 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
511 return tex_info; 500 return tex_info;
512} 501}
513 502
503Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
504 std::size_t offset) const {
505 const auto& shader = state.shader_stages[static_cast<std::size_t>(stage)];
506 const auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index];
507 ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
508
509 const GPUVAddr tex_info_address =
510 tex_info_buffer.address + offset * sizeof(Texture::TextureHandle);
511
512 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
513
514 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
515
516 return GetTextureInfo(tex_handle, offset);
517}
518
514u32 Maxwell3D::GetRegisterValue(u32 method) const { 519u32 Maxwell3D::GetRegisterValue(u32 method) const {
515 ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register"); 520 ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register");
516 return regs.reg_array[method]; 521 return regs.reg_array[method];
@@ -524,4 +529,12 @@ void Maxwell3D::ProcessClearBuffers() {
524 rasterizer.Clear(); 529 rasterizer.Clear();
525} 530}
526 531
532u32 Maxwell3D::AccessConstBuffer32(Regs::ShaderStage stage, u64 const_buffer, u64 offset) const {
533 const auto& shader_stage = state.shader_stages[static_cast<std::size_t>(stage)];
534 const auto& buffer = shader_stage.const_buffers[const_buffer];
535 u32 result;
536 std::memcpy(&result, memory_manager.GetPointer(buffer.address + offset), sizeof(u32));
537 return result;
538}
539
527} // namespace Tegra::Engines 540} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 321af3297..85d309d9b 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -243,9 +243,10 @@ public:
243 return "10_10_10_2"; 243 return "10_10_10_2";
244 case Size::Size_11_11_10: 244 case Size::Size_11_11_10:
245 return "11_11_10"; 245 return "11_11_10";
246 default:
247 UNREACHABLE();
248 return {};
246 } 249 }
247 UNREACHABLE();
248 return {};
249 } 250 }
250 251
251 std::string TypeString() const { 252 std::string TypeString() const {
@@ -1131,12 +1132,18 @@ public:
1131 /// Write the value to the register identified by method. 1132 /// Write the value to the register identified by method.
1132 void CallMethod(const GPU::MethodCall& method_call); 1133 void CallMethod(const GPU::MethodCall& method_call);
1133 1134
1135 /// Given a Texture Handle, returns the TSC and TIC entries.
1136 Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle,
1137 std::size_t offset) const;
1138
1134 /// Returns a list of enabled textures for the specified shader stage. 1139 /// Returns a list of enabled textures for the specified shader stage.
1135 std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; 1140 std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const;
1136 1141
1137 /// Returns the texture information for a specific texture in a specific shader stage. 1142 /// Returns the texture information for a specific texture in a specific shader stage.
1138 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; 1143 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
1139 1144
1145 u32 AccessConstBuffer32(Regs::ShaderStage stage, u64 const_buffer, u64 offset) const;
1146
1140 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than 1147 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
1141 /// we've seen used. 1148 /// we've seen used.
1142 using MacroMemory = std::array<u32, 0x40000>; 1149 using MacroMemory = std::array<u32, 0x40000>;
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 2e1e96c81..e5b4eadea 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -387,6 +387,20 @@ enum class IpaSampleMode : u64 {
387 Offset = 2, 387 Offset = 2,
388}; 388};
389 389
390enum class LmemLoadCacheManagement : u64 {
391 Default = 0,
392 LU = 1,
393 CI = 2,
394 CV = 3,
395};
396
397enum class LmemStoreCacheManagement : u64 {
398 Default = 0,
399 CG = 1,
400 CS = 2,
401 WT = 3,
402};
403
390struct IpaMode { 404struct IpaMode {
391 IpaInterpMode interpolation_mode; 405 IpaInterpMode interpolation_mode;
392 IpaSampleMode sampling_mode; 406 IpaSampleMode sampling_mode;
@@ -782,7 +796,7 @@ union Instruction {
782 } ld_l; 796 } ld_l;
783 797
784 union { 798 union {
785 BitField<44, 2, u64> unknown; 799 BitField<44, 2, LmemStoreCacheManagement> cache_management;
786 } st_l; 800 } st_l;
787 801
788 union { 802 union {
@@ -792,6 +806,12 @@ union Instruction {
792 } ldg; 806 } ldg;
793 807
794 union { 808 union {
809 BitField<48, 3, UniformType> type;
810 BitField<46, 2, u64> cache_mode;
811 BitField<20, 24, s64> immediate_offset;
812 } stg;
813
814 union {
795 BitField<0, 3, u64> pred0; 815 BitField<0, 3, u64> pred0;
796 BitField<3, 3, u64> pred3; 816 BitField<3, 3, u64> pred3;
797 BitField<7, 1, u64> abs_a; 817 BitField<7, 1, u64> abs_a;
@@ -917,21 +937,34 @@ union Instruction {
917 } iset; 937 } iset;
918 938
919 union { 939 union {
920 BitField<8, 2, Register::Size> dest_size; 940 BitField<41, 2, u64> selector; // i2i and i2f only
921 BitField<10, 2, Register::Size> src_size;
922 BitField<12, 1, u64> is_output_signed;
923 BitField<13, 1, u64> is_input_signed;
924 BitField<41, 2, u64> selector;
925 BitField<45, 1, u64> negate_a; 941 BitField<45, 1, u64> negate_a;
926 BitField<49, 1, u64> abs_a; 942 BitField<49, 1, u64> abs_a;
943 BitField<10, 2, Register::Size> src_size;
944 BitField<13, 1, u64> is_input_signed;
945 BitField<8, 2, Register::Size> dst_size;
946 BitField<12, 1, u64> is_output_signed;
947
948 union {
949 BitField<39, 2, u64> tab5cb8_2;
950 } i2f;
927 951
928 union { 952 union {
929 BitField<39, 2, F2iRoundingOp> rounding; 953 BitField<39, 2, F2iRoundingOp> rounding;
930 } f2i; 954 } f2i;
931 955
932 union { 956 union {
933 BitField<39, 4, F2fRoundingOp> rounding; 957 BitField<8, 2, Register::Size> src_size;
958 BitField<10, 2, Register::Size> dst_size;
959 BitField<39, 4, u64> rounding;
960 // H0, H1 extract for F16 missing
961 BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value
962 F2fRoundingOp GetRoundingMode() const {
963 constexpr u64 rounding_mask = 0x0B;
964 return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask);
965 }
934 } f2f; 966 } f2f;
967
935 } conversion; 968 } conversion;
936 969
937 union { 970 union {
@@ -967,6 +1000,38 @@ union Instruction {
967 } tex; 1000 } tex;
968 1001
969 union { 1002 union {
1003 BitField<28, 1, u64> array;
1004 BitField<29, 2, TextureType> texture_type;
1005 BitField<31, 4, u64> component_mask;
1006 BitField<49, 1, u64> nodep_flag;
1007 BitField<50, 1, u64> dc_flag;
1008 BitField<36, 1, u64> aoffi_flag;
1009 BitField<37, 3, TextureProcessMode> process_mode;
1010
1011 bool IsComponentEnabled(std::size_t component) const {
1012 return ((1ULL << component) & component_mask) != 0;
1013 }
1014
1015 TextureProcessMode GetTextureProcessMode() const {
1016 return process_mode;
1017 }
1018
1019 bool UsesMiscMode(TextureMiscMode mode) const {
1020 switch (mode) {
1021 case TextureMiscMode::DC:
1022 return dc_flag != 0;
1023 case TextureMiscMode::NODEP:
1024 return nodep_flag != 0;
1025 case TextureMiscMode::AOFFI:
1026 return aoffi_flag != 0;
1027 default:
1028 break;
1029 }
1030 return false;
1031 }
1032 } tex_b;
1033
1034 union {
970 BitField<22, 6, TextureQueryType> query_type; 1035 BitField<22, 6, TextureQueryType> query_type;
971 BitField<31, 4, u64> component_mask; 1036 BitField<31, 4, u64> component_mask;
972 BitField<49, 1, u64> nodep_flag; 1037 BitField<49, 1, u64> nodep_flag;
@@ -1312,7 +1377,9 @@ public:
1312 LDG, // Load from global memory 1377 LDG, // Load from global memory
1313 STG, // Store in global memory 1378 STG, // Store in global memory
1314 TEX, 1379 TEX,
1380 TEX_B, // Texture Load Bindless
1315 TXQ, // Texture Query 1381 TXQ, // Texture Query
1382 TXQ_B, // Texture Query Bindless
1316 TEXS, // Texture Fetch with scalar/non-vec4 source/destinations 1383 TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
1317 TLDS, // Texture Load with scalar/non-vec4 source/destinations 1384 TLDS, // Texture Load with scalar/non-vec4 source/destinations
1318 TLD4, // Texture Load 4 1385 TLD4, // Texture Load 4
@@ -1580,7 +1647,9 @@ private:
1580 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), 1647 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
1581 INST("1110111011011---", Id::STG, Type::Memory, "STG"), 1648 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
1582 INST("110000----111---", Id::TEX, Type::Texture, "TEX"), 1649 INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
1650 INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
1583 INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"), 1651 INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),
1652 INST("1101111101010---", Id::TXQ_B, Type::Texture, "TXQ_B"),
1584 INST("1101-00---------", Id::TEXS, Type::Texture, "TEXS"), 1653 INST("1101-00---------", Id::TEXS, Type::Texture, "TEXS"),
1585 INST("1101101---------", Id::TLDS, Type::Texture, "TLDS"), 1654 INST("1101101---------", Id::TLDS, Type::Texture, "TLDS"),
1586 INST("110010----111---", Id::TLD4, Type::Texture, "TLD4"), 1655 INST("110010----111---", Id::TLD4, Type::Texture, "TLD4"),
@@ -1678,7 +1747,7 @@ private:
1678 INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"), 1747 INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"),
1679 INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"), 1748 INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
1680 INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"), 1749 INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
1681 INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"), 1750 INST("0011101-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
1682 INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"), 1751 INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"),
1683 INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"), 1752 INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"),
1684 INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"), 1753 INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"),
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index de30ea354..fe6628923 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -207,6 +207,11 @@ public:
207 }; 207 };
208 } regs{}; 208 } regs{};
209 209
210 /// Performs any additional setup necessary in order to begin GPU emulation.
211 /// This can be used to launch any necessary threads and register any necessary
212 /// core timing events.
213 virtual void Start() = 0;
214
210 /// Push GPU command entries to be processed 215 /// Push GPU command entries to be processed
211 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; 216 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
212 217
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index db507cf04..d4e2553a9 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -9,10 +9,14 @@
9namespace VideoCommon { 9namespace VideoCommon {
10 10
11GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) 11GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
12 : Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {} 12 : GPU(system, renderer), gpu_thread{system} {}
13 13
14GPUAsynch::~GPUAsynch() = default; 14GPUAsynch::~GPUAsynch() = default;
15 15
16void GPUAsynch::Start() {
17 gpu_thread.StartThread(renderer, *dma_pusher);
18}
19
16void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { 20void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
17 gpu_thread.SubmitList(std::move(entries)); 21 gpu_thread.SubmitList(std::move(entries));
18} 22}
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 1dcc61a6c..30be74cba 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -13,16 +13,13 @@ class RendererBase;
13 13
14namespace VideoCommon { 14namespace VideoCommon {
15 15
16namespace GPUThread {
17class ThreadManager;
18} // namespace GPUThread
19
20/// Implementation of GPU interface that runs the GPU asynchronously 16/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch : public Tegra::GPU { 17class GPUAsynch : public Tegra::GPU {
22public: 18public:
23 explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer); 19 explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
24 ~GPUAsynch() override; 20 ~GPUAsynch() override;
25 21
22 void Start() override;
26 void PushGPUEntries(Tegra::CommandList&& entries) override; 23 void PushGPUEntries(Tegra::CommandList&& entries) override;
27 void SwapBuffers( 24 void SwapBuffers(
28 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 25 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 2cfc900ed..45e43b1dc 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -8,10 +8,12 @@
8namespace VideoCommon { 8namespace VideoCommon {
9 9
10GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) 10GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
11 : Tegra::GPU(system, renderer) {} 11 : GPU(system, renderer) {}
12 12
13GPUSynch::~GPUSynch() = default; 13GPUSynch::~GPUSynch() = default;
14 14
15void GPUSynch::Start() {}
16
15void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { 17void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
16 dma_pusher->Push(std::move(entries)); 18 dma_pusher->Push(std::move(entries));
17 dma_pusher->DispatchCalls(); 19 dma_pusher->DispatchCalls();
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 766b5631c..3031fcf72 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -18,6 +18,7 @@ public:
18 explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer); 18 explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
19 ~GPUSynch() override; 19 ~GPUSynch() override;
20 20
21 void Start() override;
21 void PushGPUEntries(Tegra::CommandList&& entries) override; 22 void PushGPUEntries(Tegra::CommandList&& entries) override;
22 void SwapBuffers( 23 void SwapBuffers(
23 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 24 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index cc56cf467..c9a2077de 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -55,19 +55,24 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
55 } 55 }
56} 56}
57 57
58ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, 58ThreadManager::ThreadManager(Core::System& system) : system{system} {}
59 Tegra::DmaPusher& dma_pusher)
60 : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
61 synchronization_event = system.CoreTiming().RegisterEvent(
62 "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
63}
64 59
65ThreadManager::~ThreadManager() { 60ThreadManager::~ThreadManager() {
61 if (!thread.joinable()) {
62 return;
63 }
64
66 // Notify GPU thread that a shutdown is pending 65 // Notify GPU thread that a shutdown is pending
67 PushCommand(EndProcessingCommand()); 66 PushCommand(EndProcessingCommand());
68 thread.join(); 67 thread.join();
69} 68}
70 69
70void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
71 thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
72 synchronization_event = system.CoreTiming().RegisterEvent(
73 "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
74}
75
71void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 76void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
72 const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))}; 77 const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
73 const s64 synchronization_ticks{Core::Timing::usToCycles(9000)}; 78 const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 62bcea5bb..cc14527c7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -138,10 +138,12 @@ struct SynchState final {
138/// Class used to manage the GPU thread 138/// Class used to manage the GPU thread
139class ThreadManager final { 139class ThreadManager final {
140public: 140public:
141 explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, 141 explicit ThreadManager(Core::System& system);
142 Tegra::DmaPusher& dma_pusher);
143 ~ThreadManager(); 142 ~ThreadManager();
144 143
144 /// Creates and starts the GPU thread.
145 void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
146
145 /// Push GPU command entries to be processed 147 /// Push GPU command entries to be processed
146 void SubmitList(Tegra::CommandList&& entries); 148 void SubmitList(Tegra::CommandList&& entries);
147 149
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 0f4e820aa..6c98c6701 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -199,7 +199,15 @@ const u8* MemoryManager::GetPointer(GPUVAddr addr) const {
199 return {}; 199 return {};
200} 200}
201 201
202void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const { 202bool MemoryManager::IsBlockContinous(const GPUVAddr start, const std::size_t size) {
203 const GPUVAddr end = start + size;
204 const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start));
205 const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end));
206 const std::size_t range = static_cast<std::size_t>(host_ptr_end - host_ptr_start);
207 return range == size;
208}
209
210void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const {
203 std::size_t remaining_size{size}; 211 std::size_t remaining_size{size};
204 std::size_t page_index{src_addr >> page_bits}; 212 std::size_t page_index{src_addr >> page_bits};
205 std::size_t page_offset{src_addr & page_mask}; 213 std::size_t page_offset{src_addr & page_mask};
@@ -226,7 +234,30 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t
226 } 234 }
227} 235}
228 236
229void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { 237void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer,
238 const std::size_t size) const {
239 std::size_t remaining_size{size};
240 std::size_t page_index{src_addr >> page_bits};
241 std::size_t page_offset{src_addr & page_mask};
242
243 while (remaining_size > 0) {
244 const std::size_t copy_amount{
245 std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
246 const u8* page_pointer = page_table.pointers[page_index];
247 if (page_pointer) {
248 const u8* src_ptr{page_pointer + page_offset};
249 std::memcpy(dest_buffer, src_ptr, copy_amount);
250 } else {
251 std::memset(dest_buffer, 0, copy_amount);
252 }
253 page_index++;
254 page_offset = 0;
255 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
256 remaining_size -= copy_amount;
257 }
258}
259
260void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size) {
230 std::size_t remaining_size{size}; 261 std::size_t remaining_size{size};
231 std::size_t page_index{dest_addr >> page_bits}; 262 std::size_t page_index{dest_addr >> page_bits};
232 std::size_t page_offset{dest_addr & page_mask}; 263 std::size_t page_offset{dest_addr & page_mask};
@@ -253,7 +284,28 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::
253 } 284 }
254} 285}
255 286
256void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { 287void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer,
288 const std::size_t size) {
289 std::size_t remaining_size{size};
290 std::size_t page_index{dest_addr >> page_bits};
291 std::size_t page_offset{dest_addr & page_mask};
292
293 while (remaining_size > 0) {
294 const std::size_t copy_amount{
295 std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
296 u8* page_pointer = page_table.pointers[page_index];
297 if (page_pointer) {
298 u8* dest_ptr{page_pointer + page_offset};
299 std::memcpy(dest_ptr, src_buffer, copy_amount);
300 }
301 page_index++;
302 page_offset = 0;
303 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
304 remaining_size -= copy_amount;
305 }
306}
307
308void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) {
257 std::size_t remaining_size{size}; 309 std::size_t remaining_size{size};
258 std::size_t page_index{src_addr >> page_bits}; 310 std::size_t page_index{src_addr >> page_bits};
259 std::size_t page_offset{src_addr & page_mask}; 311 std::size_t page_offset{src_addr & page_mask};
@@ -281,6 +333,12 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t
281 } 333 }
282} 334}
283 335
336void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) {
337 std::vector<u8> tmp_buffer(size);
338 ReadBlockUnsafe(src_addr, tmp_buffer.data(), size);
339 WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size);
340}
341
284void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, 342void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
285 VAddr backing_addr) { 343 VAddr backing_addr) {
286 LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size, 344 LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size,
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 647cbf93a..e4f0c4bd6 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -65,9 +65,32 @@ public:
65 u8* GetPointer(GPUVAddr addr); 65 u8* GetPointer(GPUVAddr addr);
66 const u8* GetPointer(GPUVAddr addr) const; 66 const u8* GetPointer(GPUVAddr addr) const;
67 67
68 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; 68 // Returns true if the block is continous in host memory, false otherwise
69 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); 69 bool IsBlockContinous(const GPUVAddr start, const std::size_t size);
70 void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); 70
71 /**
72 * ReadBlock and WriteBlock are full read and write operations over virtual
73 * GPU Memory. It's important to use these when GPU memory may not be continous
74 * in the Host Memory counterpart. Note: This functions cause Host GPU Memory
75 * Flushes and Invalidations, respectively to each operation.
76 */
77 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const;
78 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size);
79 void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size);
80
81 /**
82 * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
83 * WriteBlock respectively. In this versions, no flushing or invalidation is actually
84 * done and their performance is similar to a memcpy. This functions can be used
85 * on either of this 2 scenarios instead of their safe counterpart:
86 * - Memory which is sure to never be represented in the Host GPU.
87 * - Memory Managed by a Cache Manager. Example: Texture Flushing should use
88 * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture
89 * being flushed.
90 */
91 void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const;
92 void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size);
93 void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size);
71 94
72private: 95private:
73 using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; 96 using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>;
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
new file mode 100644
index 000000000..b6d9e0ddb
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -0,0 +1,45 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstddef>
6#include <glad/glad.h>
7
8#include "common/logging/log.h"
9#include "video_core/renderer_opengl/gl_device.h"
10
11namespace OpenGL {
12
13namespace {
14template <typename T>
15T GetInteger(GLenum pname) {
16 GLint temporary;
17 glGetIntegerv(pname, &temporary);
18 return static_cast<T>(temporary);
19}
20} // Anonymous namespace
21
22Device::Device() {
23 uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
24 has_variable_aoffi = TestVariableAoffi();
25}
26
27bool Device::TestVariableAoffi() {
28 const GLchar* AOFFI_TEST = R"(#version 430 core
29uniform sampler2D tex;
30uniform ivec2 variable_offset;
31void main() {
32 gl_Position = textureOffset(tex, vec2(0), variable_offset);
33}
34)";
35 const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &AOFFI_TEST)};
36 GLint link_status{};
37 glGetProgramiv(shader, GL_LINK_STATUS, &link_status);
38 glDeleteProgram(shader);
39
40 const bool supported{link_status == GL_TRUE};
41 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", supported);
42 return supported;
43}
44
45} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
new file mode 100644
index 000000000..78ff5ee58
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -0,0 +1,30 @@
1// Copyright 2019 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 <cstddef>
8
9namespace OpenGL {
10
11class Device {
12public:
13 Device();
14
15 std::size_t GetUniformBufferAlignment() const {
16 return uniform_buffer_alignment;
17 }
18
19 bool HasVariableAoffi() const {
20 return has_variable_aoffi;
21 }
22
23private:
24 static bool TestVariableAoffi();
25
26 std::size_t uniform_buffer_alignment{};
27 bool has_variable_aoffi{};
28};
29
30} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index 8d9ee81f1..ea4a593af 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -14,28 +14,28 @@
14 14
15namespace OpenGL { 15namespace OpenGL {
16 16
17CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr) 17CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size)
18 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size} { 18 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, host_ptr{host_ptr}, size{size},
19 max_size{max_size} {
19 buffer.Create(); 20 buffer.Create();
20 // Bind and unbind the buffer so it gets allocated by the driver
21 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
22 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
23 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory"); 21 LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory");
24} 22}
25 23
26void CachedGlobalRegion::Reload(u32 size_) { 24CachedGlobalRegion::~CachedGlobalRegion() = default;
27 constexpr auto max_size = static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize);
28 25
26void CachedGlobalRegion::Reload(u32 size_) {
29 size = size_; 27 size = size_;
30 if (size > max_size) { 28 if (size > max_size) {
31 size = max_size; 29 size = max_size;
32 LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the expected size {}!", size_, 30 LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the supported size {}!", size_,
33 max_size); 31 max_size);
34 } 32 }
33 glNamedBufferData(buffer.handle, size, host_ptr, GL_STREAM_DRAW);
34}
35 35
36 // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer 36void CachedGlobalRegion::Flush() {
37 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); 37 LOG_DEBUG(Render_OpenGL, "Flushing {} bytes to CPU memory address 0x{:16}", size, cpu_addr);
38 glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW); 38 glGetNamedBufferSubData(buffer.handle, 0, static_cast<GLsizeiptr>(size), host_ptr);
39} 39}
40 40
41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const { 41GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const {
@@ -46,14 +46,16 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr,
46 return search->second; 46 return search->second;
47} 47}
48 48
49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u32 size, 49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr,
50 u8* host_ptr) { 50 u32 size) {
51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; 51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)};
52 if (!region) { 52 if (!region) {
53 // No reserved surface available, create a new one and reserve it 53 // No reserved surface available, create a new one and reserve it
54 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 54 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
55 const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr); 55 const auto cpu_addr{memory_manager.GpuToCpuAddress(addr)};
56 region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr); 56 ASSERT(cpu_addr);
57
58 region = std::make_shared<CachedGlobalRegion>(*cpu_addr, host_ptr, size, max_ssbo_size);
57 ReserveGlobalRegion(region); 59 ReserveGlobalRegion(region);
58 } 60 }
59 region->Reload(size); 61 region->Reload(size);
@@ -65,7 +67,11 @@ void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
65} 67}
66 68
67GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) 69GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
68 : RasterizerCache{rasterizer} {} 70 : RasterizerCache{rasterizer} {
71 GLint max_ssbo_size_;
72 glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size_);
73 max_ssbo_size = static_cast<u32>(max_ssbo_size_);
74}
69 75
70GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( 76GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
71 const GLShader::GlobalMemoryEntry& global_region, 77 const GLShader::GlobalMemoryEntry& global_region,
@@ -73,7 +79,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
73 79
74 auto& gpu{Core::System::GetInstance().GPU()}; 80 auto& gpu{Core::System::GetInstance().GPU()};
75 auto& memory_manager{gpu.MemoryManager()}; 81 auto& memory_manager{gpu.MemoryManager()};
76 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]}; 82 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<std::size_t>(stage)]};
77 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + 83 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address +
78 global_region.GetCbufOffset()}; 84 global_region.GetCbufOffset()};
79 const auto actual_addr{memory_manager.Read<u64>(addr)}; 85 const auto actual_addr{memory_manager.Read<u64>(addr)};
@@ -85,7 +91,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
85 91
86 if (!region) { 92 if (!region) {
87 // No global region found - create a new one 93 // No global region found - create a new one
88 region = GetUncachedGlobalRegion(actual_addr, size, host_ptr); 94 region = GetUncachedGlobalRegion(actual_addr, host_ptr, size);
89 Register(region); 95 Register(region);
90 } 96 }
91 97
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h
index 5a21ab66f..196e6e278 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.h
+++ b/src/video_core/renderer_opengl/gl_global_cache.h
@@ -19,7 +19,7 @@ namespace OpenGL {
19 19
20namespace GLShader { 20namespace GLShader {
21class GlobalMemoryEntry; 21class GlobalMemoryEntry;
22} // namespace GLShader 22}
23 23
24class RasterizerOpenGL; 24class RasterizerOpenGL;
25class CachedGlobalRegion; 25class CachedGlobalRegion;
@@ -27,7 +27,8 @@ using GlobalRegion = std::shared_ptr<CachedGlobalRegion>;
27 27
28class CachedGlobalRegion final : public RasterizerCacheObject { 28class CachedGlobalRegion final : public RasterizerCacheObject {
29public: 29public:
30 explicit CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr); 30 explicit CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size);
31 ~CachedGlobalRegion();
31 32
32 VAddr GetCpuAddr() const override { 33 VAddr GetCpuAddr() const override {
33 return cpu_addr; 34 return cpu_addr;
@@ -45,14 +46,14 @@ public:
45 /// Reloads the global region from guest memory 46 /// Reloads the global region from guest memory
46 void Reload(u32 size_); 47 void Reload(u32 size_);
47 48
48 // TODO(Rodrigo): When global memory is written (STG), implement flushing 49 void Flush() override;
49 void Flush() override {
50 UNIMPLEMENTED();
51 }
52 50
53private: 51private:
54 VAddr cpu_addr{}; 52 VAddr cpu_addr{};
53 u8* host_ptr{};
55 u32 size{}; 54 u32 size{};
55 u32 max_size{};
56
56 OGLBuffer buffer; 57 OGLBuffer buffer;
57}; 58};
58 59
@@ -66,10 +67,11 @@ public:
66 67
67private: 68private:
68 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; 69 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const;
69 GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u32 size, u8* host_ptr); 70 GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, u32 size);
70 void ReserveGlobalRegion(GlobalRegion region); 71 void ReserveGlobalRegion(GlobalRegion region);
71 72
72 std::unordered_map<CacheAddr, GlobalRegion> reserve; 73 std::unordered_map<CacheAddr, GlobalRegion> reserve;
74 u32 max_ssbo_size{};
73}; 75};
74 76
75} // namespace OpenGL 77} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d250d5cbb..db73e746c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -99,22 +99,14 @@ struct FramebufferCacheKey {
99}; 99};
100 100
101RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) 101RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info)
102 : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, system{system}, 102 : res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system},
103 screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { 103 screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) {
104 // Create sampler objects
105 for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
106 texture_samplers[i].Create();
107 state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
108 }
109
110 OpenGLState::ApplyDefaultState(); 104 OpenGLState::ApplyDefaultState();
111 105
112 shader_program_manager = std::make_unique<GLShader::ProgramManager>(); 106 shader_program_manager = std::make_unique<GLShader::ProgramManager>();
113 state.draw.shader_program = 0; 107 state.draw.shader_program = 0;
114 state.Apply(); 108 state.Apply();
115 109
116 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
117
118 LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here"); 110 LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here");
119 CheckExtensions(); 111 CheckExtensions();
120} 112}
@@ -313,6 +305,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
313 case Maxwell::ShaderProgram::Geometry: 305 case Maxwell::ShaderProgram::Geometry:
314 shader_program_manager->UseTrivialGeometryShader(); 306 shader_program_manager->UseTrivialGeometryShader();
315 break; 307 break;
308 default:
309 break;
316 } 310 }
317 continue; 311 continue;
318 } 312 }
@@ -321,8 +315,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
321 315
322 GLShader::MaxwellUniformData ubo{}; 316 GLShader::MaxwellUniformData ubo{};
323 ubo.SetFromRegs(gpu, stage); 317 ubo.SetFromRegs(gpu, stage);
324 const GLintptr offset = buffer_cache.UploadHostMemory( 318 const GLintptr offset =
325 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 319 buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
326 320
327 // Bind the emulation info buffer 321 // Bind the emulation info buffer
328 bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, 322 bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset,
@@ -582,9 +576,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
582} 576}
583 577
584void RasterizerOpenGL::Clear() { 578void RasterizerOpenGL::Clear() {
585 const auto prev_state{state};
586 SCOPE_EXIT({ prev_state.Apply(); });
587
588 const auto& regs = system.GPU().Maxwell3D().regs; 579 const auto& regs = system.GPU().Maxwell3D().regs;
589 bool use_color{}; 580 bool use_color{};
590 bool use_depth{}; 581 bool use_depth{};
@@ -656,7 +647,10 @@ void RasterizerOpenGL::Clear() {
656 clear_state.EmulateViewportWithScissor(); 647 clear_state.EmulateViewportWithScissor();
657 } 648 }
658 649
659 clear_state.Apply(); 650 clear_state.ApplyColorMask();
651 clear_state.ApplyDepth();
652 clear_state.ApplyStencilTest();
653 clear_state.ApplyViewport();
660 654
661 if (use_color) { 655 if (use_color) {
662 glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); 656 glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color);
@@ -706,23 +700,24 @@ void RasterizerOpenGL::DrawArrays() {
706 // Add space for index buffer (keeping in mind non-core primitives) 700 // Add space for index buffer (keeping in mind non-core primitives)
707 switch (regs.draw.topology) { 701 switch (regs.draw.topology) {
708 case Maxwell::PrimitiveTopology::Quads: 702 case Maxwell::PrimitiveTopology::Quads:
709 buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + 703 buffer_size = Common::AlignUp(buffer_size, 4) +
710 primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count); 704 primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count);
711 break; 705 break;
712 default: 706 default:
713 if (is_indexed) { 707 if (is_indexed) {
714 buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + CalculateIndexBufferSize(); 708 buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize();
715 } 709 }
716 break; 710 break;
717 } 711 }
718 712
719 // Uniform space for the 5 shader stages 713 // Uniform space for the 5 shader stages
720 buffer_size = 714 buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) +
721 Common::AlignUp<std::size_t>(buffer_size, 4) + 715 (sizeof(GLShader::MaxwellUniformData) + device.GetUniformBufferAlignment()) *
722 (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; 716 Maxwell::MaxShaderStage;
723 717
724 // Add space for at least 18 constant buffers 718 // Add space for at least 18 constant buffers
725 buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); 719 buffer_size +=
720 Maxwell::MaxConstBuffers * (MaxConstbufferSize + device.GetUniformBufferAlignment());
726 721
727 const bool invalidate = buffer_cache.Map(buffer_size); 722 const bool invalidate = buffer_cache.Map(buffer_size);
728 if (invalidate) { 723 if (invalidate) {
@@ -756,6 +751,7 @@ void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) {
756 return; 751 return;
757 } 752 }
758 res_cache.FlushRegion(addr, size); 753 res_cache.FlushRegion(addr, size);
754 global_cache.FlushRegion(addr, size);
759} 755}
760 756
761void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { 757void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
@@ -812,92 +808,6 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
812 return true; 808 return true;
813} 809}
814 810
815void RasterizerOpenGL::SamplerInfo::Create() {
816 sampler.Create();
817 mag_filter = Tegra::Texture::TextureFilter::Linear;
818 min_filter = Tegra::Texture::TextureFilter::Linear;
819 wrap_u = Tegra::Texture::WrapMode::Wrap;
820 wrap_v = Tegra::Texture::WrapMode::Wrap;
821 wrap_p = Tegra::Texture::WrapMode::Wrap;
822 use_depth_compare = false;
823 depth_compare_func = Tegra::Texture::DepthCompareFunc::Never;
824
825 // OpenGL's default is GL_LINEAR_MIPMAP_LINEAR
826 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
827 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
828
829 // Other attributes have correct defaults
830}
831
832void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
833 const GLuint sampler_id = sampler.handle;
834 if (mag_filter != config.mag_filter) {
835 mag_filter = config.mag_filter;
836 glSamplerParameteri(
837 sampler_id, GL_TEXTURE_MAG_FILTER,
838 MaxwellToGL::TextureFilterMode(mag_filter, Tegra::Texture::TextureMipmapFilter::None));
839 }
840 if (min_filter != config.min_filter || mipmap_filter != config.mipmap_filter) {
841 min_filter = config.min_filter;
842 mipmap_filter = config.mipmap_filter;
843 glSamplerParameteri(sampler_id, GL_TEXTURE_MIN_FILTER,
844 MaxwellToGL::TextureFilterMode(min_filter, mipmap_filter));
845 }
846
847 if (wrap_u != config.wrap_u) {
848 wrap_u = config.wrap_u;
849 glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(wrap_u));
850 }
851 if (wrap_v != config.wrap_v) {
852 wrap_v = config.wrap_v;
853 glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
854 }
855 if (wrap_p != config.wrap_p) {
856 wrap_p = config.wrap_p;
857 glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
858 }
859
860 if (const bool enabled = config.depth_compare_enabled == 1; use_depth_compare != enabled) {
861 use_depth_compare = enabled;
862 glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_MODE,
863 use_depth_compare ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE);
864 }
865
866 if (depth_compare_func != config.depth_compare_func) {
867 depth_compare_func = config.depth_compare_func;
868 glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_FUNC,
869 MaxwellToGL::DepthCompareFunc(depth_compare_func));
870 }
871
872 if (const auto new_border_color = config.GetBorderColor(); border_color != new_border_color) {
873 border_color = new_border_color;
874 glSamplerParameterfv(sampler_id, GL_TEXTURE_BORDER_COLOR, border_color.data());
875 }
876
877 if (const float anisotropic = config.GetMaxAnisotropy(); max_anisotropic != anisotropic) {
878 max_anisotropic = anisotropic;
879 if (GLAD_GL_ARB_texture_filter_anisotropic) {
880 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
881 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
882 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
883 }
884 }
885
886 if (const float min = config.GetMinLod(); min_lod != min) {
887 min_lod = min;
888 glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, min_lod);
889 }
890 if (const float max = config.GetMaxLod(); max_lod != max) {
891 max_lod = max;
892 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, max_lod);
893 }
894
895 if (const float bias = config.GetLodBias(); lod_bias != bias) {
896 lod_bias = bias;
897 glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, lod_bias);
898 }
899}
900
901void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, 811void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
902 const Shader& shader, GLuint program_handle, 812 const Shader& shader, GLuint program_handle,
903 BaseBindings base_bindings) { 813 BaseBindings base_bindings) {
@@ -939,8 +849,8 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
939 size = Common::AlignUp(size, sizeof(GLvec4)); 849 size = Common::AlignUp(size, sizeof(GLvec4));
940 ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); 850 ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
941 851
942 const GLintptr const_buffer_offset = buffer_cache.UploadMemory( 852 const GLintptr const_buffer_offset =
943 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); 853 buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment());
944 854
945 bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size); 855 bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size);
946 } 856 }
@@ -953,6 +863,9 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade
953 for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { 863 for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
954 const auto& entry{entries[bindpoint]}; 864 const auto& entry{entries[bindpoint]};
955 const auto& region{global_cache.GetGlobalRegion(entry, stage)}; 865 const auto& region{global_cache.GetGlobalRegion(entry, stage)};
866 if (entry.IsWritten()) {
867 region->MarkAsModified(true, global_cache);
868 }
956 bind_ssbo_pushbuffer.Push(region->GetBufferHandle(), 0, 869 bind_ssbo_pushbuffer.Push(region->GetBufferHandle(), 0,
957 static_cast<GLsizeiptr>(region->GetSizeInBytes())); 870 static_cast<GLsizeiptr>(region->GetSizeInBytes()));
958 } 871 }
@@ -970,10 +883,18 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s
970 883
971 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { 884 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
972 const auto& entry = entries[bindpoint]; 885 const auto& entry = entries[bindpoint];
973 const auto texture = maxwell3d.GetStageTexture(stage, entry.GetOffset()); 886 Tegra::Texture::FullTextureInfo texture;
887 if (entry.IsBindless()) {
888 const auto cbuf = entry.GetBindlessCBuf();
889 Tegra::Texture::TextureHandle tex_handle;
890 tex_handle.raw = maxwell3d.AccessConstBuffer32(stage, cbuf.first, cbuf.second);
891 texture = maxwell3d.GetTextureInfo(tex_handle, entry.GetOffset());
892 } else {
893 texture = maxwell3d.GetStageTexture(stage, entry.GetOffset());
894 }
974 const u32 current_bindpoint = base_bindings.sampler + bindpoint; 895 const u32 current_bindpoint = base_bindings.sampler + bindpoint;
975 896
976 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); 897 state.texture_units[current_bindpoint].sampler = sampler_cache.GetSampler(texture.tsc);
977 898
978 if (Surface surface = res_cache.GetTextureSurface(texture, entry); surface) { 899 if (Surface surface = res_cache.GetTextureSurface(texture, entry); surface) {
979 state.texture_units[current_bindpoint].texture = 900 state.texture_units[current_bindpoint].texture =
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index e4c64ae71..71b9c5ead 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -21,10 +21,12 @@
21#include "video_core/rasterizer_cache.h" 21#include "video_core/rasterizer_cache.h"
22#include "video_core/rasterizer_interface.h" 22#include "video_core/rasterizer_interface.h"
23#include "video_core/renderer_opengl/gl_buffer_cache.h" 23#include "video_core/renderer_opengl/gl_buffer_cache.h"
24#include "video_core/renderer_opengl/gl_device.h"
24#include "video_core/renderer_opengl/gl_global_cache.h" 25#include "video_core/renderer_opengl/gl_global_cache.h"
25#include "video_core/renderer_opengl/gl_primitive_assembler.h" 26#include "video_core/renderer_opengl/gl_primitive_assembler.h"
26#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 27#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
27#include "video_core/renderer_opengl/gl_resource_manager.h" 28#include "video_core/renderer_opengl/gl_resource_manager.h"
29#include "video_core/renderer_opengl/gl_sampler_cache.h"
28#include "video_core/renderer_opengl/gl_shader_cache.h" 30#include "video_core/renderer_opengl/gl_shader_cache.h"
29#include "video_core/renderer_opengl/gl_shader_manager.h" 31#include "video_core/renderer_opengl/gl_shader_manager.h"
30#include "video_core/renderer_opengl/gl_state.h" 32#include "video_core/renderer_opengl/gl_state.h"
@@ -71,39 +73,7 @@ public:
71 static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, 73 static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
72 "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); 74 "The maximum size of a constbuffer must be a multiple of the size of GLvec4");
73 75
74 static constexpr std::size_t MaxGlobalMemorySize = 0x10000;
75 static_assert(MaxGlobalMemorySize % sizeof(float) == 0,
76 "The maximum size of a global memory must be a multiple of the size of float");
77
78private: 76private:
79 class SamplerInfo {
80 public:
81 OGLSampler sampler;
82
83 /// Creates the sampler object, initializing its state so that it's in sync with the
84 /// SamplerInfo struct.
85 void Create();
86 /// Syncs the sampler object with the config, updating any necessary state.
87 void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
88
89 private:
90 Tegra::Texture::TextureFilter mag_filter = Tegra::Texture::TextureFilter::Nearest;
91 Tegra::Texture::TextureFilter min_filter = Tegra::Texture::TextureFilter::Nearest;
92 Tegra::Texture::TextureMipmapFilter mipmap_filter =
93 Tegra::Texture::TextureMipmapFilter::None;
94 Tegra::Texture::WrapMode wrap_u = Tegra::Texture::WrapMode::ClampToEdge;
95 Tegra::Texture::WrapMode wrap_v = Tegra::Texture::WrapMode::ClampToEdge;
96 Tegra::Texture::WrapMode wrap_p = Tegra::Texture::WrapMode::ClampToEdge;
97 bool use_depth_compare = false;
98 Tegra::Texture::DepthCompareFunc depth_compare_func =
99 Tegra::Texture::DepthCompareFunc::Always;
100 GLvec4 border_color = {};
101 float min_lod = 0.0f;
102 float max_lod = 16.0f;
103 float lod_bias = 0.0f;
104 float max_anisotropic = 1.0f;
105 };
106
107 struct FramebufferConfigState { 77 struct FramebufferConfigState {
108 bool using_color_fb{}; 78 bool using_color_fb{};
109 bool using_depth_fb{}; 79 bool using_depth_fb{};
@@ -203,14 +173,15 @@ private:
203 /// but are needed for correct emulation 173 /// but are needed for correct emulation
204 void CheckExtensions(); 174 void CheckExtensions();
205 175
176 const Device device;
206 OpenGLState state; 177 OpenGLState state;
207 178
208 RasterizerCacheOpenGL res_cache; 179 RasterizerCacheOpenGL res_cache;
209 ShaderCacheOpenGL shader_cache; 180 ShaderCacheOpenGL shader_cache;
210 GlobalRegionCacheOpenGL global_cache; 181 GlobalRegionCacheOpenGL global_cache;
182 SamplerCacheOpenGL sampler_cache;
211 183
212 Core::System& system; 184 Core::System& system;
213
214 ScreenInfo& screen_info; 185 ScreenInfo& screen_info;
215 186
216 std::unique_ptr<GLShader::ProgramManager> shader_program_manager; 187 std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
@@ -223,12 +194,9 @@ private:
223 FramebufferConfigState current_framebuffer_config_state; 194 FramebufferConfigState current_framebuffer_config_state;
224 std::pair<bool, bool> current_depth_stencil_usage{}; 195 std::pair<bool, bool> current_depth_stencil_usage{};
225 196
226 std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
227
228 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 197 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
229 OGLBufferCache buffer_cache; 198 OGLBufferCache buffer_cache;
230 PrimitiveAssembler primitive_assembler{buffer_cache}; 199 PrimitiveAssembler primitive_assembler{buffer_cache};
231 GLint uniform_buffer_alignment;
232 200
233 BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; 201 BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
234 BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; 202 BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index f2ffc4710..5a25f5b37 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -281,10 +281,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
281 params.component_type = ComponentTypeFromRenderTarget(config.format); 281 params.component_type = ComponentTypeFromRenderTarget(config.format);
282 params.type = GetFormatType(params.pixel_format); 282 params.type = GetFormatType(params.pixel_format);
283 params.width = config.width; 283 params.width = config.width;
284 if (!params.is_tiled) { 284 params.pitch = config.pitch;
285 const u32 bpp = params.GetFormatBpp() / 8;
286 params.pitch = config.width * bpp;
287 }
288 params.height = config.height; 285 params.height = config.height;
289 params.unaligned_height = config.height; 286 params.unaligned_height = config.height;
290 params.target = SurfaceTarget::Texture2D; 287 params.target = SurfaceTarget::Texture2D;
@@ -643,13 +640,16 @@ void CachedSurface::LoadGLBuffer() {
643 SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i); 640 SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i);
644 } else { 641 } else {
645 const u32 bpp = params.GetFormatBpp() / 8; 642 const u32 bpp = params.GetFormatBpp() / 8;
646 const u32 copy_size = params.width * bpp; 643 const u32 copy_size = (params.width * bpp + GetDefaultBlockWidth(params.pixel_format) - 1) /
644 GetDefaultBlockWidth(params.pixel_format);
647 if (params.pitch == copy_size) { 645 if (params.pitch == copy_size) {
648 std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl); 646 std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl);
649 } else { 647 } else {
648 const u32 height = (params.height + GetDefaultBlockHeight(params.pixel_format) - 1) /
649 GetDefaultBlockHeight(params.pixel_format);
650 const u8* start{params.host_ptr}; 650 const u8* start{params.host_ptr};
651 u8* write_to = gl_buffer[0].data(); 651 u8* write_to = gl_buffer[0].data();
652 for (u32 h = params.height; h > 0; h--) { 652 for (u32 h = height; h > 0; h--) {
653 std::memcpy(write_to, start, copy_size); 653 std::memcpy(write_to, start, copy_size);
654 start += params.pitch; 654 start += params.pitch;
655 write_to += copy_size; 655 write_to += copy_size;
diff --git a/src/video_core/renderer_opengl/gl_sampler_cache.cpp b/src/video_core/renderer_opengl/gl_sampler_cache.cpp
new file mode 100644
index 000000000..3ded5ecea
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_sampler_cache.cpp
@@ -0,0 +1,52 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "video_core/renderer_opengl/gl_resource_manager.h"
7#include "video_core/renderer_opengl/gl_sampler_cache.h"
8#include "video_core/renderer_opengl/maxwell_to_gl.h"
9
10namespace OpenGL {
11
12SamplerCacheOpenGL::SamplerCacheOpenGL() = default;
13
14SamplerCacheOpenGL::~SamplerCacheOpenGL() = default;
15
16OGLSampler SamplerCacheOpenGL::CreateSampler(const Tegra::Texture::TSCEntry& tsc) const {
17 OGLSampler sampler;
18 sampler.Create();
19
20 const GLuint sampler_id{sampler.handle};
21 glSamplerParameteri(
22 sampler_id, GL_TEXTURE_MAG_FILTER,
23 MaxwellToGL::TextureFilterMode(tsc.mag_filter, Tegra::Texture::TextureMipmapFilter::None));
24 glSamplerParameteri(sampler_id, GL_TEXTURE_MIN_FILTER,
25 MaxwellToGL::TextureFilterMode(tsc.min_filter, tsc.mipmap_filter));
26 glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(tsc.wrap_u));
27 glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(tsc.wrap_v));
28 glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(tsc.wrap_p));
29 glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_MODE,
30 tsc.depth_compare_enabled == 1 ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE);
31 glSamplerParameteri(sampler_id, GL_TEXTURE_COMPARE_FUNC,
32 MaxwellToGL::DepthCompareFunc(tsc.depth_compare_func));
33 glSamplerParameterfv(sampler_id, GL_TEXTURE_BORDER_COLOR, tsc.GetBorderColor().data());
34 glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, tsc.GetMinLod());
35 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, tsc.GetMaxLod());
36 glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, tsc.GetLodBias());
37 if (GLAD_GL_ARB_texture_filter_anisotropic) {
38 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY, tsc.GetMaxAnisotropy());
39 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
40 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, tsc.GetMaxAnisotropy());
41 } else if (tsc.GetMaxAnisotropy() != 1) {
42 LOG_WARNING(Render_OpenGL, "Anisotropy not supported by host GPU driver");
43 }
44
45 return sampler;
46}
47
48GLuint SamplerCacheOpenGL::ToSamplerType(const OGLSampler& sampler) const {
49 return sampler.handle;
50}
51
52} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_sampler_cache.h b/src/video_core/renderer_opengl/gl_sampler_cache.h
new file mode 100644
index 000000000..defbc2d81
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_sampler_cache.h
@@ -0,0 +1,25 @@
1// Copyright 2019 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 <glad/glad.h>
8
9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/sampler_cache.h"
11
12namespace OpenGL {
13
14class SamplerCacheOpenGL final : public VideoCommon::SamplerCache<GLuint, OGLSampler> {
15public:
16 explicit SamplerCacheOpenGL();
17 ~SamplerCacheOpenGL();
18
19protected:
20 OGLSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc) const;
21
22 GLuint ToSamplerType(const OGLSampler& sampler) const;
23};
24
25} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 99f67494c..b1c8f7c35 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -38,13 +38,15 @@ GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
38} 38}
39 39
40/// Gets the shader program code from memory for the specified address 40/// Gets the shader program code from memory for the specified address
41ProgramCode GetShaderCode(const u8* host_ptr) { 41ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, const GPUVAddr gpu_addr,
42 const u8* host_ptr) {
42 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); 43 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
43 ASSERT_OR_EXECUTE(host_ptr != nullptr, { 44 ASSERT_OR_EXECUTE(host_ptr != nullptr, {
44 std::fill(program_code.begin(), program_code.end(), 0); 45 std::fill(program_code.begin(), program_code.end(), 0);
45 return program_code; 46 return program_code;
46 }); 47 });
47 std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64)); 48 memory_manager.ReadBlockUnsafe(gpu_addr, program_code.data(),
49 program_code.size() * sizeof(u64));
48 return program_code; 50 return program_code;
49} 51}
50 52
@@ -134,8 +136,8 @@ u64 GetUniqueIdentifier(Maxwell::ShaderProgram program_type, const ProgramCode&
134} 136}
135 137
136/// Creates an unspecialized program from code streams 138/// Creates an unspecialized program from code streams
137GLShader::ProgramResult CreateProgram(Maxwell::ShaderProgram program_type, ProgramCode program_code, 139GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgram program_type,
138 ProgramCode program_code_b) { 140 ProgramCode program_code, ProgramCode program_code_b) {
139 GLShader::ShaderSetup setup(program_code); 141 GLShader::ShaderSetup setup(program_code);
140 if (program_type == Maxwell::ShaderProgram::VertexA) { 142 if (program_type == Maxwell::ShaderProgram::VertexA) {
141 // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders. 143 // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders.
@@ -149,11 +151,11 @@ GLShader::ProgramResult CreateProgram(Maxwell::ShaderProgram program_type, Progr
149 switch (program_type) { 151 switch (program_type) {
150 case Maxwell::ShaderProgram::VertexA: 152 case Maxwell::ShaderProgram::VertexA:
151 case Maxwell::ShaderProgram::VertexB: 153 case Maxwell::ShaderProgram::VertexB:
152 return GLShader::GenerateVertexShader(setup); 154 return GLShader::GenerateVertexShader(device, setup);
153 case Maxwell::ShaderProgram::Geometry: 155 case Maxwell::ShaderProgram::Geometry:
154 return GLShader::GenerateGeometryShader(setup); 156 return GLShader::GenerateGeometryShader(device, setup);
155 case Maxwell::ShaderProgram::Fragment: 157 case Maxwell::ShaderProgram::Fragment:
156 return GLShader::GenerateFragmentShader(setup); 158 return GLShader::GenerateFragmentShader(device, setup);
157 default: 159 default:
158 LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type)); 160 LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type));
159 UNREACHABLE(); 161 UNREACHABLE();
@@ -212,22 +214,20 @@ std::set<GLenum> GetSupportedFormats() {
212 return supported_formats; 214 return supported_formats;
213} 215}
214 216
215} // namespace 217} // Anonymous namespace
216 218
217CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, 219CachedShader::CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier,
218 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, 220 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
219 const PrecompiledPrograms& precompiled_programs, 221 const PrecompiledPrograms& precompiled_programs,
220 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr) 222 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
221 : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr}, 223 : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr},
222 unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache}, 224 unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache},
223 precompiled_programs{precompiled_programs} { 225 precompiled_programs{precompiled_programs} {
224 226 const std::size_t code_size{CalculateProgramSize(program_code)};
225 const std::size_t code_size = CalculateProgramSize(program_code); 227 const std::size_t code_size_b{program_code_b.empty() ? 0
226 const std::size_t code_size_b = 228 : CalculateProgramSize(program_code_b)};
227 program_code_b.empty() ? 0 : CalculateProgramSize(program_code_b); 229 GLShader::ProgramResult program_result{
228 230 CreateProgram(device, program_type, program_code, program_code_b)};
229 GLShader::ProgramResult program_result =
230 CreateProgram(program_type, program_code, program_code_b);
231 if (program_result.first.empty()) { 231 if (program_result.first.empty()) {
232 // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now 232 // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now
233 return; 233 return;
@@ -251,7 +251,6 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
251 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, 251 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
252 program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{ 252 program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{
253 precompiled_programs} { 253 precompiled_programs} {
254
255 code = std::move(result.first); 254 code = std::move(result.first);
256 entries = result.second; 255 entries = result.second;
257 shader_length = entries.shader_length; 256 shader_length = entries.shader_length;
@@ -344,8 +343,9 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode,
344 return {unique_identifier, base_bindings, primitive_mode}; 343 return {unique_identifier, base_bindings, primitive_mode};
345} 344}
346 345
347ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system) 346ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
348 : RasterizerCache{rasterizer}, disk_cache{system} {} 347 const Device& device)
348 : RasterizerCache{rasterizer}, disk_cache{system}, device{device} {}
349 349
350void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, 350void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
351 const VideoCore::DiskResourceLoadCallback& callback) { 351 const VideoCore::DiskResourceLoadCallback& callback) {
@@ -363,6 +363,10 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
363 if (stop_loading) 363 if (stop_loading)
364 return; 364 return;
365 365
366 // Track if precompiled cache was altered during loading to know if we have to serialize the
367 // virtual precompiled cache file back to the hard drive
368 bool precompiled_cache_altered = false;
369
366 // Build shaders 370 // Build shaders
367 if (callback) 371 if (callback)
368 callback(VideoCore::LoadCallbackStage::Build, 0, usages.size()); 372 callback(VideoCore::LoadCallbackStage::Build, 0, usages.size());
@@ -384,6 +388,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
384 if (!shader) { 388 if (!shader) {
385 // Invalidate the precompiled cache if a shader dumped shader was rejected 389 // Invalidate the precompiled cache if a shader dumped shader was rejected
386 disk_cache.InvalidatePrecompiled(); 390 disk_cache.InvalidatePrecompiled();
391 precompiled_cache_altered = true;
387 dumps.clear(); 392 dumps.clear();
388 } 393 }
389 } 394 }
@@ -405,8 +410,13 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
405 if (dumps.find(usage) == dumps.end()) { 410 if (dumps.find(usage) == dumps.end()) {
406 const auto& program = precompiled_programs.at(usage); 411 const auto& program = precompiled_programs.at(usage);
407 disk_cache.SaveDump(usage, program->handle); 412 disk_cache.SaveDump(usage, program->handle);
413 precompiled_cache_altered = true;
408 } 414 }
409 } 415 }
416
417 if (precompiled_cache_altered) {
418 disk_cache.SaveVirtualPrecompiledFile();
419 }
410} 420}
411 421
412CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( 422CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram(
@@ -439,17 +449,18 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia
439 const std::unordered_map<u64, ShaderDiskCacheDecompiled>& decompiled) { 449 const std::unordered_map<u64, ShaderDiskCacheDecompiled>& decompiled) {
440 std::unordered_map<u64, UnspecializedShader> unspecialized; 450 std::unordered_map<u64, UnspecializedShader> unspecialized;
441 451
442 if (callback) 452 if (callback) {
443 callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size()); 453 callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size());
454 }
444 455
445 for (std::size_t i = 0; i < raws.size(); ++i) { 456 for (std::size_t i = 0; i < raws.size(); ++i) {
446 if (stop_loading) 457 if (stop_loading) {
447 return {}; 458 return {};
448 459 }
449 const auto& raw{raws[i]}; 460 const auto& raw{raws[i]};
450 const u64 unique_identifier = raw.GetUniqueIdentifier(); 461 const u64 unique_identifier{raw.GetUniqueIdentifier()};
451 const u64 calculated_hash = 462 const u64 calculated_hash{
452 GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); 463 GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB())};
453 if (unique_identifier != calculated_hash) { 464 if (unique_identifier != calculated_hash) {
454 LOG_ERROR( 465 LOG_ERROR(
455 Render_OpenGL, 466 Render_OpenGL,
@@ -466,8 +477,8 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia
466 result = {stored_decompiled.code, stored_decompiled.entries}; 477 result = {stored_decompiled.code, stored_decompiled.entries};
467 } else { 478 } else {
468 // Otherwise decompile the shader at boot and save the result to the decompiled file 479 // Otherwise decompile the shader at boot and save the result to the decompiled file
469 result = 480 result = CreateProgram(device, raw.GetProgramType(), raw.GetProgramCode(),
470 CreateProgram(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); 481 raw.GetProgramCodeB());
471 disk_cache.SaveDecompiled(unique_identifier, result.first, result.second); 482 disk_cache.SaveDecompiled(unique_identifier, result.first, result.second);
472 } 483 }
473 484
@@ -477,8 +488,9 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia
477 {raw.GetUniqueIdentifier(), 488 {raw.GetUniqueIdentifier(),
478 {std::move(result.first), std::move(result.second), raw.GetProgramType()}}); 489 {std::move(result.first), std::move(result.second), raw.GetProgramType()}});
479 490
480 if (callback) 491 if (callback) {
481 callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size()); 492 callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size());
493 }
482 } 494 }
483 return unspecialized; 495 return unspecialized;
484} 496}
@@ -497,11 +509,12 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
497 509
498 if (!shader) { 510 if (!shader) {
499 // No shader found - create a new one 511 // No shader found - create a new one
500 ProgramCode program_code{GetShaderCode(host_ptr)}; 512 ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)};
501 ProgramCode program_code_b; 513 ProgramCode program_code_b;
502 if (program == Maxwell::ShaderProgram::VertexA) { 514 if (program == Maxwell::ShaderProgram::VertexA) {
503 program_code_b = GetShaderCode( 515 const GPUVAddr program_addr_b{GetShaderAddress(Maxwell::ShaderProgram::VertexB)};
504 memory_manager.GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); 516 program_code_b = GetShaderCode(memory_manager, program_addr_b,
517 memory_manager.GetPointer(program_addr_b));
505 } 518 }
506 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); 519 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
507 const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)}; 520 const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
@@ -512,7 +525,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
512 precompiled_programs, found->second, host_ptr); 525 precompiled_programs, found->second, host_ptr);
513 } else { 526 } else {
514 shader = std::make_shared<CachedShader>( 527 shader = std::make_shared<CachedShader>(
515 cpu_addr, unique_identifier, program, disk_cache, precompiled_programs, 528 device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
516 std::move(program_code), std::move(program_code_b), host_ptr); 529 std::move(program_code), std::move(program_code_b), host_ptr);
517 } 530 }
518 Register(shader); 531 Register(shader);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 0cf8e0b3d..a332087f8 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -27,6 +27,7 @@ class System;
27namespace OpenGL { 27namespace OpenGL {
28 28
29class CachedShader; 29class CachedShader;
30class Device;
30class RasterizerOpenGL; 31class RasterizerOpenGL;
31struct UnspecializedShader; 32struct UnspecializedShader;
32 33
@@ -38,7 +39,7 @@ using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>;
38 39
39class CachedShader final : public RasterizerCacheObject { 40class CachedShader final : public RasterizerCacheObject {
40public: 41public:
41 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier, 42 explicit CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier,
42 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, 43 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
43 const PrecompiledPrograms& precompiled_programs, 44 const PrecompiledPrograms& precompiled_programs,
44 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr); 45 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr);
@@ -112,7 +113,8 @@ private:
112 113
113class ShaderCacheOpenGL final : public RasterizerCache<Shader> { 114class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
114public: 115public:
115 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system); 116 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
117 const Device& device);
116 118
117 /// Loads disk cache for the current game 119 /// Loads disk cache for the current game
118 void LoadDiskCache(const std::atomic_bool& stop_loading, 120 void LoadDiskCache(const std::atomic_bool& stop_loading,
@@ -130,6 +132,8 @@ private:
130 CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, 132 CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
131 const std::set<GLenum>& supported_formats); 133 const std::set<GLenum>& supported_formats);
132 134
135 const Device& device;
136
133 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; 137 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
134 138
135 ShaderDiskCacheOpenGL disk_cache; 139 ShaderDiskCacheOpenGL disk_cache;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 28e490b3c..ef1a1995f 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -15,6 +15,7 @@
15#include "common/assert.h" 15#include "common/assert.h"
16#include "common/common_types.h" 16#include "common/common_types.h"
17#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/renderer_opengl/gl_device.h"
18#include "video_core/renderer_opengl/gl_rasterizer.h" 19#include "video_core/renderer_opengl/gl_rasterizer.h"
19#include "video_core/renderer_opengl/gl_shader_decompiler.h" 20#include "video_core/renderer_opengl/gl_shader_decompiler.h"
20#include "video_core/shader/shader_ir.h" 21#include "video_core/shader/shader_ir.h"
@@ -45,8 +46,6 @@ using TextureIR = std::variant<TextureAoffi, TextureArgument>;
45enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; 46enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
46constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 47constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
47 static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float)); 48 static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float));
48constexpr u32 MAX_GLOBALMEMORY_ELEMENTS =
49 static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize) / sizeof(float);
50 49
51class ShaderWriter { 50class ShaderWriter {
52public: 51public:
@@ -121,14 +120,10 @@ std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
121 120
122/// Returns true if an object has to be treated as precise 121/// Returns true if an object has to be treated as precise
123bool IsPrecise(Operation operand) { 122bool IsPrecise(Operation operand) {
124 const auto& meta = operand.GetMeta(); 123 const auto& meta{operand.GetMeta()};
125
126 if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) { 124 if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) {
127 return arithmetic->precise; 125 return arithmetic->precise;
128 } 126 }
129 if (const auto half_arithmetic = std::get_if<MetaHalfArithmetic>(&meta)) {
130 return half_arithmetic->precise;
131 }
132 return false; 127 return false;
133} 128}
134 129
@@ -141,8 +136,9 @@ bool IsPrecise(Node node) {
141 136
142class GLSLDecompiler final { 137class GLSLDecompiler final {
143public: 138public:
144 explicit GLSLDecompiler(const ShaderIR& ir, ShaderStage stage, std::string suffix) 139 explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage,
145 : ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} 140 std::string suffix)
141 : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
146 142
147 void Decompile() { 143 void Decompile() {
148 DeclareVertex(); 144 DeclareVertex();
@@ -208,8 +204,10 @@ public:
208 for (const auto& sampler : ir.GetSamplers()) { 204 for (const auto& sampler : ir.GetSamplers()) {
209 entries.samplers.emplace_back(sampler); 205 entries.samplers.emplace_back(sampler);
210 } 206 }
211 for (const auto& gmem : ir.GetGlobalMemoryBases()) { 207 for (const auto& gmem_pair : ir.GetGlobalMemory()) {
212 entries.global_memory_entries.emplace_back(gmem.cbuf_index, gmem.cbuf_offset); 208 const auto& [base, usage] = gmem_pair;
209 entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset,
210 usage.is_read, usage.is_written);
213 } 211 }
214 entries.clip_distances = ir.GetClipDistances(); 212 entries.clip_distances = ir.GetClipDistances();
215 entries.shader_length = ir.GetLength(); 213 entries.shader_length = ir.GetLength();
@@ -380,12 +378,22 @@ private:
380 } 378 }
381 379
382 void DeclareGlobalMemory() { 380 void DeclareGlobalMemory() {
383 for (const auto& entry : ir.GetGlobalMemoryBases()) { 381 for (const auto& gmem : ir.GetGlobalMemory()) {
382 const auto& [base, usage] = gmem;
383
384 // Since we don't know how the shader will use the shader, hint the driver to disable as
385 // much optimizations as possible
386 std::string qualifier = "coherent volatile";
387 if (usage.is_read && !usage.is_written)
388 qualifier += " readonly";
389 else if (usage.is_written && !usage.is_read)
390 qualifier += " writeonly";
391
384 const std::string binding = 392 const std::string binding =
385 fmt::format("GMEM_BINDING_{}_{}", entry.cbuf_index, entry.cbuf_offset); 393 fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset);
386 code.AddLine("layout (std430, binding = " + binding + ") buffer " + 394 code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " +
387 GetGlobalMemoryBlock(entry) + " {"); 395 GetGlobalMemoryBlock(base) + " {");
388 code.AddLine(" float " + GetGlobalMemory(entry) + "[MAX_GLOBALMEMORY_ELEMENTS];"); 396 code.AddLine(" float " + GetGlobalMemory(base) + "[];");
389 code.AddLine("};"); 397 code.AddLine("};");
390 code.AddNewLine(); 398 code.AddNewLine();
391 } 399 }
@@ -617,28 +625,7 @@ private:
617 } 625 }
618 626
619 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { 627 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
620 std::string value = VisitOperand(operation, operand_index); 628 return CastOperand(VisitOperand(operation, operand_index), type);
621 switch (type) {
622 case Type::HalfFloat: {
623 const auto half_meta = std::get_if<MetaHalfArithmetic>(&operation.GetMeta());
624 if (!half_meta) {
625 value = "toHalf2(" + value + ')';
626 }
627
628 switch (half_meta->types.at(operand_index)) {
629 case Tegra::Shader::HalfType::H0_H1:
630 return "toHalf2(" + value + ')';
631 case Tegra::Shader::HalfType::F32:
632 return "vec2(" + value + ')';
633 case Tegra::Shader::HalfType::H0_H0:
634 return "vec2(toHalf2(" + value + ")[0])";
635 case Tegra::Shader::HalfType::H1_H1:
636 return "vec2(toHalf2(" + value + ")[1])";
637 }
638 }
639 default:
640 return CastOperand(value, type);
641 }
642 } 629 }
643 630
644 std::string CastOperand(const std::string& value, Type type) const { 631 std::string CastOperand(const std::string& value, Type type) const {
@@ -652,9 +639,7 @@ private:
652 case Type::Uint: 639 case Type::Uint:
653 return "ftou(" + value + ')'; 640 return "ftou(" + value + ')';
654 case Type::HalfFloat: 641 case Type::HalfFloat:
655 // Can't be handled as a stand-alone value 642 return "toHalf2(" + value + ')';
656 UNREACHABLE();
657 return value;
658 } 643 }
659 UNREACHABLE(); 644 UNREACHABLE();
660 return value; 645 return value;
@@ -819,8 +804,12 @@ private:
819 // Inline the string as an immediate integer in GLSL (AOFFI arguments are required 804 // Inline the string as an immediate integer in GLSL (AOFFI arguments are required
820 // to be constant by the standard). 805 // to be constant by the standard).
821 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 806 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
822 } else { 807 } else if (device.HasVariableAoffi()) {
808 // Avoid using variable AOFFI on unsupported devices.
823 expr += "ftoi(" + Visit(operand) + ')'; 809 expr += "ftoi(" + Visit(operand) + ')';
810 } else {
811 // Insert 0 on devices not supporting variable AOFFI.
812 expr += '0';
824 } 813 }
825 if (index + 1 < aoffi.size()) { 814 if (index + 1 < aoffi.size()) {
826 expr += ", "; 815 expr += ", ";
@@ -868,6 +857,12 @@ private:
868 } else if (const auto lmem = std::get_if<LmemNode>(dest)) { 857 } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
869 target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; 858 target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]";
870 859
860 } else if (const auto gmem = std::get_if<GmemNode>(dest)) {
861 const std::string real = Visit(gmem->GetRealAddress());
862 const std::string base = Visit(gmem->GetBaseAddress());
863 const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4";
864 target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
865
871 } else { 866 } else {
872 UNREACHABLE_MSG("Assign called without a proper target"); 867 UNREACHABLE_MSG("Assign called without a proper target");
873 } 868 }
@@ -1067,13 +1062,40 @@ private:
1067 return BitwiseCastResult(value, Type::HalfFloat); 1062 return BitwiseCastResult(value, Type::HalfFloat);
1068 } 1063 }
1069 1064
1065 std::string HClamp(Operation operation) {
1066 const std::string value = VisitOperand(operation, 0, Type::HalfFloat);
1067 const std::string min = VisitOperand(operation, 1, Type::Float);
1068 const std::string max = VisitOperand(operation, 2, Type::Float);
1069 const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))";
1070 return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat));
1071 }
1072
1073 std::string HUnpack(Operation operation) {
1074 const std::string operand{VisitOperand(operation, 0, Type::HalfFloat)};
1075 const auto value = [&]() -> std::string {
1076 switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
1077 case Tegra::Shader::HalfType::H0_H1:
1078 return operand;
1079 case Tegra::Shader::HalfType::F32:
1080 return "vec2(fromHalf2(" + operand + "))";
1081 case Tegra::Shader::HalfType::H0_H0:
1082 return "vec2(" + operand + "[0])";
1083 case Tegra::Shader::HalfType::H1_H1:
1084 return "vec2(" + operand + "[1])";
1085 }
1086 UNREACHABLE();
1087 return "0";
1088 }();
1089 return "fromHalf2(" + value + ')';
1090 }
1091
1070 std::string HMergeF32(Operation operation) { 1092 std::string HMergeF32(Operation operation) {
1071 return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; 1093 return "float(toHalf2(" + Visit(operation[0]) + ")[0])";
1072 } 1094 }
1073 1095
1074 std::string HMergeH0(Operation operation) { 1096 std::string HMergeH0(Operation operation) {
1075 return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[1], toHalf2(" + 1097 return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" +
1076 Visit(operation[1]) + ")[0]))"; 1098 Visit(operation[0]) + ")[1]))";
1077 } 1099 }
1078 1100
1079 std::string HMergeH1(Operation operation) { 1101 std::string HMergeH1(Operation operation) {
@@ -1173,34 +1195,46 @@ private:
1173 return GenerateUnary(operation, "any", Type::Bool, Type::Bool2); 1195 return GenerateUnary(operation, "any", Type::Bool, Type::Bool2);
1174 } 1196 }
1175 1197
1198 template <bool with_nan>
1199 std::string GenerateHalfComparison(Operation operation, std::string compare_op) {
1200 std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2,
1201 Type::HalfFloat, Type::HalfFloat)};
1202 if constexpr (!with_nan) {
1203 return comparison;
1204 }
1205 return "halfFloatNanComparison(" + comparison + ", " +
1206 VisitOperand(operation, 0, Type::HalfFloat) + ", " +
1207 VisitOperand(operation, 1, Type::HalfFloat) + ')';
1208 }
1209
1210 template <bool with_nan>
1176 std::string Logical2HLessThan(Operation operation) { 1211 std::string Logical2HLessThan(Operation operation) {
1177 return GenerateBinaryCall(operation, "lessThan", Type::Bool2, Type::HalfFloat, 1212 return GenerateHalfComparison<with_nan>(operation, "lessThan");
1178 Type::HalfFloat);
1179 } 1213 }
1180 1214
1215 template <bool with_nan>
1181 std::string Logical2HEqual(Operation operation) { 1216 std::string Logical2HEqual(Operation operation) {
1182 return GenerateBinaryCall(operation, "equal", Type::Bool2, Type::HalfFloat, 1217 return GenerateHalfComparison<with_nan>(operation, "equal");
1183 Type::HalfFloat);
1184 } 1218 }
1185 1219
1220 template <bool with_nan>
1186 std::string Logical2HLessEqual(Operation operation) { 1221 std::string Logical2HLessEqual(Operation operation) {
1187 return GenerateBinaryCall(operation, "lessThanEqual", Type::Bool2, Type::HalfFloat, 1222 return GenerateHalfComparison<with_nan>(operation, "lessThanEqual");
1188 Type::HalfFloat);
1189 } 1223 }
1190 1224
1225 template <bool with_nan>
1191 std::string Logical2HGreaterThan(Operation operation) { 1226 std::string Logical2HGreaterThan(Operation operation) {
1192 return GenerateBinaryCall(operation, "greaterThan", Type::Bool2, Type::HalfFloat, 1227 return GenerateHalfComparison<with_nan>(operation, "greaterThan");
1193 Type::HalfFloat);
1194 } 1228 }
1195 1229
1230 template <bool with_nan>
1196 std::string Logical2HNotEqual(Operation operation) { 1231 std::string Logical2HNotEqual(Operation operation) {
1197 return GenerateBinaryCall(operation, "notEqual", Type::Bool2, Type::HalfFloat, 1232 return GenerateHalfComparison<with_nan>(operation, "notEqual");
1198 Type::HalfFloat);
1199 } 1233 }
1200 1234
1235 template <bool with_nan>
1201 std::string Logical2HGreaterEqual(Operation operation) { 1236 std::string Logical2HGreaterEqual(Operation operation) {
1202 return GenerateBinaryCall(operation, "greaterThanEqual", Type::Bool2, Type::HalfFloat, 1237 return GenerateHalfComparison<with_nan>(operation, "greaterThanEqual");
1203 Type::HalfFloat);
1204 } 1238 }
1205 1239
1206 std::string Texture(Operation operation) { 1240 std::string Texture(Operation operation) {
@@ -1489,6 +1523,8 @@ private:
1489 &GLSLDecompiler::Fma<Type::HalfFloat>, 1523 &GLSLDecompiler::Fma<Type::HalfFloat>,
1490 &GLSLDecompiler::Absolute<Type::HalfFloat>, 1524 &GLSLDecompiler::Absolute<Type::HalfFloat>,
1491 &GLSLDecompiler::HNegate, 1525 &GLSLDecompiler::HNegate,
1526 &GLSLDecompiler::HClamp,
1527 &GLSLDecompiler::HUnpack,
1492 &GLSLDecompiler::HMergeF32, 1528 &GLSLDecompiler::HMergeF32,
1493 &GLSLDecompiler::HMergeH0, 1529 &GLSLDecompiler::HMergeH0,
1494 &GLSLDecompiler::HMergeH1, 1530 &GLSLDecompiler::HMergeH1,
@@ -1525,12 +1561,18 @@ private:
1525 &GLSLDecompiler::LogicalNotEqual<Type::Uint>, 1561 &GLSLDecompiler::LogicalNotEqual<Type::Uint>,
1526 &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>, 1562 &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>,
1527 1563
1528 &GLSLDecompiler::Logical2HLessThan, 1564 &GLSLDecompiler::Logical2HLessThan<false>,
1529 &GLSLDecompiler::Logical2HEqual, 1565 &GLSLDecompiler::Logical2HEqual<false>,
1530 &GLSLDecompiler::Logical2HLessEqual, 1566 &GLSLDecompiler::Logical2HLessEqual<false>,
1531 &GLSLDecompiler::Logical2HGreaterThan, 1567 &GLSLDecompiler::Logical2HGreaterThan<false>,
1532 &GLSLDecompiler::Logical2HNotEqual, 1568 &GLSLDecompiler::Logical2HNotEqual<false>,
1533 &GLSLDecompiler::Logical2HGreaterEqual, 1569 &GLSLDecompiler::Logical2HGreaterEqual<false>,
1570 &GLSLDecompiler::Logical2HLessThan<true>,
1571 &GLSLDecompiler::Logical2HEqual<true>,
1572 &GLSLDecompiler::Logical2HLessEqual<true>,
1573 &GLSLDecompiler::Logical2HGreaterThan<true>,
1574 &GLSLDecompiler::Logical2HNotEqual<true>,
1575 &GLSLDecompiler::Logical2HGreaterEqual<true>,
1534 1576
1535 &GLSLDecompiler::Texture, 1577 &GLSLDecompiler::Texture,
1536 &GLSLDecompiler::TextureLod, 1578 &GLSLDecompiler::TextureLod,
@@ -1609,6 +1651,7 @@ private:
1609 return name + '_' + std::to_string(index) + '_' + suffix; 1651 return name + '_' + std::to_string(index) + '_' + suffix;
1610 } 1652 }
1611 1653
1654 const Device& device;
1612 const ShaderIR& ir; 1655 const ShaderIR& ir;
1613 const ShaderStage stage; 1656 const ShaderStage stage;
1614 const std::string suffix; 1657 const std::string suffix;
@@ -1621,9 +1664,7 @@ private:
1621 1664
1622std::string GetCommonDeclarations() { 1665std::string GetCommonDeclarations() {
1623 const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); 1666 const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS);
1624 const auto gmem = std::to_string(MAX_GLOBALMEMORY_ELEMENTS);
1625 return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + 1667 return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" +
1626 "#define MAX_GLOBALMEMORY_ELEMENTS " + gmem + "\n" +
1627 "#define ftoi floatBitsToInt\n" 1668 "#define ftoi floatBitsToInt\n"
1628 "#define ftou floatBitsToUint\n" 1669 "#define ftou floatBitsToUint\n"
1629 "#define itof intBitsToFloat\n" 1670 "#define itof intBitsToFloat\n"
@@ -1633,11 +1674,18 @@ std::string GetCommonDeclarations() {
1633 "}\n\n" 1674 "}\n\n"
1634 "vec2 toHalf2(float value) {\n" 1675 "vec2 toHalf2(float value) {\n"
1635 " return unpackHalf2x16(ftou(value));\n" 1676 " return unpackHalf2x16(ftou(value));\n"
1677 "}\n\n"
1678 "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n"
1679 " bvec2 is_nan1 = isnan(pair1);\n"
1680 " bvec2 is_nan2 = isnan(pair2);\n"
1681 " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
1682 "is_nan2.y);\n"
1636 "}\n"; 1683 "}\n";
1637} 1684}
1638 1685
1639ProgramResult Decompile(const ShaderIR& ir, Maxwell::ShaderStage stage, const std::string& suffix) { 1686ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage,
1640 GLSLDecompiler decompiler(ir, stage, suffix); 1687 const std::string& suffix) {
1688 GLSLDecompiler decompiler(device, ir, stage, suffix);
1641 decompiler.Decompile(); 1689 decompiler.Decompile();
1642 return {decompiler.GetResult(), decompiler.GetShaderEntries()}; 1690 return {decompiler.GetResult(), decompiler.GetShaderEntries()};
1643} 1691}
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 4e04ab2f8..c1569e737 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -12,6 +12,10 @@
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/shader/shader_ir.h" 13#include "video_core/shader/shader_ir.h"
14 14
15namespace OpenGL {
16class Device;
17}
18
15namespace VideoCommon::Shader { 19namespace VideoCommon::Shader {
16class ShaderIR; 20class ShaderIR;
17} 21}
@@ -39,8 +43,9 @@ private:
39 43
40class GlobalMemoryEntry { 44class GlobalMemoryEntry {
41public: 45public:
42 explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset) 46 explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset, bool is_read, bool is_written)
43 : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} 47 : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset}, is_read{is_read}, is_written{
48 is_written} {}
44 49
45 u32 GetCbufIndex() const { 50 u32 GetCbufIndex() const {
46 return cbuf_index; 51 return cbuf_index;
@@ -50,14 +55,25 @@ public:
50 return cbuf_offset; 55 return cbuf_offset;
51 } 56 }
52 57
58 bool IsRead() const {
59 return is_read;
60 }
61
62 bool IsWritten() const {
63 return is_written;
64 }
65
53private: 66private:
54 u32 cbuf_index{}; 67 u32 cbuf_index{};
55 u32 cbuf_offset{}; 68 u32 cbuf_offset{};
69 bool is_read{};
70 bool is_written{};
56}; 71};
57 72
58struct ShaderEntries { 73struct ShaderEntries {
59 std::vector<ConstBufferEntry> const_buffers; 74 std::vector<ConstBufferEntry> const_buffers;
60 std::vector<SamplerEntry> samplers; 75 std::vector<SamplerEntry> samplers;
76 std::vector<SamplerEntry> bindless_samplers;
61 std::vector<GlobalMemoryEntry> global_memory_entries; 77 std::vector<GlobalMemoryEntry> global_memory_entries;
62 std::array<bool, Maxwell::NumClipDistances> clip_distances{}; 78 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
63 std::size_t shader_length{}; 79 std::size_t shader_length{};
@@ -65,7 +81,7 @@ struct ShaderEntries {
65 81
66std::string GetCommonDeclarations(); 82std::string GetCommonDeclarations();
67 83
68ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage, 84ProgramResult Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
69 const std::string& suffix); 85 Maxwell::ShaderStage stage, const std::string& suffix);
70 86
71} // namespace OpenGL::GLShader \ No newline at end of file 87} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 8a43eb157..ed7afc4a0 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -104,7 +104,8 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
104 return true; 104 return true;
105} 105}
106 106
107ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} 107ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system)
108 : system{system}, precompiled_cache_virtual_file_offset{0} {}
108 109
109std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> 110std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
110ShaderDiskCacheOpenGL::LoadTransferable() { 111ShaderDiskCacheOpenGL::LoadTransferable() {
@@ -177,6 +178,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
177 return {}; 178 return {};
178 } 179 }
179 } 180 }
181
180 return {{raws, usages}}; 182 return {{raws, usages}};
181} 183}
182 184
@@ -208,59 +210,64 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() {
208std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, 210std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
209 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> 211 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
210ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { 212ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
213 // Read compressed file from disk and decompress to virtual precompiled cache file
214 std::vector<u8> compressed(file.GetSize());
215 file.ReadBytes(compressed.data(), compressed.size());
216 const std::vector<u8> decompressed = Common::Compression::DecompressDataZSTD(compressed);
217 SaveArrayToPrecompiled(decompressed.data(), decompressed.size());
218 precompiled_cache_virtual_file_offset = 0;
219
211 ShaderCacheVersionHash file_hash{}; 220 ShaderCacheVersionHash file_hash{};
212 if (file.ReadArray(file_hash.data(), file_hash.size()) != file_hash.size()) { 221 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) {
222 precompiled_cache_virtual_file_offset = 0;
213 return {}; 223 return {};
214 } 224 }
215 if (GetShaderCacheVersionHash() != file_hash) { 225 if (GetShaderCacheVersionHash() != file_hash) {
216 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); 226 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
227 precompiled_cache_virtual_file_offset = 0;
217 return {}; 228 return {};
218 } 229 }
219 230
220 std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; 231 std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled;
221 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; 232 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps;
222 while (file.Tell() < file.GetSize()) { 233 while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
223 PrecompiledEntryKind kind{}; 234 PrecompiledEntryKind kind{};
224 if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { 235 if (!LoadObjectFromPrecompiled(kind)) {
225 return {}; 236 return {};
226 } 237 }
227 238
228 switch (kind) { 239 switch (kind) {
229 case PrecompiledEntryKind::Decompiled: { 240 case PrecompiledEntryKind::Decompiled: {
230 u64 unique_identifier{}; 241 u64 unique_identifier{};
231 if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64)) 242 if (!LoadObjectFromPrecompiled(unique_identifier)) {
232 return {}; 243 return {};
244 }
233 245
234 const auto entry = LoadDecompiledEntry(file); 246 const auto entry = LoadDecompiledEntry();
235 if (!entry) 247 if (!entry) {
236 return {}; 248 return {};
249 }
237 decompiled.insert({unique_identifier, std::move(*entry)}); 250 decompiled.insert({unique_identifier, std::move(*entry)});
238 break; 251 break;
239 } 252 }
240 case PrecompiledEntryKind::Dump: { 253 case PrecompiledEntryKind::Dump: {
241 ShaderDiskCacheUsage usage; 254 ShaderDiskCacheUsage usage;
242 if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) 255 if (!LoadObjectFromPrecompiled(usage)) {
243 return {}; 256 return {};
257 }
244 258
245 ShaderDiskCacheDump dump; 259 ShaderDiskCacheDump dump;
246 if (file.ReadBytes(&dump.binary_format, sizeof(u32)) != sizeof(u32)) 260 if (!LoadObjectFromPrecompiled(dump.binary_format)) {
247 return {};
248
249 u32 binary_length{};
250 u32 compressed_size{};
251 if (file.ReadBytes(&binary_length, sizeof(u32)) != sizeof(u32) ||
252 file.ReadBytes(&compressed_size, sizeof(u32)) != sizeof(u32)) {
253 return {}; 261 return {};
254 } 262 }
255 263
256 std::vector<u8> compressed_binary(compressed_size); 264 u32 binary_length{};
257 if (file.ReadArray(compressed_binary.data(), compressed_binary.size()) != 265 if (!LoadObjectFromPrecompiled(binary_length)) {
258 compressed_binary.size()) {
259 return {}; 266 return {};
260 } 267 }
261 268
262 dump.binary = Common::Compression::DecompressDataZSTD(compressed_binary); 269 dump.binary.resize(binary_length);
263 if (dump.binary.empty()) { 270 if (!LoadArrayFromPrecompiled(dump.binary.data(), dump.binary.size())) {
264 return {}; 271 return {};
265 } 272 }
266 273
@@ -274,143 +281,154 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
274 return {{decompiled, dumps}}; 281 return {{decompiled, dumps}};
275} 282}
276 283
277std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry( 284std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry() {
278 FileUtil::IOFile& file) {
279 u32 code_size{}; 285 u32 code_size{};
280 u32 compressed_code_size{}; 286 if (!LoadObjectFromPrecompiled(code_size)) {
281 if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) ||
282 file.ReadBytes(&compressed_code_size, sizeof(u32)) != sizeof(u32)) {
283 return {}; 287 return {};
284 } 288 }
285 289
286 std::vector<u8> compressed_code(compressed_code_size); 290 std::vector<u8> code(code_size);
287 if (file.ReadArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { 291 if (!LoadArrayFromPrecompiled(code.data(), code.size())) {
288 return {}; 292 return {};
289 } 293 }
290 294
291 const std::vector<u8> code = Common::Compression::DecompressDataZSTD(compressed_code);
292 if (code.empty()) {
293 return {};
294 }
295 ShaderDiskCacheDecompiled entry; 295 ShaderDiskCacheDecompiled entry;
296 entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size); 296 entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size);
297 297
298 u32 const_buffers_count{}; 298 u32 const_buffers_count{};
299 if (file.ReadBytes(&const_buffers_count, sizeof(u32)) != sizeof(u32)) 299 if (!LoadObjectFromPrecompiled(const_buffers_count)) {
300 return {}; 300 return {};
301 }
302
301 for (u32 i = 0; i < const_buffers_count; ++i) { 303 for (u32 i = 0; i < const_buffers_count; ++i) {
302 u32 max_offset{}; 304 u32 max_offset{};
303 u32 index{}; 305 u32 index{};
304 u8 is_indirect{}; 306 u8 is_indirect{};
305 if (file.ReadBytes(&max_offset, sizeof(u32)) != sizeof(u32) || 307 if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) ||
306 file.ReadBytes(&index, sizeof(u32)) != sizeof(u32) || 308 !LoadObjectFromPrecompiled(is_indirect)) {
307 file.ReadBytes(&is_indirect, sizeof(u8)) != sizeof(u8)) {
308 return {}; 309 return {};
309 } 310 }
310 entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); 311 entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index);
311 } 312 }
312 313
313 u32 samplers_count{}; 314 u32 samplers_count{};
314 if (file.ReadBytes(&samplers_count, sizeof(u32)) != sizeof(u32)) 315 if (!LoadObjectFromPrecompiled(samplers_count)) {
315 return {}; 316 return {};
317 }
318
316 for (u32 i = 0; i < samplers_count; ++i) { 319 for (u32 i = 0; i < samplers_count; ++i) {
317 u64 offset{}; 320 u64 offset{};
318 u64 index{}; 321 u64 index{};
319 u32 type{}; 322 u32 type{};
320 u8 is_array{}; 323 u8 is_array{};
321 u8 is_shadow{}; 324 u8 is_shadow{};
322 if (file.ReadBytes(&offset, sizeof(u64)) != sizeof(u64) || 325 u8 is_bindless{};
323 file.ReadBytes(&index, sizeof(u64)) != sizeof(u64) || 326 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
324 file.ReadBytes(&type, sizeof(u32)) != sizeof(u32) || 327 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) ||
325 file.ReadBytes(&is_array, sizeof(u8)) != sizeof(u8) || 328 !LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) {
326 file.ReadBytes(&is_shadow, sizeof(u8)) != sizeof(u8)) {
327 return {}; 329 return {};
328 } 330 }
329 entry.entries.samplers.emplace_back( 331 entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset),
330 static_cast<std::size_t>(offset), static_cast<std::size_t>(index), 332 static_cast<std::size_t>(index),
331 static_cast<Tegra::Shader::TextureType>(type), is_array != 0, is_shadow != 0); 333 static_cast<Tegra::Shader::TextureType>(type),
334 is_array != 0, is_shadow != 0, is_bindless != 0);
332 } 335 }
333 336
334 u32 global_memory_count{}; 337 u32 global_memory_count{};
335 if (file.ReadBytes(&global_memory_count, sizeof(u32)) != sizeof(u32)) 338 if (!LoadObjectFromPrecompiled(global_memory_count)) {
336 return {}; 339 return {};
340 }
341
337 for (u32 i = 0; i < global_memory_count; ++i) { 342 for (u32 i = 0; i < global_memory_count; ++i) {
338 u32 cbuf_index{}; 343 u32 cbuf_index{};
339 u32 cbuf_offset{}; 344 u32 cbuf_offset{};
340 if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || 345 u8 is_read{};
341 file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32)) { 346 u8 is_written{};
347 if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) ||
348 !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) {
342 return {}; 349 return {};
343 } 350 }
344 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset); 351 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0,
352 is_written != 0);
345 } 353 }
346 354
347 for (auto& clip_distance : entry.entries.clip_distances) { 355 for (auto& clip_distance : entry.entries.clip_distances) {
348 u8 clip_distance_raw{}; 356 u8 clip_distance_raw{};
349 if (file.ReadBytes(&clip_distance_raw, sizeof(u8)) != sizeof(u8)) 357 if (!LoadObjectFromPrecompiled(clip_distance_raw))
350 return {}; 358 return {};
351 clip_distance = clip_distance_raw != 0; 359 clip_distance = clip_distance_raw != 0;
352 } 360 }
353 361
354 u64 shader_length{}; 362 u64 shader_length{};
355 if (file.ReadBytes(&shader_length, sizeof(u64)) != sizeof(u64)) 363 if (!LoadObjectFromPrecompiled(shader_length)) {
356 return {}; 364 return {};
365 }
366
357 entry.entries.shader_length = static_cast<std::size_t>(shader_length); 367 entry.entries.shader_length = static_cast<std::size_t>(shader_length);
358 368
359 return entry; 369 return entry;
360} 370}
361 371
362bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, 372bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std::string& code,
363 const std::string& code,
364 const std::vector<u8>& compressed_code,
365 const GLShader::ShaderEntries& entries) { 373 const GLShader::ShaderEntries& entries) {
366 if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 || 374 if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) ||
367 file.WriteObject(unique_identifier) != 1 || 375 !SaveObjectToPrecompiled(unique_identifier) ||
368 file.WriteObject(static_cast<u32>(code.size())) != 1 || 376 !SaveObjectToPrecompiled(static_cast<u32>(code.size())) ||
369 file.WriteObject(static_cast<u32>(compressed_code.size())) != 1 || 377 !SaveArrayToPrecompiled(code.data(), code.size())) {
370 file.WriteArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) {
371 return false; 378 return false;
372 } 379 }
373 380
374 if (file.WriteObject(static_cast<u32>(entries.const_buffers.size())) != 1) 381 if (!SaveObjectToPrecompiled(static_cast<u32>(entries.const_buffers.size()))) {
375 return false; 382 return false;
383 }
376 for (const auto& cbuf : entries.const_buffers) { 384 for (const auto& cbuf : entries.const_buffers) {
377 if (file.WriteObject(static_cast<u32>(cbuf.GetMaxOffset())) != 1 || 385 if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) ||
378 file.WriteObject(static_cast<u32>(cbuf.GetIndex())) != 1 || 386 !SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) ||
379 file.WriteObject(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0)) != 1) { 387 !SaveObjectToPrecompiled(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0))) {
380 return false; 388 return false;
381 } 389 }
382 } 390 }
383 391
384 if (file.WriteObject(static_cast<u32>(entries.samplers.size())) != 1) 392 if (!SaveObjectToPrecompiled(static_cast<u32>(entries.samplers.size()))) {
385 return false; 393 return false;
394 }
386 for (const auto& sampler : entries.samplers) { 395 for (const auto& sampler : entries.samplers) {
387 if (file.WriteObject(static_cast<u64>(sampler.GetOffset())) != 1 || 396 if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) ||
388 file.WriteObject(static_cast<u64>(sampler.GetIndex())) != 1 || 397 !SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) ||
389 file.WriteObject(static_cast<u32>(sampler.GetType())) != 1 || 398 !SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) ||
390 file.WriteObject(static_cast<u8>(sampler.IsArray() ? 1 : 0)) != 1 || 399 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsArray() ? 1 : 0)) ||
391 file.WriteObject(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) != 1) { 400 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) ||
401 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsBindless() ? 1 : 0))) {
392 return false; 402 return false;
393 } 403 }
394 } 404 }
395 405
396 if (file.WriteObject(static_cast<u32>(entries.global_memory_entries.size())) != 1) 406 if (!SaveObjectToPrecompiled(static_cast<u32>(entries.global_memory_entries.size()))) {
397 return false; 407 return false;
408 }
398 for (const auto& gmem : entries.global_memory_entries) { 409 for (const auto& gmem : entries.global_memory_entries) {
399 if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || 410 if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) ||
400 file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1) { 411 !SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) ||
412 !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsRead() ? 1 : 0)) ||
413 !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsWritten() ? 1 : 0))) {
401 return false; 414 return false;
402 } 415 }
403 } 416 }
404 417
405 for (const bool clip_distance : entries.clip_distances) { 418 for (const bool clip_distance : entries.clip_distances) {
406 if (file.WriteObject(static_cast<u8>(clip_distance ? 1 : 0)) != 1) 419 if (!SaveObjectToPrecompiled(static_cast<u8>(clip_distance ? 1 : 0))) {
407 return false; 420 return false;
421 }
408 } 422 }
409 423
410 return file.WriteObject(static_cast<u64>(entries.shader_length)) == 1; 424 if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) {
425 return false;
426 }
427
428 return true;
411} 429}
412 430
413void ShaderDiskCacheOpenGL::InvalidateTransferable() const { 431void ShaderDiskCacheOpenGL::InvalidateTransferable() {
414 if (!FileUtil::Delete(GetTransferablePath())) { 432 if (!FileUtil::Delete(GetTransferablePath())) {
415 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", 433 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
416 GetTransferablePath()); 434 GetTransferablePath());
@@ -418,7 +436,10 @@ void ShaderDiskCacheOpenGL::InvalidateTransferable() const {
418 InvalidatePrecompiled(); 436 InvalidatePrecompiled();
419} 437}
420 438
421void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { 439void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
440 // Clear virtaul precompiled cache file
441 precompiled_cache_virtual_file.Resize(0);
442
422 if (!FileUtil::Delete(GetPrecompiledPath())) { 443 if (!FileUtil::Delete(GetPrecompiledPath())) {
423 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); 444 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
424 } 445 }
@@ -474,22 +495,13 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str
474 if (!IsUsable()) 495 if (!IsUsable())
475 return; 496 return;
476 497
477 const std::vector<u8> compressed_code{Common::Compression::CompressDataZSTDDefault( 498 if (precompiled_cache_virtual_file.GetSize() == 0) {
478 reinterpret_cast<const u8*>(code.data()), code.size())}; 499 SavePrecompiledHeaderToVirtualPrecompiledCache();
479 if (compressed_code.empty()) {
480 LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}",
481 unique_identifier);
482 return;
483 } 500 }
484 501
485 FileUtil::IOFile file = AppendPrecompiledFile(); 502 if (!SaveDecompiledFile(unique_identifier, code, entries)) {
486 if (!file.IsOpen())
487 return;
488
489 if (!SaveDecompiledFile(file, unique_identifier, code, compressed_code, entries)) {
490 LOG_ERROR(Render_OpenGL, 503 LOG_ERROR(Render_OpenGL,
491 "Failed to save decompiled entry to the precompiled file - removing"); 504 "Failed to save decompiled entry to the precompiled file - removing");
492 file.Close();
493 InvalidatePrecompiled(); 505 InvalidatePrecompiled();
494 } 506 }
495} 507}
@@ -505,28 +517,13 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
505 std::vector<u8> binary(binary_length); 517 std::vector<u8> binary(binary_length);
506 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); 518 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
507 519
508 const std::vector<u8> compressed_binary = 520 if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Dump)) ||
509 Common::Compression::CompressDataZSTDDefault(binary.data(), binary.size()); 521 !SaveObjectToPrecompiled(usage) ||
510 522 !SaveObjectToPrecompiled(static_cast<u32>(binary_format)) ||
511 if (compressed_binary.empty()) { 523 !SaveObjectToPrecompiled(static_cast<u32>(binary_length)) ||
512 LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", 524 !SaveArrayToPrecompiled(binary.data(), binary.size())) {
513 usage.unique_identifier);
514 return;
515 }
516
517 FileUtil::IOFile file = AppendPrecompiledFile();
518 if (!file.IsOpen())
519 return;
520
521 if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Dump)) != 1 ||
522 file.WriteObject(usage) != 1 || file.WriteObject(static_cast<u32>(binary_format)) != 1 ||
523 file.WriteObject(static_cast<u32>(binary_length)) != 1 ||
524 file.WriteObject(static_cast<u32>(compressed_binary.size())) != 1 ||
525 file.WriteArray(compressed_binary.data(), compressed_binary.size()) !=
526 compressed_binary.size()) {
527 LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", 525 LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing",
528 usage.unique_identifier); 526 usage.unique_identifier);
529 file.Close();
530 InvalidatePrecompiled(); 527 InvalidatePrecompiled();
531 return; 528 return;
532 } 529 }
@@ -559,28 +556,33 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
559 return file; 556 return file;
560} 557}
561 558
562FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { 559void ShaderDiskCacheOpenGL::SavePrecompiledHeaderToVirtualPrecompiledCache() {
563 if (!EnsureDirectories()) 560 const auto hash{GetShaderCacheVersionHash()};
564 return {}; 561 if (!SaveArrayToPrecompiled(hash.data(), hash.size())) {
562 LOG_ERROR(
563 Render_OpenGL,
564 "Failed to write precompiled cache version hash to virtual precompiled cache file");
565 }
566}
567
568void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
569 precompiled_cache_virtual_file_offset = 0;
570 const std::vector<u8>& uncompressed = precompiled_cache_virtual_file.ReadAllBytes();
571 const std::vector<u8>& compressed =
572 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size());
565 573
566 const auto precompiled_path{GetPrecompiledPath()}; 574 const auto precompiled_path{GetPrecompiledPath()};
567 const bool existed = FileUtil::Exists(precompiled_path); 575 FileUtil::IOFile file(precompiled_path, "wb");
568 576
569 FileUtil::IOFile file(precompiled_path, "ab");
570 if (!file.IsOpen()) { 577 if (!file.IsOpen()) {
571 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); 578 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
572 return {}; 579 return;
573 } 580 }
574 581 if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) {
575 if (!existed || file.GetSize() == 0) { 582 LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}",
576 const auto hash{GetShaderCacheVersionHash()}; 583 precompiled_path);
577 if (file.WriteArray(hash.data(), hash.size()) != hash.size()) { 584 return;
578 LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version hash in path={}",
579 precompiled_path);
580 return {};
581 }
582 } 585 }
583 return file;
584} 586}
585 587
586bool ShaderDiskCacheOpenGL::EnsureDirectories() const { 588bool ShaderDiskCacheOpenGL::EnsureDirectories() const {
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index 6be0c0547..0142b2e3b 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -16,6 +16,7 @@
16 16
17#include "common/assert.h" 17#include "common/assert.h"
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "core/file_sys/vfs_vector.h"
19#include "video_core/engines/maxwell_3d.h" 20#include "video_core/engines/maxwell_3d.h"
20#include "video_core/renderer_opengl/gl_shader_gen.h" 21#include "video_core/renderer_opengl/gl_shader_gen.h"
21 22
@@ -172,10 +173,10 @@ public:
172 LoadPrecompiled(); 173 LoadPrecompiled();
173 174
174 /// Removes the transferable (and precompiled) cache file. 175 /// Removes the transferable (and precompiled) cache file.
175 void InvalidateTransferable() const; 176 void InvalidateTransferable();
176 177
177 /// Removes the precompiled cache file. 178 /// Removes the precompiled cache file and clears virtual precompiled cache file.
178 void InvalidatePrecompiled() const; 179 void InvalidatePrecompiled();
179 180
180 /// Saves a raw dump to the transferable file. Checks for collisions. 181 /// Saves a raw dump to the transferable file. Checks for collisions.
181 void SaveRaw(const ShaderDiskCacheRaw& entry); 182 void SaveRaw(const ShaderDiskCacheRaw& entry);
@@ -190,18 +191,21 @@ public:
190 /// Saves a dump entry to the precompiled file. Does not check for collisions. 191 /// Saves a dump entry to the precompiled file. Does not check for collisions.
191 void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); 192 void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program);
192 193
194 /// Serializes virtual precompiled shader cache file to real file
195 void SaveVirtualPrecompiledFile();
196
193private: 197private:
194 /// Loads the transferable cache. Returns empty on failure. 198 /// Loads the transferable cache. Returns empty on failure.
195 std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, 199 std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
196 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> 200 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
197 LoadPrecompiledFile(FileUtil::IOFile& file); 201 LoadPrecompiledFile(FileUtil::IOFile& file);
198 202
199 /// Loads a decompiled cache entry from the passed file. Returns empty on failure. 203 /// Loads a decompiled cache entry from m_precompiled_cache_virtual_file. Returns empty on
200 std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(FileUtil::IOFile& file); 204 /// failure.
205 std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry();
201 206
202 /// Saves a decompiled entry to the passed file. Returns true on success. 207 /// Saves a decompiled entry to the passed file. Returns true on success.
203 bool SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, const std::string& code, 208 bool SaveDecompiledFile(u64 unique_identifier, const std::string& code,
204 const std::vector<u8>& compressed_code,
205 const GLShader::ShaderEntries& entries); 209 const GLShader::ShaderEntries& entries);
206 210
207 /// Returns if the cache can be used 211 /// Returns if the cache can be used
@@ -210,8 +214,8 @@ private:
210 /// Opens current game's transferable file and write it's header if it doesn't exist 214 /// Opens current game's transferable file and write it's header if it doesn't exist
211 FileUtil::IOFile AppendTransferableFile() const; 215 FileUtil::IOFile AppendTransferableFile() const;
212 216
213 /// Opens current game's precompiled file and write it's header if it doesn't exist 217 /// Save precompiled header to precompiled_cache_in_memory
214 FileUtil::IOFile AppendPrecompiledFile() const; 218 void SavePrecompiledHeaderToVirtualPrecompiledCache();
215 219
216 /// Create shader disk cache directories. Returns true on success. 220 /// Create shader disk cache directories. Returns true on success.
217 bool EnsureDirectories() const; 221 bool EnsureDirectories() const;
@@ -234,10 +238,42 @@ private:
234 /// Get current game's title id 238 /// Get current game's title id
235 std::string GetTitleID() const; 239 std::string GetTitleID() const;
236 240
241 template <typename T>
242 bool SaveArrayToPrecompiled(const T* data, std::size_t length) {
243 const std::size_t write_length = precompiled_cache_virtual_file.WriteArray(
244 data, length, precompiled_cache_virtual_file_offset);
245 precompiled_cache_virtual_file_offset += write_length;
246 return write_length == sizeof(T) * length;
247 }
248
249 template <typename T>
250 bool LoadArrayFromPrecompiled(T* data, std::size_t length) {
251 const std::size_t read_length = precompiled_cache_virtual_file.ReadArray(
252 data, length, precompiled_cache_virtual_file_offset);
253 precompiled_cache_virtual_file_offset += read_length;
254 return read_length == sizeof(T) * length;
255 }
256
257 template <typename T>
258 bool SaveObjectToPrecompiled(const T& object) {
259 return SaveArrayToPrecompiled(&object, 1);
260 }
261
262 template <typename T>
263 bool LoadObjectFromPrecompiled(T& object) {
264 return LoadArrayFromPrecompiled(&object, 1);
265 }
266
237 // Copre system 267 // Copre system
238 Core::System& system; 268 Core::System& system;
239 // Stored transferable shaders 269 // Stored transferable shaders
240 std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; 270 std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
271 // Stores whole precompiled cache which will be read from or saved to the precompiled chache
272 // file
273 FileSys::VectorVfsFile precompiled_cache_virtual_file;
274 // Stores the current offset of the precompiled cache file for IO purposes
275 std::size_t precompiled_cache_virtual_file_offset;
276
241 // The cache has been loaded at boot 277 // The cache has been loaded at boot
242 bool tried_to_load{}; 278 bool tried_to_load{};
243}; 279};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 8763d9c71..6abf948f8 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -16,7 +16,7 @@ using VideoCommon::Shader::ShaderIR;
16 16
17static constexpr u32 PROGRAM_OFFSET{10}; 17static constexpr u32 PROGRAM_OFFSET{10};
18 18
19ProgramResult GenerateVertexShader(const ShaderSetup& setup) { 19ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) {
20 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); 20 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
21 21
22 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; 22 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
@@ -34,14 +34,15 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
34 34
35)"; 35)";
36 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); 36 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
37 ProgramResult program = Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); 37 ProgramResult program =
38 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex");
38 39
39 out += program.first; 40 out += program.first;
40 41
41 if (setup.IsDualProgram()) { 42 if (setup.IsDualProgram()) {
42 ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET); 43 ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET);
43 ProgramResult program_b = 44 ProgramResult program_b =
44 Decompile(program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); 45 Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b");
45 46
46 out += program_b.first; 47 out += program_b.first;
47 } 48 }
@@ -57,6 +58,9 @@ void main() {
57 } 58 }
58 59
59 out += R"( 60 out += R"(
61
62 // Set Position Y direction
63 position.y *= utof(config_pack[2]);
60 // Check if the flip stage is VertexB 64 // Check if the flip stage is VertexB
61 // Config pack's second value is flip_stage 65 // Config pack's second value is flip_stage
62 if (config_pack[1] == 1) { 66 if (config_pack[1] == 1) {
@@ -75,7 +79,7 @@ void main() {
75 return {out, program.second}; 79 return {out, program.second};
76} 80}
77 81
78ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { 82ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) {
79 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); 83 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
80 84
81 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; 85 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
@@ -95,7 +99,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
95)"; 99)";
96 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); 100 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
97 ProgramResult program = 101 ProgramResult program =
98 Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); 102 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry");
99 out += program.first; 103 out += program.first;
100 104
101 out += R"( 105 out += R"(
@@ -106,7 +110,7 @@ void main() {
106 return {out, program.second}; 110 return {out, program.second};
107} 111}
108 112
109ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { 113ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) {
110 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); 114 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
111 115
112 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; 116 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
@@ -158,7 +162,7 @@ bool AlphaFunc(in float value) {
158)"; 162)";
159 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); 163 ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
160 ProgramResult program = 164 ProgramResult program =
161 Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); 165 Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment");
162 166
163 out += program.first; 167 out += program.first;
164 168
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index fad346b48..0536c8a03 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -10,6 +10,10 @@
10#include "video_core/renderer_opengl/gl_shader_decompiler.h" 10#include "video_core/renderer_opengl/gl_shader_decompiler.h"
11#include "video_core/shader/shader_ir.h" 11#include "video_core/shader/shader_ir.h"
12 12
13namespace OpenGL {
14class Device;
15}
16
13namespace OpenGL::GLShader { 17namespace OpenGL::GLShader {
14 18
15using VideoCommon::Shader::ProgramCode; 19using VideoCommon::Shader::ProgramCode;
@@ -39,22 +43,13 @@ private:
39 bool has_program_b{}; 43 bool has_program_b{};
40}; 44};
41 45
42/** 46/// Generates the GLSL vertex shader program source code for the given VS program
43 * Generates the GLSL vertex shader program source code for the given VS program 47ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup);
44 * @returns String of the shader source code 48
45 */ 49/// Generates the GLSL geometry shader program source code for the given GS program
46ProgramResult GenerateVertexShader(const ShaderSetup& setup); 50ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup);
47 51
48/** 52/// Generates the GLSL fragment shader program source code for the given FS program
49 * Generates the GLSL geometry shader program source code for the given GS program 53ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup);
50 * @returns String of the shader source code
51 */
52ProgramResult GenerateGeometryShader(const ShaderSetup& setup);
53
54/**
55 * Generates the GLSL fragment shader program source code for the given FS program
56 * @returns String of the shader source code
57 */
58ProgramResult GenerateFragmentShader(const ShaderSetup& setup);
59 54
60} // namespace OpenGL::GLShader 55} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 52d569a1b..7425fbe5d 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -471,8 +471,9 @@ void OpenGLState::ApplyTextures() const {
471 const auto& texture_unit = texture_units[i]; 471 const auto& texture_unit = texture_units[i];
472 auto& cur_state_texture_unit = cur_state.texture_units[i]; 472 auto& cur_state_texture_unit = cur_state.texture_units[i];
473 textures[i] = texture_unit.texture; 473 textures[i] = texture_unit.texture;
474 if (cur_state_texture_unit.texture == textures[i]) 474 if (cur_state_texture_unit.texture == textures[i]) {
475 continue; 475 continue;
476 }
476 cur_state_texture_unit.texture = textures[i]; 477 cur_state_texture_unit.texture = textures[i];
477 if (!has_delta) { 478 if (!has_delta) {
478 first = i; 479 first = i;
@@ -493,10 +494,11 @@ void OpenGLState::ApplySamplers() const {
493 std::array<GLuint, Maxwell::NumTextureSamplers> samplers; 494 std::array<GLuint, Maxwell::NumTextureSamplers> samplers;
494 495
495 for (std::size_t i = 0; i < std::size(samplers); ++i) { 496 for (std::size_t i = 0; i < std::size(samplers); ++i) {
496 if (cur_state.texture_units[i].sampler == texture_units[i].sampler) 497 samplers[i] = texture_units[i].sampler;
498 if (cur_state.texture_units[i].sampler == texture_units[i].sampler) {
497 continue; 499 continue;
500 }
498 cur_state.texture_units[i].sampler = texture_units[i].sampler; 501 cur_state.texture_units[i].sampler = texture_units[i].sampler;
499 samplers[i] = texture_units[i].sampler;
500 if (!has_delta) { 502 if (!has_delta) {
501 first = i; 503 first = i;
502 has_delta = true; 504 has_delta = true;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index a8833c06e..95b773135 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -27,8 +27,7 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
27inline GLenum VertexType(Maxwell::VertexAttribute attrib) { 27inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
28 switch (attrib.type) { 28 switch (attrib.type) {
29 case Maxwell::VertexAttribute::Type::UnsignedInt: 29 case Maxwell::VertexAttribute::Type::UnsignedInt:
30 case Maxwell::VertexAttribute::Type::UnsignedNorm: { 30 case Maxwell::VertexAttribute::Type::UnsignedNorm:
31
32 switch (attrib.size) { 31 switch (attrib.size) {
33 case Maxwell::VertexAttribute::Size::Size_8: 32 case Maxwell::VertexAttribute::Size::Size_8:
34 case Maxwell::VertexAttribute::Size::Size_8_8: 33 case Maxwell::VertexAttribute::Size::Size_8_8:
@@ -47,16 +46,13 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
47 return GL_UNSIGNED_INT; 46 return GL_UNSIGNED_INT;
48 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 47 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
49 return GL_UNSIGNED_INT_2_10_10_10_REV; 48 return GL_UNSIGNED_INT_2_10_10_10_REV;
49 default:
50 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
51 UNREACHABLE();
52 return {};
50 } 53 }
51
52 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
53 UNREACHABLE();
54 return {};
55 }
56
57 case Maxwell::VertexAttribute::Type::SignedInt: 54 case Maxwell::VertexAttribute::Type::SignedInt:
58 case Maxwell::VertexAttribute::Type::SignedNorm: { 55 case Maxwell::VertexAttribute::Type::SignedNorm:
59
60 switch (attrib.size) { 56 switch (attrib.size) {
61 case Maxwell::VertexAttribute::Size::Size_8: 57 case Maxwell::VertexAttribute::Size::Size_8:
62 case Maxwell::VertexAttribute::Size::Size_8_8: 58 case Maxwell::VertexAttribute::Size::Size_8_8:
@@ -75,14 +71,12 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
75 return GL_INT; 71 return GL_INT;
76 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 72 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
77 return GL_INT_2_10_10_10_REV; 73 return GL_INT_2_10_10_10_REV;
74 default:
75 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
76 UNREACHABLE();
77 return {};
78 } 78 }
79 79 case Maxwell::VertexAttribute::Type::Float:
80 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
81 UNREACHABLE();
82 return {};
83 }
84
85 case Maxwell::VertexAttribute::Type::Float: {
86 switch (attrib.size) { 80 switch (attrib.size) {
87 case Maxwell::VertexAttribute::Size::Size_16: 81 case Maxwell::VertexAttribute::Size::Size_16:
88 case Maxwell::VertexAttribute::Size::Size_16_16: 82 case Maxwell::VertexAttribute::Size::Size_16_16:
@@ -94,13 +88,16 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
94 case Maxwell::VertexAttribute::Size::Size_32_32_32: 88 case Maxwell::VertexAttribute::Size::Size_32_32_32:
95 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 89 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
96 return GL_FLOAT; 90 return GL_FLOAT;
91 default:
92 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
93 UNREACHABLE();
94 return {};
97 } 95 }
96 default:
97 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
98 UNREACHABLE();
99 return {};
98 } 100 }
99 }
100
101 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
102 UNREACHABLE();
103 return {};
104} 101}
105 102
106inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { 103inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
@@ -129,10 +126,11 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
129 return GL_TRIANGLES; 126 return GL_TRIANGLES;
130 case Maxwell::PrimitiveTopology::TriangleStrip: 127 case Maxwell::PrimitiveTopology::TriangleStrip:
131 return GL_TRIANGLE_STRIP; 128 return GL_TRIANGLE_STRIP;
129 default:
130 LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology));
131 UNREACHABLE();
132 return {};
132 } 133 }
133 LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology));
134 UNREACHABLE();
135 return {};
136} 134}
137 135
138inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, 136inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
@@ -186,9 +184,10 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
186 } else { 184 } else {
187 return GL_MIRROR_CLAMP_TO_EDGE; 185 return GL_MIRROR_CLAMP_TO_EDGE;
188 } 186 }
187 default:
188 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
189 return GL_REPEAT;
189 } 190 }
190 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
191 return GL_REPEAT;
192} 191}
193 192
194inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { 193inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 34bf26ff2..9fe1e3280 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -62,9 +62,10 @@ vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode) {
62 case Tegra::Texture::WrapMode::MirrorOnceBorder: 62 case Tegra::Texture::WrapMode::MirrorOnceBorder:
63 UNIMPLEMENTED(); 63 UNIMPLEMENTED();
64 return vk::SamplerAddressMode::eMirrorClampToEdge; 64 return vk::SamplerAddressMode::eMirrorClampToEdge;
65 default:
66 UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
67 return {};
65 } 68 }
66 UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
67 return {};
68} 69}
69 70
70vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { 71vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) {
@@ -225,9 +226,10 @@ vk::PrimitiveTopology PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
225 return vk::PrimitiveTopology::eTriangleList; 226 return vk::PrimitiveTopology::eTriangleList;
226 case Maxwell::PrimitiveTopology::TriangleStrip: 227 case Maxwell::PrimitiveTopology::TriangleStrip:
227 return vk::PrimitiveTopology::eTriangleStrip; 228 return vk::PrimitiveTopology::eTriangleStrip;
229 default:
230 UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
231 return {};
228 } 232 }
229 UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
230 return {};
231} 233}
232 234
233vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { 235vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
index ed3178f09..801826d3d 100644
--- a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
@@ -7,7 +7,6 @@
7#include <unordered_map> 7#include <unordered_map>
8 8
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/cityhash.h"
11#include "video_core/renderer_vulkan/declarations.h" 10#include "video_core/renderer_vulkan/declarations.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h" 11#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/vk_sampler_cache.h" 12#include "video_core/renderer_vulkan/vk_sampler_cache.h"
@@ -28,39 +27,20 @@ static std::optional<vk::BorderColor> TryConvertBorderColor(std::array<float, 4>
28 } 27 }
29} 28}
30 29
31std::size_t SamplerCacheKey::Hash() const {
32 static_assert(sizeof(raw) % sizeof(u64) == 0);
33 return static_cast<std::size_t>(
34 Common::CityHash64(reinterpret_cast<const char*>(raw.data()), sizeof(raw) / sizeof(u64)));
35}
36
37bool SamplerCacheKey::operator==(const SamplerCacheKey& rhs) const {
38 return raw == rhs.raw;
39}
40
41VKSamplerCache::VKSamplerCache(const VKDevice& device) : device{device} {} 30VKSamplerCache::VKSamplerCache(const VKDevice& device) : device{device} {}
42 31
43VKSamplerCache::~VKSamplerCache() = default; 32VKSamplerCache::~VKSamplerCache() = default;
44 33
45vk::Sampler VKSamplerCache::GetSampler(const Tegra::Texture::TSCEntry& tsc) { 34UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) const {
46 const auto [entry, is_cache_miss] = cache.try_emplace(SamplerCacheKey{tsc}); 35 const float max_anisotropy{tsc.GetMaxAnisotropy()};
47 auto& sampler = entry->second; 36 const bool has_anisotropy{max_anisotropy > 1.0f};
48 if (is_cache_miss) {
49 sampler = CreateSampler(tsc);
50 }
51 return *sampler;
52}
53
54UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) {
55 const float max_anisotropy = tsc.GetMaxAnisotropy();
56 const bool has_anisotropy = max_anisotropy > 1.0f;
57 37
58 const auto border_color = tsc.GetBorderColor(); 38 const auto border_color{tsc.GetBorderColor()};
59 const auto vk_border_color = TryConvertBorderColor(border_color); 39 const auto vk_border_color{TryConvertBorderColor(border_color)};
60 UNIMPLEMENTED_IF_MSG(!vk_border_color, "Unimplemented border color {} {} {} {}", 40 UNIMPLEMENTED_IF_MSG(!vk_border_color, "Unimplemented border color {} {} {} {}",
61 border_color[0], border_color[1], border_color[2], border_color[3]); 41 border_color[0], border_color[1], border_color[2], border_color[3]);
62 42
63 constexpr bool unnormalized_coords = false; 43 constexpr bool unnormalized_coords{false};
64 44
65 const vk::SamplerCreateInfo sampler_ci( 45 const vk::SamplerCreateInfo sampler_ci(
66 {}, MaxwellToVK::Sampler::Filter(tsc.mag_filter), 46 {}, MaxwellToVK::Sampler::Filter(tsc.mag_filter),
@@ -73,9 +53,13 @@ UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc)
73 tsc.GetMaxLod(), vk_border_color.value_or(vk::BorderColor::eFloatTransparentBlack), 53 tsc.GetMaxLod(), vk_border_color.value_or(vk::BorderColor::eFloatTransparentBlack),
74 unnormalized_coords); 54 unnormalized_coords);
75 55
76 const auto& dld = device.GetDispatchLoader(); 56 const auto& dld{device.GetDispatchLoader()};
77 const auto dev = device.GetLogical(); 57 const auto dev{device.GetLogical()};
78 return dev.createSamplerUnique(sampler_ci, nullptr, dld); 58 return dev.createSamplerUnique(sampler_ci, nullptr, dld);
79} 59}
80 60
61vk::Sampler VKSamplerCache::ToSamplerType(const UniqueSampler& sampler) const {
62 return *sampler;
63}
64
81} // namespace Vulkan 65} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.h b/src/video_core/renderer_vulkan/vk_sampler_cache.h
index c6394dc87..771b05c73 100644
--- a/src/video_core/renderer_vulkan/vk_sampler_cache.h
+++ b/src/video_core/renderer_vulkan/vk_sampler_cache.h
@@ -8,49 +8,25 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/declarations.h" 10#include "video_core/renderer_vulkan/declarations.h"
11#include "video_core/sampler_cache.h"
11#include "video_core/textures/texture.h" 12#include "video_core/textures/texture.h"
12 13
13namespace Vulkan { 14namespace Vulkan {
14 15
15class VKDevice; 16class VKDevice;
16 17
17struct SamplerCacheKey final : public Tegra::Texture::TSCEntry { 18class VKSamplerCache final : public VideoCommon::SamplerCache<vk::Sampler, UniqueSampler> {
18 std::size_t Hash() const;
19
20 bool operator==(const SamplerCacheKey& rhs) const;
21
22 bool operator!=(const SamplerCacheKey& rhs) const {
23 return !operator==(rhs);
24 }
25};
26
27} // namespace Vulkan
28
29namespace std {
30
31template <>
32struct hash<Vulkan::SamplerCacheKey> {
33 std::size_t operator()(const Vulkan::SamplerCacheKey& k) const noexcept {
34 return k.Hash();
35 }
36};
37
38} // namespace std
39
40namespace Vulkan {
41
42class VKSamplerCache {
43public: 19public:
44 explicit VKSamplerCache(const VKDevice& device); 20 explicit VKSamplerCache(const VKDevice& device);
45 ~VKSamplerCache(); 21 ~VKSamplerCache();
46 22
47 vk::Sampler GetSampler(const Tegra::Texture::TSCEntry& tsc); 23protected:
24 UniqueSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc) const;
48 25
49private: 26 vk::Sampler ToSamplerType(const UniqueSampler& sampler) const;
50 UniqueSampler CreateSampler(const Tegra::Texture::TSCEntry& tsc);
51 27
28private:
52 const VKDevice& device; 29 const VKDevice& device;
53 std::unordered_map<SamplerCacheKey, UniqueSampler> cache;
54}; 30};
55 31
56} // namespace Vulkan 32} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index e0a6f5e87..23d9b10db 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -76,14 +76,10 @@ constexpr u32 GetGenericAttributeLocation(Attribute::Index attribute) {
76 76
77/// Returns true if an object has to be treated as precise 77/// Returns true if an object has to be treated as precise
78bool IsPrecise(Operation operand) { 78bool IsPrecise(Operation operand) {
79 const auto& meta = operand.GetMeta(); 79 const auto& meta{operand.GetMeta()};
80
81 if (std::holds_alternative<MetaArithmetic>(meta)) { 80 if (std::holds_alternative<MetaArithmetic>(meta)) {
82 return std::get<MetaArithmetic>(meta).precise; 81 return std::get<MetaArithmetic>(meta).precise;
83 } 82 }
84 if (std::holds_alternative<MetaHalfArithmetic>(meta)) {
85 return std::get<MetaHalfArithmetic>(meta).precise;
86 }
87 return false; 83 return false;
88} 84}
89 85
@@ -191,8 +187,9 @@ public:
191 for (const auto& cbuf : ir.GetConstantBuffers()) { 187 for (const auto& cbuf : ir.GetConstantBuffers()) {
192 entries.const_buffers.emplace_back(cbuf.second, cbuf.first); 188 entries.const_buffers.emplace_back(cbuf.second, cbuf.first);
193 } 189 }
194 for (const auto& gmem : ir.GetGlobalMemoryBases()) { 190 for (const auto& gmem_pair : ir.GetGlobalMemory()) {
195 entries.global_buffers.emplace_back(gmem.cbuf_index, gmem.cbuf_offset); 191 const auto& [base, usage] = gmem_pair;
192 entries.global_buffers.emplace_back(base.cbuf_index, base.cbuf_offset);
196 } 193 }
197 for (const auto& sampler : ir.GetSamplers()) { 194 for (const auto& sampler : ir.GetSamplers()) {
198 entries.samplers.emplace_back(sampler); 195 entries.samplers.emplace_back(sampler);
@@ -225,7 +222,7 @@ private:
225 return current_binding; 222 return current_binding;
226 }; 223 };
227 const_buffers_base_binding = Allocate(ir.GetConstantBuffers().size()); 224 const_buffers_base_binding = Allocate(ir.GetConstantBuffers().size());
228 global_buffers_base_binding = Allocate(ir.GetGlobalMemoryBases().size()); 225 global_buffers_base_binding = Allocate(ir.GetGlobalMemory().size());
229 samplers_base_binding = Allocate(ir.GetSamplers().size()); 226 samplers_base_binding = Allocate(ir.GetSamplers().size());
230 227
231 ASSERT_MSG(binding_iterator - binding_base < STAGE_BINDING_STRIDE, 228 ASSERT_MSG(binding_iterator - binding_base < STAGE_BINDING_STRIDE,
@@ -390,14 +387,15 @@ private:
390 387
391 void DeclareGlobalBuffers() { 388 void DeclareGlobalBuffers() {
392 u32 binding = global_buffers_base_binding; 389 u32 binding = global_buffers_base_binding;
393 for (const auto& entry : ir.GetGlobalMemoryBases()) { 390 for (const auto& entry : ir.GetGlobalMemory()) {
391 const auto [base, usage] = entry;
394 const Id id = OpVariable(t_gmem_ssbo, spv::StorageClass::StorageBuffer); 392 const Id id = OpVariable(t_gmem_ssbo, spv::StorageClass::StorageBuffer);
395 AddGlobalVariable( 393 AddGlobalVariable(
396 Name(id, fmt::format("gmem_{}_{}", entry.cbuf_index, entry.cbuf_offset))); 394 Name(id, fmt::format("gmem_{}_{}", base.cbuf_index, base.cbuf_offset)));
397 395
398 Decorate(id, spv::Decoration::Binding, binding++); 396 Decorate(id, spv::Decoration::Binding, binding++);
399 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); 397 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
400 global_buffers.emplace(entry, id); 398 global_buffers.emplace(base, id);
401 } 399 }
402 } 400 }
403 401
@@ -744,6 +742,16 @@ private:
744 return {}; 742 return {};
745 } 743 }
746 744
745 Id HClamp(Operation operation) {
746 UNIMPLEMENTED();
747 return {};
748 }
749
750 Id HUnpack(Operation operation) {
751 UNIMPLEMENTED();
752 return {};
753 }
754
747 Id HMergeF32(Operation operation) { 755 Id HMergeF32(Operation operation) {
748 UNIMPLEMENTED(); 756 UNIMPLEMENTED();
749 return {}; 757 return {};
@@ -1216,6 +1224,8 @@ private:
1216 &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>, 1224 &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>,
1217 &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>, 1225 &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>,
1218 &SPIRVDecompiler::HNegate, 1226 &SPIRVDecompiler::HNegate,
1227 &SPIRVDecompiler::HClamp,
1228 &SPIRVDecompiler::HUnpack,
1219 &SPIRVDecompiler::HMergeF32, 1229 &SPIRVDecompiler::HMergeF32,
1220 &SPIRVDecompiler::HMergeH0, 1230 &SPIRVDecompiler::HMergeH0,
1221 &SPIRVDecompiler::HMergeH1, 1231 &SPIRVDecompiler::HMergeH1,
@@ -1258,6 +1268,13 @@ private:
1258 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>, 1268 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>,
1259 &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>, 1269 &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>,
1260 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>, 1270 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>,
1271 // TODO(Rodrigo): Should these use the OpFUnord* variants?
1272 &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>,
1273 &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>,
1274 &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>,
1275 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>,
1276 &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>,
1277 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>,
1261 1278
1262 &SPIRVDecompiler::Texture, 1279 &SPIRVDecompiler::Texture,
1263 &SPIRVDecompiler::TextureLod, 1280 &SPIRVDecompiler::TextureLod,
diff --git a/src/video_core/sampler_cache.cpp b/src/video_core/sampler_cache.cpp
new file mode 100644
index 000000000..53c7ef12d
--- /dev/null
+++ b/src/video_core/sampler_cache.cpp
@@ -0,0 +1,21 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/cityhash.h"
6#include "common/common_types.h"
7#include "video_core/sampler_cache.h"
8
9namespace VideoCommon {
10
11std::size_t SamplerCacheKey::Hash() const {
12 static_assert(sizeof(raw) % sizeof(u64) == 0);
13 return static_cast<std::size_t>(
14 Common::CityHash64(reinterpret_cast<const char*>(raw.data()), sizeof(raw) / sizeof(u64)));
15}
16
17bool SamplerCacheKey::operator==(const SamplerCacheKey& rhs) const {
18 return raw == rhs.raw;
19}
20
21} // namespace VideoCommon
diff --git a/src/video_core/sampler_cache.h b/src/video_core/sampler_cache.h
new file mode 100644
index 000000000..cbe3ad071
--- /dev/null
+++ b/src/video_core/sampler_cache.h
@@ -0,0 +1,60 @@
1// Copyright 2019 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 <cstddef>
8#include <unordered_map>
9
10#include "video_core/textures/texture.h"
11
12namespace VideoCommon {
13
14struct SamplerCacheKey final : public Tegra::Texture::TSCEntry {
15 std::size_t Hash() const;
16
17 bool operator==(const SamplerCacheKey& rhs) const;
18
19 bool operator!=(const SamplerCacheKey& rhs) const {
20 return !operator==(rhs);
21 }
22};
23
24} // namespace VideoCommon
25
26namespace std {
27
28template <>
29struct hash<VideoCommon::SamplerCacheKey> {
30 std::size_t operator()(const VideoCommon::SamplerCacheKey& k) const noexcept {
31 return k.Hash();
32 }
33};
34
35} // namespace std
36
37namespace VideoCommon {
38
39template <typename SamplerType, typename SamplerStorageType>
40class SamplerCache {
41public:
42 SamplerType GetSampler(const Tegra::Texture::TSCEntry& tsc) {
43 const auto [entry, is_cache_miss] = cache.try_emplace(SamplerCacheKey{tsc});
44 auto& sampler = entry->second;
45 if (is_cache_miss) {
46 sampler = CreateSampler(tsc);
47 }
48 return ToSamplerType(sampler);
49 }
50
51protected:
52 virtual SamplerStorageType CreateSampler(const Tegra::Texture::TSCEntry& tsc) const = 0;
53
54 virtual SamplerType ToSamplerType(const SamplerStorageType& sampler) const = 0;
55
56private:
57 std::unordered_map<SamplerCacheKey, SamplerStorageType> cache;
58};
59
60} // namespace VideoCommon \ No newline at end of file
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index e4c438792..2da595c0d 100644
--- a/src/video_core/shader/decode.cpp
+++ b/src/video_core/shader/decode.cpp
@@ -116,6 +116,8 @@ ExitMethod ShaderIR::Scan(u32 begin, u32 end, std::set<u32>& labels) {
116 // Continue scanning for an exit method. 116 // Continue scanning for an exit method.
117 break; 117 break;
118 } 118 }
119 default:
120 break;
119 } 121 }
120 } 122 }
121 return exit_method = ExitMethod::AlwaysReturn; 123 return exit_method = ExitMethod::AlwaysReturn;
@@ -206,4 +208,4 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) {
206 return pc + 1; 208 return pc + 1;
207} 209}
208 210
209} // namespace VideoCommon::Shader \ No newline at end of file 211} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp
index baee89107..2098c1170 100644
--- a/src/video_core/shader/decode/arithmetic_half.cpp
+++ b/src/video_core/shader/decode/arithmetic_half.cpp
@@ -9,6 +9,7 @@
9 9
10namespace VideoCommon::Shader { 10namespace VideoCommon::Shader {
11 11
12using Tegra::Shader::HalfType;
12using Tegra::Shader::Instruction; 13using Tegra::Shader::Instruction;
13using Tegra::Shader::OpCode; 14using Tegra::Shader::OpCode;
14 15
@@ -18,48 +19,50 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
18 19
19 if (opcode->get().GetId() == OpCode::Id::HADD2_C || 20 if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
20 opcode->get().GetId() == OpCode::Id::HADD2_R) { 21 opcode->get().GetId() == OpCode::Id::HADD2_R) {
21 UNIMPLEMENTED_IF(instr.alu_half.ftz != 0); 22 if (instr.alu_half.ftz != 0) {
23 LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
24 }
22 } 25 }
23 UNIMPLEMENTED_IF_MSG(instr.alu_half.saturate != 0, "Half float saturation not implemented");
24 26
25 const bool negate_a = 27 const bool negate_a =
26 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; 28 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
27 const bool negate_b = 29 const bool negate_b =
28 opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; 30 opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
29 31
30 const Node op_a = GetOperandAbsNegHalf(GetRegister(instr.gpr8), instr.alu_half.abs_a, negate_a); 32 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a);
31 33 op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a);
32 // instr.alu_half.type_a
33 34
34 Node op_b = [&]() { 35 auto [type_b, op_b] = [&]() -> std::tuple<HalfType, Node> {
35 switch (opcode->get().GetId()) { 36 switch (opcode->get().GetId()) {
36 case OpCode::Id::HADD2_C: 37 case OpCode::Id::HADD2_C:
37 case OpCode::Id::HMUL2_C: 38 case OpCode::Id::HMUL2_C:
38 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); 39 return {HalfType::F32, GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
39 case OpCode::Id::HADD2_R: 40 case OpCode::Id::HADD2_R:
40 case OpCode::Id::HMUL2_R: 41 case OpCode::Id::HMUL2_R:
41 return GetRegister(instr.gpr20); 42 return {instr.alu_half.type_b, GetRegister(instr.gpr20)};
42 default: 43 default:
43 UNREACHABLE(); 44 UNREACHABLE();
44 return Immediate(0); 45 return {HalfType::F32, Immediate(0)};
45 } 46 }
46 }(); 47 }();
47 op_b = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b); 48 op_b = UnpackHalfFloat(op_b, type_b);
49 // redeclaration to avoid a bug in clang with reusing local bindings in lambdas
50 Node op_b_alt = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b);
48 51
49 Node value = [&]() { 52 Node value = [&]() {
50 MetaHalfArithmetic meta{true, {instr.alu_half_imm.type_a, instr.alu_half.type_b}};
51 switch (opcode->get().GetId()) { 53 switch (opcode->get().GetId()) {
52 case OpCode::Id::HADD2_C: 54 case OpCode::Id::HADD2_C:
53 case OpCode::Id::HADD2_R: 55 case OpCode::Id::HADD2_R:
54 return Operation(OperationCode::HAdd, meta, op_a, op_b); 56 return Operation(OperationCode::HAdd, PRECISE, op_a, op_b_alt);
55 case OpCode::Id::HMUL2_C: 57 case OpCode::Id::HMUL2_C:
56 case OpCode::Id::HMUL2_R: 58 case OpCode::Id::HMUL2_R:
57 return Operation(OperationCode::HMul, meta, op_a, op_b); 59 return Operation(OperationCode::HMul, PRECISE, op_a, op_b_alt);
58 default: 60 default:
59 UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); 61 UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName());
60 return Immediate(0); 62 return Immediate(0);
61 } 63 }
62 }(); 64 }();
65 value = GetSaturatedHalfFloat(value, instr.alu_half.saturate);
63 value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half.merge); 66 value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half.merge);
64 67
65 SetRegister(bb, instr.gpr0, value); 68 SetRegister(bb, instr.gpr0, value);
@@ -67,4 +70,4 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
67 return pc; 70 return pc;
68} 71}
69 72
70} // namespace VideoCommon::Shader \ No newline at end of file 73} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/arithmetic_half_immediate.cpp b/src/video_core/shader/decode/arithmetic_half_immediate.cpp
index c2164ba50..fbcd35b18 100644
--- a/src/video_core/shader/decode/arithmetic_half_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_half_immediate.cpp
@@ -17,34 +17,33 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) {
17 const auto opcode = OpCode::Decode(instr); 17 const auto opcode = OpCode::Decode(instr);
18 18
19 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { 19 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
20 UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0); 20 if (instr.alu_half_imm.ftz != 0) {
21 LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
22 }
21 } else { 23 } else {
22 UNIMPLEMENTED_IF(instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None); 24 UNIMPLEMENTED_IF(instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None);
23 } 25 }
24 UNIMPLEMENTED_IF_MSG(instr.alu_half_imm.saturate != 0,
25 "Half float immediate saturation not implemented");
26 26
27 Node op_a = GetRegister(instr.gpr8); 27 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half_imm.type_a);
28 op_a = GetOperandAbsNegHalf(op_a, instr.alu_half_imm.abs_a, instr.alu_half_imm.negate_a); 28 op_a = GetOperandAbsNegHalf(op_a, instr.alu_half_imm.abs_a, instr.alu_half_imm.negate_a);
29 29
30 const Node op_b = UnpackHalfImmediate(instr, true); 30 const Node op_b = UnpackHalfImmediate(instr, true);
31 31
32 Node value = [&]() { 32 Node value = [&]() {
33 MetaHalfArithmetic meta{true, {instr.alu_half_imm.type_a}};
34 switch (opcode->get().GetId()) { 33 switch (opcode->get().GetId()) {
35 case OpCode::Id::HADD2_IMM: 34 case OpCode::Id::HADD2_IMM:
36 return Operation(OperationCode::HAdd, meta, op_a, op_b); 35 return Operation(OperationCode::HAdd, PRECISE, op_a, op_b);
37 case OpCode::Id::HMUL2_IMM: 36 case OpCode::Id::HMUL2_IMM:
38 return Operation(OperationCode::HMul, meta, op_a, op_b); 37 return Operation(OperationCode::HMul, PRECISE, op_a, op_b);
39 default: 38 default:
40 UNREACHABLE(); 39 UNREACHABLE();
41 return Immediate(0); 40 return Immediate(0);
42 } 41 }
43 }(); 42 }();
44 value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half_imm.merge);
45 43
44 value = GetSaturatedHalfFloat(value, instr.alu_half_imm.saturate);
45 value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half_imm.merge);
46 SetRegister(bb, instr.gpr0, value); 46 SetRegister(bb, instr.gpr0, value);
47
48 return pc; 47 return pc;
49} 48}
50 49
diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp
index 55a6fbbf2..b5ec9a6f5 100644
--- a/src/video_core/shader/decode/conversion.cpp
+++ b/src/video_core/shader/decode/conversion.cpp
@@ -18,13 +18,29 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
18 const auto opcode = OpCode::Decode(instr); 18 const auto opcode = OpCode::Decode(instr);
19 19
20 switch (opcode->get().GetId()) { 20 switch (opcode->get().GetId()) {
21 case OpCode::Id::I2I_R: { 21 case OpCode::Id::I2I_R:
22 case OpCode::Id::I2I_C:
23 case OpCode::Id::I2I_IMM: {
22 UNIMPLEMENTED_IF(instr.conversion.selector); 24 UNIMPLEMENTED_IF(instr.conversion.selector);
25 UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word);
26 UNIMPLEMENTED_IF(instr.alu.saturate_d);
23 27
24 const bool input_signed = instr.conversion.is_input_signed; 28 const bool input_signed = instr.conversion.is_input_signed;
25 const bool output_signed = instr.conversion.is_output_signed; 29 const bool output_signed = instr.conversion.is_output_signed;
26 30
27 Node value = GetRegister(instr.gpr20); 31 Node value = [&]() {
32 switch (opcode->get().GetId()) {
33 case OpCode::Id::I2I_R:
34 return GetRegister(instr.gpr20);
35 case OpCode::Id::I2I_C:
36 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
37 case OpCode::Id::I2I_IMM:
38 return Immediate(instr.alu.GetSignedImm20_20());
39 default:
40 UNREACHABLE();
41 return Immediate(0);
42 }
43 }();
28 value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); 44 value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed);
29 45
30 value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a, 46 value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a,
@@ -38,17 +54,24 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
38 break; 54 break;
39 } 55 }
40 case OpCode::Id::I2F_R: 56 case OpCode::Id::I2F_R:
41 case OpCode::Id::I2F_C: { 57 case OpCode::Id::I2F_C:
42 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); 58 case OpCode::Id::I2F_IMM: {
59 UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word);
43 UNIMPLEMENTED_IF(instr.conversion.selector); 60 UNIMPLEMENTED_IF(instr.conversion.selector);
44 UNIMPLEMENTED_IF_MSG(instr.generates_cc, 61 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
45 "Condition codes generation in I2F is not implemented"); 62 "Condition codes generation in I2F is not implemented");
46 63
47 Node value = [&]() { 64 Node value = [&]() {
48 if (instr.is_b_gpr) { 65 switch (opcode->get().GetId()) {
66 case OpCode::Id::I2F_R:
49 return GetRegister(instr.gpr20); 67 return GetRegister(instr.gpr20);
50 } else { 68 case OpCode::Id::I2F_C:
51 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); 69 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
70 case OpCode::Id::I2F_IMM:
71 return Immediate(instr.alu.GetSignedImm20_20());
72 default:
73 UNREACHABLE();
74 return Immediate(0);
52 } 75 }
53 }(); 76 }();
54 const bool input_signed = instr.conversion.is_input_signed; 77 const bool input_signed = instr.conversion.is_input_signed;
@@ -62,24 +85,31 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
62 break; 85 break;
63 } 86 }
64 case OpCode::Id::F2F_R: 87 case OpCode::Id::F2F_R:
65 case OpCode::Id::F2F_C: { 88 case OpCode::Id::F2F_C:
66 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); 89 case OpCode::Id::F2F_IMM: {
67 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); 90 UNIMPLEMENTED_IF(instr.conversion.f2f.dst_size != Register::Size::Word);
91 UNIMPLEMENTED_IF(instr.conversion.f2f.src_size != Register::Size::Word);
68 UNIMPLEMENTED_IF_MSG(instr.generates_cc, 92 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
69 "Condition codes generation in F2F is not implemented"); 93 "Condition codes generation in F2F is not implemented");
70 94
71 Node value = [&]() { 95 Node value = [&]() {
72 if (instr.is_b_gpr) { 96 switch (opcode->get().GetId()) {
97 case OpCode::Id::F2F_R:
73 return GetRegister(instr.gpr20); 98 return GetRegister(instr.gpr20);
74 } else { 99 case OpCode::Id::F2F_C:
75 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); 100 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
101 case OpCode::Id::F2F_IMM:
102 return GetImmediate19(instr);
103 default:
104 UNREACHABLE();
105 return Immediate(0);
76 } 106 }
77 }(); 107 }();
78 108
79 value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); 109 value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a);
80 110
81 value = [&]() { 111 value = [&]() {
82 switch (instr.conversion.f2f.rounding) { 112 switch (instr.conversion.f2f.GetRoundingMode()) {
83 case Tegra::Shader::F2fRoundingOp::None: 113 case Tegra::Shader::F2fRoundingOp::None:
84 return value; 114 return value;
85 case Tegra::Shader::F2fRoundingOp::Round: 115 case Tegra::Shader::F2fRoundingOp::Round:
@@ -90,10 +120,11 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
90 return Operation(OperationCode::FCeil, PRECISE, value); 120 return Operation(OperationCode::FCeil, PRECISE, value);
91 case Tegra::Shader::F2fRoundingOp::Trunc: 121 case Tegra::Shader::F2fRoundingOp::Trunc:
92 return Operation(OperationCode::FTrunc, PRECISE, value); 122 return Operation(OperationCode::FTrunc, PRECISE, value);
123 default:
124 UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",
125 static_cast<u32>(instr.conversion.f2f.rounding.Value()));
126 return Immediate(0);
93 } 127 }
94 UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",
95 static_cast<u32>(instr.conversion.f2f.rounding.Value()));
96 return Immediate(0);
97 }(); 128 }();
98 value = GetSaturatedFloat(value, instr.alu.saturate_d); 129 value = GetSaturatedFloat(value, instr.alu.saturate_d);
99 130
@@ -102,15 +133,22 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
102 break; 133 break;
103 } 134 }
104 case OpCode::Id::F2I_R: 135 case OpCode::Id::F2I_R:
105 case OpCode::Id::F2I_C: { 136 case OpCode::Id::F2I_C:
137 case OpCode::Id::F2I_IMM: {
106 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); 138 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
107 UNIMPLEMENTED_IF_MSG(instr.generates_cc, 139 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
108 "Condition codes generation in F2I is not implemented"); 140 "Condition codes generation in F2I is not implemented");
109 Node value = [&]() { 141 Node value = [&]() {
110 if (instr.is_b_gpr) { 142 switch (opcode->get().GetId()) {
143 case OpCode::Id::F2I_R:
111 return GetRegister(instr.gpr20); 144 return GetRegister(instr.gpr20);
112 } else { 145 case OpCode::Id::F2I_C:
113 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); 146 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
147 case OpCode::Id::F2I_IMM:
148 return GetImmediate19(instr);
149 default:
150 UNREACHABLE();
151 return Immediate(0);
114 } 152 }
115 }(); 153 }();
116 154
@@ -134,7 +172,7 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
134 }(); 172 }();
135 const bool is_signed = instr.conversion.is_output_signed; 173 const bool is_signed = instr.conversion.is_output_signed;
136 value = SignedOperation(OperationCode::ICastFloat, is_signed, PRECISE, value); 174 value = SignedOperation(OperationCode::ICastFloat, is_signed, PRECISE, value);
137 value = ConvertIntegerSize(value, instr.conversion.dest_size, is_signed); 175 value = ConvertIntegerSize(value, instr.conversion.dst_size, is_signed);
138 176
139 SetRegister(bb, instr.gpr0, value); 177 SetRegister(bb, instr.gpr0, value);
140 break; 178 break;
diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp
index 748368555..1dd94bf9d 100644
--- a/src/video_core/shader/decode/half_set.cpp
+++ b/src/video_core/shader/decode/half_set.cpp
@@ -18,11 +18,13 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
18 const Instruction instr = {program_code[pc]}; 18 const Instruction instr = {program_code[pc]};
19 const auto opcode = OpCode::Decode(instr); 19 const auto opcode = OpCode::Decode(instr);
20 20
21 UNIMPLEMENTED_IF(instr.hset2.ftz != 0); 21 if (instr.hset2.ftz != 0) {
22 LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
23 }
24
25 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a);
26 op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a);
22 27
23 // instr.hset2.type_a
24 // instr.hset2.type_b
25 Node op_a = GetRegister(instr.gpr8);
26 Node op_b = [&]() { 28 Node op_b = [&]() {
27 switch (opcode->get().GetId()) { 29 switch (opcode->get().GetId()) {
28 case OpCode::Id::HSET2_R: 30 case OpCode::Id::HSET2_R:
@@ -32,14 +34,12 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
32 return Immediate(0); 34 return Immediate(0);
33 } 35 }
34 }(); 36 }();
35 37 op_b = UnpackHalfFloat(op_b, instr.hset2.type_b);
36 op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a);
37 op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b); 38 op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b);
38 39
39 const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); 40 const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred);
40 41
41 MetaHalfArithmetic meta{false, {instr.hset2.type_a, instr.hset2.type_b}}; 42 const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, op_a, op_b);
42 const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, meta, op_a, op_b);
43 43
44 const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); 44 const OperationCode combiner = GetPredicateCombiner(instr.hset2.op);
45 45
diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp
index e68512692..6e59eb650 100644
--- a/src/video_core/shader/decode/half_set_predicate.cpp
+++ b/src/video_core/shader/decode/half_set_predicate.cpp
@@ -19,10 +19,10 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
19 19
20 UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0); 20 UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0);
21 21
22 Node op_a = GetRegister(instr.gpr8); 22 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hsetp2.type_a);
23 op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a); 23 op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a);
24 24
25 const Node op_b = [&]() { 25 Node op_b = [&]() {
26 switch (opcode->get().GetId()) { 26 switch (opcode->get().GetId()) {
27 case OpCode::Id::HSETP2_R: 27 case OpCode::Id::HSETP2_R:
28 return GetOperandAbsNegHalf(GetRegister(instr.gpr20), instr.hsetp2.abs_a, 28 return GetOperandAbsNegHalf(GetRegister(instr.gpr20), instr.hsetp2.abs_a,
@@ -32,6 +32,7 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
32 return Immediate(0); 32 return Immediate(0);
33 } 33 }
34 }(); 34 }();
35 op_b = UnpackHalfFloat(op_b, instr.hsetp2.type_b);
35 36
36 // We can't use the constant predicate as destination. 37 // We can't use the constant predicate as destination.
37 ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex)); 38 ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex));
@@ -42,8 +43,7 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
42 const OperationCode pair_combiner = 43 const OperationCode pair_combiner =
43 instr.hsetp2.h_and ? OperationCode::LogicalAll2 : OperationCode::LogicalAny2; 44 instr.hsetp2.h_and ? OperationCode::LogicalAll2 : OperationCode::LogicalAny2;
44 45
45 MetaHalfArithmetic meta = {false, {instr.hsetp2.type_a, instr.hsetp2.type_b}}; 46 const Node comparison = GetPredicateComparisonHalf(instr.hsetp2.cond, op_a, op_b);
46 const Node comparison = GetPredicateComparisonHalf(instr.hsetp2.cond, meta, op_a, op_b);
47 const Node first_pred = Operation(pair_combiner, comparison); 47 const Node first_pred = Operation(pair_combiner, comparison);
48 48
49 // Set the primary predicate to the result of Predicate OP SecondPredicate 49 // Set the primary predicate to the result of Predicate OP SecondPredicate
diff --git a/src/video_core/shader/decode/hfma2.cpp b/src/video_core/shader/decode/hfma2.cpp
index 7a07c5ec6..a425f9eb7 100644
--- a/src/video_core/shader/decode/hfma2.cpp
+++ b/src/video_core/shader/decode/hfma2.cpp
@@ -27,10 +27,6 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) {
27 } 27 }
28 28
29 constexpr auto identity = HalfType::H0_H1; 29 constexpr auto identity = HalfType::H0_H1;
30
31 const HalfType type_a = instr.hfma2.type_a;
32 const Node op_a = GetRegister(instr.gpr8);
33
34 bool neg_b{}, neg_c{}; 30 bool neg_b{}, neg_c{};
35 auto [saturate, type_b, op_b, type_c, 31 auto [saturate, type_b, op_b, type_c,
36 op_c] = [&]() -> std::tuple<bool, HalfType, Node, HalfType, Node> { 32 op_c] = [&]() -> std::tuple<bool, HalfType, Node, HalfType, Node> {
@@ -38,15 +34,14 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) {
38 case OpCode::Id::HFMA2_CR: 34 case OpCode::Id::HFMA2_CR:
39 neg_b = instr.hfma2.negate_b; 35 neg_b = instr.hfma2.negate_b;
40 neg_c = instr.hfma2.negate_c; 36 neg_c = instr.hfma2.negate_c;
41 return {instr.hfma2.saturate, instr.hfma2.type_b, 37 return {instr.hfma2.saturate, HalfType::F32,
42 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), 38 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()),
43 instr.hfma2.type_reg39, GetRegister(instr.gpr39)}; 39 instr.hfma2.type_reg39, GetRegister(instr.gpr39)};
44 case OpCode::Id::HFMA2_RC: 40 case OpCode::Id::HFMA2_RC:
45 neg_b = instr.hfma2.negate_b; 41 neg_b = instr.hfma2.negate_b;
46 neg_c = instr.hfma2.negate_c; 42 neg_c = instr.hfma2.negate_c;
47 return {instr.hfma2.saturate, instr.hfma2.type_reg39, GetRegister(instr.gpr39), 43 return {instr.hfma2.saturate, instr.hfma2.type_reg39, GetRegister(instr.gpr39),
48 instr.hfma2.type_b, 44 HalfType::F32, GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
49 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
50 case OpCode::Id::HFMA2_RR: 45 case OpCode::Id::HFMA2_RR:
51 neg_b = instr.hfma2.rr.negate_b; 46 neg_b = instr.hfma2.rr.negate_b;
52 neg_c = instr.hfma2.rr.negate_c; 47 neg_c = instr.hfma2.rr.negate_c;
@@ -60,13 +55,13 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) {
60 return {false, identity, Immediate(0), identity, Immediate(0)}; 55 return {false, identity, Immediate(0), identity, Immediate(0)};
61 } 56 }
62 }(); 57 }();
63 UNIMPLEMENTED_IF_MSG(saturate, "HFMA2 saturation is not implemented");
64 58
65 op_b = GetOperandAbsNegHalf(op_b, false, neg_b); 59 const Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hfma2.type_a);
66 op_c = GetOperandAbsNegHalf(op_c, false, neg_c); 60 op_b = GetOperandAbsNegHalf(UnpackHalfFloat(op_b, type_b), false, neg_b);
61 op_c = GetOperandAbsNegHalf(UnpackHalfFloat(op_c, type_c), false, neg_c);
67 62
68 MetaHalfArithmetic meta{true, {type_a, type_b, type_c}}; 63 Node value = Operation(OperationCode::HFma, PRECISE, op_a, op_b, op_c);
69 Node value = Operation(OperationCode::HFma, meta, op_a, op_b, op_c); 64 value = GetSaturatedHalfFloat(value, saturate);
70 value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge); 65 value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge);
71 66
72 SetRegister(bb, instr.gpr0, value); 67 SetRegister(bb, instr.gpr0, value);
@@ -74,4 +69,4 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) {
74 return pc; 69 return pc;
75} 70}
76 71
77} // namespace VideoCommon::Shader \ No newline at end of file 72} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index ea3c71eed..ea1092db1 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -8,6 +8,7 @@
8 8
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/logging/log.h"
11#include "video_core/engines/shader_bytecode.h" 12#include "video_core/engines/shader_bytecode.h"
12#include "video_core/shader/shader_ir.h" 13#include "video_core/shader/shader_ir.h"
13 14
@@ -18,6 +19,23 @@ using Tegra::Shader::Instruction;
18using Tegra::Shader::OpCode; 19using Tegra::Shader::OpCode;
19using Tegra::Shader::Register; 20using Tegra::Shader::Register;
20 21
22namespace {
23u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) {
24 switch (uniform_type) {
25 case Tegra::Shader::UniformType::Single:
26 return 1;
27 case Tegra::Shader::UniformType::Double:
28 return 2;
29 case Tegra::Shader::UniformType::Quad:
30 case Tegra::Shader::UniformType::UnsignedQuad:
31 return 4;
32 default:
33 UNIMPLEMENTED_MSG("Unimplemented size={}!", static_cast<u32>(uniform_type));
34 return 1;
35 }
36}
37} // namespace
38
21u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { 39u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
22 const Instruction instr = {program_code[pc]}; 40 const Instruction instr = {program_code[pc]};
23 const auto opcode = OpCode::Decode(instr); 41 const auto opcode = OpCode::Decode(instr);
@@ -85,8 +103,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
85 break; 103 break;
86 } 104 }
87 case OpCode::Id::LD_L: { 105 case OpCode::Id::LD_L: {
88 UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}", 106 LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}",
89 static_cast<u32>(instr.ld_l.unknown.Value())); 107 static_cast<u64>(instr.ld_l.unknown.Value()));
90 108
91 const auto GetLmem = [&](s32 offset) { 109 const auto GetLmem = [&](s32 offset) {
92 ASSERT(offset % 4 == 0); 110 ASSERT(offset % 4 == 0);
@@ -126,45 +144,15 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
126 break; 144 break;
127 } 145 }
128 case OpCode::Id::LDG: { 146 case OpCode::Id::LDG: {
129 const u32 count = [&]() { 147 const auto [real_address_base, base_address, descriptor] =
130 switch (instr.ldg.type) { 148 TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
131 case Tegra::Shader::UniformType::Single: 149 static_cast<u32>(instr.ldg.immediate_offset.Value()), false);
132 return 1;
133 case Tegra::Shader::UniformType::Double:
134 return 2;
135 case Tegra::Shader::UniformType::Quad:
136 case Tegra::Shader::UniformType::UnsignedQuad:
137 return 4;
138 default:
139 UNIMPLEMENTED_MSG("Unimplemented LDG size!");
140 return 1;
141 }
142 }();
143
144 const Node addr_register = GetRegister(instr.gpr8);
145 const Node base_address =
146 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()));
147 const auto cbuf = std::get_if<CbufNode>(base_address);
148 ASSERT(cbuf != nullptr);
149 const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
150 ASSERT(cbuf_offset_imm != nullptr);
151 const auto cbuf_offset = cbuf_offset_imm->GetValue();
152
153 bb.push_back(Comment(
154 fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset)));
155
156 const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset};
157 used_global_memory_bases.insert(descriptor);
158
159 const Node immediate_offset =
160 Immediate(static_cast<u32>(instr.ldg.immediate_offset.Value()));
161 const Node base_real_address =
162 Operation(OperationCode::UAdd, NO_PRECISE, immediate_offset, addr_register);
163 150
151 const u32 count = GetUniformTypeElementsCount(instr.ldg.type);
164 for (u32 i = 0; i < count; ++i) { 152 for (u32 i = 0; i < count; ++i) {
165 const Node it_offset = Immediate(i * 4); 153 const Node it_offset = Immediate(i * 4);
166 const Node real_address = 154 const Node real_address =
167 Operation(OperationCode::UAdd, NO_PRECISE, base_real_address, it_offset); 155 Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
168 const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); 156 const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
169 157
170 SetTemporal(bb, i, gmem); 158 SetTemporal(bb, i, gmem);
@@ -174,6 +162,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
174 } 162 }
175 break; 163 break;
176 } 164 }
165 case OpCode::Id::STG: {
166 const auto [real_address_base, base_address, descriptor] =
167 TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
168 static_cast<u32>(instr.stg.immediate_offset.Value()), true);
169
170 // Encode in temporary registers like this: real_base_address, {registers_to_be_written...}
171 SetTemporal(bb, 0, real_address_base);
172
173 const u32 count = GetUniformTypeElementsCount(instr.stg.type);
174 for (u32 i = 0; i < count; ++i) {
175 SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i));
176 }
177 for (u32 i = 0; i < count; ++i) {
178 const Node it_offset = Immediate(i * 4);
179 const Node real_address =
180 Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
181 const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
182
183 bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
184 }
185 break;
186 }
177 case OpCode::Id::ST_A: { 187 case OpCode::Id::ST_A: {
178 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, 188 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
179 "Indirect attribute loads are not supported"); 189 "Indirect attribute loads are not supported");
@@ -205,8 +215,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
205 break; 215 break;
206 } 216 }
207 case OpCode::Id::ST_L: { 217 case OpCode::Id::ST_L: {
208 UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}", 218 LOG_DEBUG(HW_GPU, "ST_L cache management mode: {}",
209 static_cast<u32>(instr.st_l.unknown.Value())); 219 static_cast<u64>(instr.st_l.cache_management.Value()));
210 220
211 const auto GetLmemAddr = [&](s32 offset) { 221 const auto GetLmemAddr = [&](s32 offset) {
212 ASSERT(offset % 4 == 0); 222 ASSERT(offset % 4 == 0);
@@ -236,4 +246,34 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
236 return pc; 246 return pc;
237} 247}
238 248
249std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb,
250 Node addr_register,
251 u32 immediate_offset,
252 bool is_write) {
253 const Node base_address{
254 TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))};
255 const auto cbuf = std::get_if<CbufNode>(base_address);
256 ASSERT(cbuf != nullptr);
257 const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
258 ASSERT(cbuf_offset_imm != nullptr);
259 const auto cbuf_offset = cbuf_offset_imm->GetValue();
260
261 bb.push_back(
262 Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset)));
263
264 const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset};
265 const auto& [entry, is_new] = used_global_memory.try_emplace(descriptor);
266 auto& usage = entry->second;
267 if (is_write) {
268 usage.is_written = true;
269 } else {
270 usage.is_read = true;
271 }
272
273 const auto real_address =
274 Operation(OperationCode::UAdd, NO_PRECISE, Immediate(immediate_offset), addr_register);
275
276 return {real_address, base_address, descriptor};
277}
278
239} // namespace VideoCommon::Shader 279} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index a775b402b..819cc6131 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -40,7 +40,7 @@ static std::size_t GetCoordCount(TextureType texture_type) {
40u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { 40u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
41 const Instruction instr = {program_code[pc]}; 41 const Instruction instr = {program_code[pc]};
42 const auto opcode = OpCode::Decode(instr); 42 const auto opcode = OpCode::Decode(instr);
43 43 bool is_bindless = false;
44 switch (opcode->get().GetId()) { 44 switch (opcode->get().GetId()) {
45 case OpCode::Id::TEX: { 45 case OpCode::Id::TEX: {
46 if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) { 46 if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
@@ -54,7 +54,25 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
54 const auto process_mode = instr.tex.GetTextureProcessMode(); 54 const auto process_mode = instr.tex.GetTextureProcessMode();
55 WriteTexInstructionFloat( 55 WriteTexInstructionFloat(
56 bb, instr, 56 bb, instr,
57 GetTexCode(instr, texture_type, process_mode, depth_compare, is_array, is_aoffi)); 57 GetTexCode(instr, texture_type, process_mode, depth_compare, is_array, is_aoffi, {}));
58 break;
59 }
60 case OpCode::Id::TEX_B: {
61 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI),
62 "AOFFI is not implemented");
63
64 if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
65 LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete");
66 }
67
68 const TextureType texture_type{instr.tex_b.texture_type};
69 const bool is_array = instr.tex_b.array != 0;
70 const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
71 const bool depth_compare = instr.tex_b.UsesMiscMode(TextureMiscMode::DC);
72 const auto process_mode = instr.tex_b.GetTextureProcessMode();
73 WriteTexInstructionFloat(bb, instr,
74 GetTexCode(instr, texture_type, process_mode, depth_compare,
75 is_array, is_aoffi, {instr.gpr20}));
58 break; 76 break;
59 } 77 }
60 case OpCode::Id::TEXS: { 78 case OpCode::Id::TEXS: {
@@ -134,6 +152,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
134 WriteTexsInstructionFloat(bb, instr, values); 152 WriteTexsInstructionFloat(bb, instr, values);
135 break; 153 break;
136 } 154 }
155 case OpCode::Id::TXQ_B:
156 is_bindless = true;
157 [[fallthrough]];
137 case OpCode::Id::TXQ: { 158 case OpCode::Id::TXQ: {
138 if (instr.txq.UsesMiscMode(TextureMiscMode::NODEP)) { 159 if (instr.txq.UsesMiscMode(TextureMiscMode::NODEP)) {
139 LOG_WARNING(HW_GPU, "TXQ.NODEP implementation is incomplete"); 160 LOG_WARNING(HW_GPU, "TXQ.NODEP implementation is incomplete");
@@ -143,7 +164,10 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
143 // Sadly, not all texture instructions specify the type of texture their sampler 164 // Sadly, not all texture instructions specify the type of texture their sampler
144 // uses. This must be fixed at a later instance. 165 // uses. This must be fixed at a later instance.
145 const auto& sampler = 166 const auto& sampler =
146 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); 167 is_bindless
168 ? GetBindlessSampler(instr.gpr8, Tegra::Shader::TextureType::Texture2D, false,
169 false)
170 : GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
147 171
148 u32 indexer = 0; 172 u32 indexer = 0;
149 switch (instr.txq.query_type) { 173 switch (instr.txq.query_type) {
@@ -154,7 +178,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
154 } 178 }
155 MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element}; 179 MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element};
156 const Node value = 180 const Node value =
157 Operation(OperationCode::TextureQueryDimensions, meta, GetRegister(instr.gpr8)); 181 Operation(OperationCode::TextureQueryDimensions, meta,
182 GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0)));
158 SetTemporal(bb, indexer++, value); 183 SetTemporal(bb, indexer++, value);
159 } 184 }
160 for (u32 i = 0; i < indexer; ++i) { 185 for (u32 i = 0; i < indexer; ++i) {
@@ -168,6 +193,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
168 } 193 }
169 break; 194 break;
170 } 195 }
196 case OpCode::Id::TMML_B:
197 is_bindless = true;
198 [[fallthrough]];
171 case OpCode::Id::TMML: { 199 case OpCode::Id::TMML: {
172 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 200 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
173 "NDV is not implemented"); 201 "NDV is not implemented");
@@ -178,7 +206,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
178 206
179 auto texture_type = instr.tmml.texture_type.Value(); 207 auto texture_type = instr.tmml.texture_type.Value();
180 const bool is_array = instr.tmml.array != 0; 208 const bool is_array = instr.tmml.array != 0;
181 const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, false); 209 const auto& sampler = is_bindless
210 ? GetBindlessSampler(instr.gpr20, texture_type, is_array, false)
211 : GetSampler(instr.sampler, texture_type, is_array, false);
182 212
183 std::vector<Node> coords; 213 std::vector<Node> coords;
184 214
@@ -199,17 +229,19 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
199 coords.push_back(GetRegister(instr.gpr8.Value() + 1)); 229 coords.push_back(GetRegister(instr.gpr8.Value() + 1));
200 texture_type = TextureType::Texture2D; 230 texture_type = TextureType::Texture2D;
201 } 231 }
202 232 u32 indexer = 0;
203 for (u32 element = 0; element < 2; ++element) { 233 for (u32 element = 0; element < 2; ++element) {
234 if (!instr.tmml.IsComponentEnabled(element)) {
235 continue;
236 }
204 auto params = coords; 237 auto params = coords;
205 MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element}; 238 MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element};
206 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); 239 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
207 SetTemporal(bb, element, value); 240 SetTemporal(bb, indexer++, value);
208 } 241 }
209 for (u32 element = 0; element < 2; ++element) { 242 for (u32 i = 0; i < indexer; ++i) {
210 SetRegister(bb, instr.gpr0.Value() + element, GetTemporal(element)); 243 SetRegister(bb, instr.gpr0.Value() + i, GetTemporal(i));
211 } 244 }
212
213 break; 245 break;
214 } 246 }
215 case OpCode::Id::TLDS: { 247 case OpCode::Id::TLDS: {
@@ -254,6 +286,34 @@ const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, Textu
254 return *used_samplers.emplace(entry).first; 286 return *used_samplers.emplace(entry).first;
255} 287}
256 288
289const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, TextureType type,
290 bool is_array, bool is_shadow) {
291 const Node sampler_register = GetRegister(reg);
292 const Node base_sampler =
293 TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
294 const auto cbuf = std::get_if<CbufNode>(base_sampler);
295 const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
296 ASSERT(cbuf_offset_imm != nullptr);
297 const auto cbuf_offset = cbuf_offset_imm->GetValue();
298 const auto cbuf_index = cbuf->GetIndex();
299 const auto cbuf_key = (static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset);
300
301 // If this sampler has already been used, return the existing mapping.
302 const auto itr =
303 std::find_if(used_samplers.begin(), used_samplers.end(),
304 [&](const Sampler& entry) { return entry.GetOffset() == cbuf_key; });
305 if (itr != used_samplers.end()) {
306 ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
307 itr->IsShadow() == is_shadow);
308 return *itr;
309 }
310
311 // Otherwise create a new mapping for this sampler
312 const std::size_t next_index = used_samplers.size();
313 const Sampler entry{cbuf_index, cbuf_offset, next_index, type, is_array, is_shadow};
314 return *used_samplers.emplace(entry).first;
315}
316
257void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { 317void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
258 u32 dest_elem = 0; 318 u32 dest_elem = 0;
259 for (u32 elem = 0; elem < 4; ++elem) { 319 for (u32 elem = 0; elem < 4; ++elem) {
@@ -326,22 +386,27 @@ void ShaderIR::WriteTexsInstructionHalfFloat(NodeBlock& bb, Instruction instr,
326Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, 386Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
327 TextureProcessMode process_mode, std::vector<Node> coords, 387 TextureProcessMode process_mode, std::vector<Node> coords,
328 Node array, Node depth_compare, u32 bias_offset, 388 Node array, Node depth_compare, u32 bias_offset,
329 std::vector<Node> aoffi) { 389 std::vector<Node> aoffi,
390 std::optional<Tegra::Shader::Register> bindless_reg) {
330 const bool is_array = array; 391 const bool is_array = array;
331 const bool is_shadow = depth_compare; 392 const bool is_shadow = depth_compare;
393 const bool is_bindless = bindless_reg.has_value();
332 394
333 UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) || 395 UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) ||
334 (texture_type == TextureType::TextureCube && is_array && is_shadow), 396 (texture_type == TextureType::TextureCube && is_array && is_shadow),
335 "This method is not supported."); 397 "This method is not supported.");
336 398
337 const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, is_shadow); 399 const auto& sampler = is_bindless
400 ? GetBindlessSampler(*bindless_reg, texture_type, is_array, is_shadow)
401 : GetSampler(instr.sampler, texture_type, is_array, is_shadow);
338 402
339 const bool lod_needed = process_mode == TextureProcessMode::LZ || 403 const bool lod_needed = process_mode == TextureProcessMode::LZ ||
340 process_mode == TextureProcessMode::LL || 404 process_mode == TextureProcessMode::LL ||
341 process_mode == TextureProcessMode::LLA; 405 process_mode == TextureProcessMode::LLA;
342 406
343 // LOD selection (either via bias or explicit textureLod) not supported in GL for 407 // LOD selection (either via bias or explicit textureLod) not
344 // sampler2DArrayShadow and samplerCubeArrayShadow. 408 // supported in GL for sampler2DArrayShadow and
409 // samplerCubeArrayShadow.
345 const bool gl_lod_supported = 410 const bool gl_lod_supported =
346 !((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) || 411 !((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
347 (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow)); 412 (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
@@ -359,8 +424,9 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
359 lod = Immediate(0.0f); 424 lod = Immediate(0.0f);
360 break; 425 break;
361 case TextureProcessMode::LB: 426 case TextureProcessMode::LB:
362 // If present, lod or bias are always stored in the register indexed by the gpr20 427 // If present, lod or bias are always stored in the register
363 // field with an offset depending on the usage of the other registers 428 // indexed by the gpr20 field with an offset depending on the
429 // usage of the other registers
364 bias = GetRegister(instr.gpr20.Value() + bias_offset); 430 bias = GetRegister(instr.gpr20.Value() + bias_offset);
365 break; 431 break;
366 case TextureProcessMode::LL: 432 case TextureProcessMode::LL:
@@ -384,11 +450,18 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
384 450
385Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, 451Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
386 TextureProcessMode process_mode, bool depth_compare, bool is_array, 452 TextureProcessMode process_mode, bool depth_compare, bool is_array,
387 bool is_aoffi) { 453 bool is_aoffi, std::optional<Tegra::Shader::Register> bindless_reg) {
388 const bool lod_bias_enabled{ 454 const bool lod_bias_enabled{
389 (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ)}; 455 (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ)};
390 456
457 const bool is_bindless = bindless_reg.has_value();
458
391 u64 parameter_register = instr.gpr20.Value(); 459 u64 parameter_register = instr.gpr20.Value();
460 if (is_bindless) {
461 ++parameter_register;
462 }
463
464 const u32 bias_lod_offset = (is_bindless ? 1 : 0);
392 if (lod_bias_enabled) { 465 if (lod_bias_enabled) {
393 ++parameter_register; 466 ++parameter_register;
394 } 467 }
@@ -423,7 +496,8 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
423 dc = GetRegister(parameter_register++); 496 dc = GetRegister(parameter_register++);
424 } 497 }
425 498
426 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, 0, aoffi); 499 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_lod_offset,
500 aoffi, bindless_reg);
427} 501}
428 502
429Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, 503Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
@@ -459,7 +533,8 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
459 dc = GetRegister(depth_register); 533 dc = GetRegister(depth_register);
460 } 534 }
461 535
462 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset, {}); 536 return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset, {},
537 {});
463} 538}
464 539
465Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare, 540Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare,
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp
index db15c0718..04a776398 100644
--- a/src/video_core/shader/decode/xmad.cpp
+++ b/src/video_core/shader/decode/xmad.cpp
@@ -56,9 +56,10 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
56 instr.xmad.mode, 56 instr.xmad.mode,
57 Immediate(static_cast<u32>(instr.xmad.imm20_16)), 57 Immediate(static_cast<u32>(instr.xmad.imm20_16)),
58 GetRegister(instr.gpr39)}; 58 GetRegister(instr.gpr39)};
59 default:
60 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
61 return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)};
59 } 62 }
60 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
61 return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)};
62 }(); 63 }();
63 64
64 op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); 65 op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16);
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index ac5112d78..e4eb0dfd9 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -189,7 +189,11 @@ Node ShaderIR::UnpackHalfImmediate(Instruction instr, bool has_negation) {
189 const Node first_negate = GetPredicate(instr.half_imm.first_negate != 0); 189 const Node first_negate = GetPredicate(instr.half_imm.first_negate != 0);
190 const Node second_negate = GetPredicate(instr.half_imm.second_negate != 0); 190 const Node second_negate = GetPredicate(instr.half_imm.second_negate != 0);
191 191
192 return Operation(OperationCode::HNegate, HALF_NO_PRECISE, value, first_negate, second_negate); 192 return Operation(OperationCode::HNegate, NO_PRECISE, value, first_negate, second_negate);
193}
194
195Node ShaderIR::UnpackHalfFloat(Node value, Tegra::Shader::HalfType type) {
196 return Operation(OperationCode::HUnpack, type, value);
193} 197}
194 198
195Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) { 199Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) {
@@ -209,17 +213,26 @@ Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) {
209 213
210Node ShaderIR::GetOperandAbsNegHalf(Node value, bool absolute, bool negate) { 214Node ShaderIR::GetOperandAbsNegHalf(Node value, bool absolute, bool negate) {
211 if (absolute) { 215 if (absolute) {
212 value = Operation(OperationCode::HAbsolute, HALF_NO_PRECISE, value); 216 value = Operation(OperationCode::HAbsolute, NO_PRECISE, value);
213 } 217 }
214 if (negate) { 218 if (negate) {
215 value = Operation(OperationCode::HNegate, HALF_NO_PRECISE, value, GetPredicate(true), 219 value = Operation(OperationCode::HNegate, NO_PRECISE, value, GetPredicate(true),
216 GetPredicate(true)); 220 GetPredicate(true));
217 } 221 }
218 return value; 222 return value;
219} 223}
220 224
225Node ShaderIR::GetSaturatedHalfFloat(Node value, bool saturate) {
226 if (!saturate) {
227 return value;
228 }
229 const Node positive_zero = Immediate(std::copysignf(0, 1));
230 const Node positive_one = Immediate(1.0f);
231 return Operation(OperationCode::HClamp, NO_PRECISE, value, positive_zero, positive_one);
232}
233
221Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) { 234Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) {
222 static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { 235 const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
223 {PredCondition::LessThan, OperationCode::LogicalFLessThan}, 236 {PredCondition::LessThan, OperationCode::LogicalFLessThan},
224 {PredCondition::Equal, OperationCode::LogicalFEqual}, 237 {PredCondition::Equal, OperationCode::LogicalFEqual},
225 {PredCondition::LessEqual, OperationCode::LogicalFLessEqual}, 238 {PredCondition::LessEqual, OperationCode::LogicalFLessEqual},
@@ -255,7 +268,7 @@ Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, N
255 268
256Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a, 269Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a,
257 Node op_b) { 270 Node op_b) {
258 static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { 271 const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
259 {PredCondition::LessThan, OperationCode::LogicalILessThan}, 272 {PredCondition::LessThan, OperationCode::LogicalILessThan},
260 {PredCondition::Equal, OperationCode::LogicalIEqual}, 273 {PredCondition::Equal, OperationCode::LogicalIEqual},
261 {PredCondition::LessEqual, OperationCode::LogicalILessEqual}, 274 {PredCondition::LessEqual, OperationCode::LogicalILessEqual},
@@ -283,40 +296,32 @@ Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_si
283 return predicate; 296 return predicate;
284} 297}
285 298
286Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, 299Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a,
287 const MetaHalfArithmetic& meta, Node op_a, Node op_b) { 300 Node op_b) {
288 301 const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
289 UNIMPLEMENTED_IF_MSG(condition == PredCondition::LessThanWithNan ||
290 condition == PredCondition::NotEqualWithNan ||
291 condition == PredCondition::LessEqualWithNan ||
292 condition == PredCondition::GreaterThanWithNan ||
293 condition == PredCondition::GreaterEqualWithNan,
294 "Unimplemented NaN comparison for half floats");
295
296 static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
297 {PredCondition::LessThan, OperationCode::Logical2HLessThan}, 302 {PredCondition::LessThan, OperationCode::Logical2HLessThan},
298 {PredCondition::Equal, OperationCode::Logical2HEqual}, 303 {PredCondition::Equal, OperationCode::Logical2HEqual},
299 {PredCondition::LessEqual, OperationCode::Logical2HLessEqual}, 304 {PredCondition::LessEqual, OperationCode::Logical2HLessEqual},
300 {PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan}, 305 {PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan},
301 {PredCondition::NotEqual, OperationCode::Logical2HNotEqual}, 306 {PredCondition::NotEqual, OperationCode::Logical2HNotEqual},
302 {PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual}, 307 {PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual},
303 {PredCondition::LessThanWithNan, OperationCode::Logical2HLessThan}, 308 {PredCondition::LessThanWithNan, OperationCode::Logical2HLessThanWithNan},
304 {PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqual}, 309 {PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqualWithNan},
305 {PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqual}, 310 {PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqualWithNan},
306 {PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThan}, 311 {PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThanWithNan},
307 {PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqual}}; 312 {PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqualWithNan}};
308 313
309 const auto comparison{PredicateComparisonTable.find(condition)}; 314 const auto comparison{PredicateComparisonTable.find(condition)};
310 UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonTable.end(), 315 UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonTable.end(),
311 "Unknown predicate comparison operation"); 316 "Unknown predicate comparison operation");
312 317
313 const Node predicate = Operation(comparison->second, meta, op_a, op_b); 318 const Node predicate = Operation(comparison->second, NO_PRECISE, op_a, op_b);
314 319
315 return predicate; 320 return predicate;
316} 321}
317 322
318OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) { 323OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) {
319 static const std::unordered_map<PredOperation, OperationCode> PredicateOperationTable = { 324 const std::unordered_map<PredOperation, OperationCode> PredicateOperationTable = {
320 {PredOperation::And, OperationCode::LogicalAnd}, 325 {PredOperation::And, OperationCode::LogicalAnd},
321 {PredOperation::Or, OperationCode::LogicalOr}, 326 {PredOperation::Or, OperationCode::LogicalOr},
322 {PredOperation::Xor, OperationCode::LogicalXor}, 327 {PredOperation::Xor, OperationCode::LogicalXor},
@@ -434,11 +439,14 @@ Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) {
434 return OperationCode::LogicalUGreaterEqual; 439 return OperationCode::LogicalUGreaterEqual;
435 case OperationCode::INegate: 440 case OperationCode::INegate:
436 UNREACHABLE_MSG("Can't negate an unsigned integer"); 441 UNREACHABLE_MSG("Can't negate an unsigned integer");
442 return {};
437 case OperationCode::IAbsolute: 443 case OperationCode::IAbsolute:
438 UNREACHABLE_MSG("Can't apply absolute to an unsigned integer"); 444 UNREACHABLE_MSG("Can't apply absolute to an unsigned integer");
445 return {};
446 default:
447 UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code));
448 return {};
439 } 449 }
440 UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code));
441 return {};
442} 450}
443 451
444} // namespace VideoCommon::Shader \ No newline at end of file 452} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 4888998d3..65f1e1de9 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -109,11 +109,13 @@ enum class OperationCode {
109 UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint 109 UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint
110 UBitCount, /// (MetaArithmetic, uint) -> uint 110 UBitCount, /// (MetaArithmetic, uint) -> uint
111 111
112 HAdd, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 112 HAdd, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
113 HMul, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 113 HMul, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
114 HFma, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2 114 HFma, /// (MetaArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2
115 HAbsolute, /// (f16vec2 a) -> f16vec2 115 HAbsolute, /// (f16vec2 a) -> f16vec2
116 HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2 116 HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2
117 HClamp, /// (f16vec2 src, float min, float max) -> f16vec2
118 HUnpack, /// (Tegra::Shader::HalfType, T value) -> f16vec2
117 HMergeF32, /// (f16vec2 src) -> float 119 HMergeF32, /// (f16vec2 src) -> float
118 HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2 120 HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2
119 HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2 121 HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2
@@ -150,12 +152,18 @@ enum class OperationCode {
150 LogicalUNotEqual, /// (uint a, uint b) -> bool 152 LogicalUNotEqual, /// (uint a, uint b) -> bool
151 LogicalUGreaterEqual, /// (uint a, uint b) -> bool 153 LogicalUGreaterEqual, /// (uint a, uint b) -> bool
152 154
153 Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 155 Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
154 Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 156 Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
155 Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 157 Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
156 Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 158 Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
157 Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 159 Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
158 Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 160 Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
161 Logical2HLessThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
162 Logical2HEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
163 Logical2HLessEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
164 Logical2HGreaterThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
165 Logical2HNotEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
166 Logical2HGreaterEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
159 167
160 Texture, /// (MetaTexture, float[N] coords) -> float4 168 Texture, /// (MetaTexture, float[N] coords) -> float4
161 TextureLod, /// (MetaTexture, float[N] coords) -> float4 169 TextureLod, /// (MetaTexture, float[N] coords) -> float4
@@ -196,9 +204,23 @@ enum class ExitMethod {
196 204
197class Sampler { 205class Sampler {
198public: 206public:
207 // Use this constructor for bounded Samplers
199 explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type, 208 explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
200 bool is_array, bool is_shadow) 209 bool is_array, bool is_shadow)
201 : offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow} {} 210 : offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
211 is_bindless{false} {}
212
213 // Use this constructor for bindless Samplers
214 explicit Sampler(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
215 Tegra::Shader::TextureType type, bool is_array, bool is_shadow)
216 : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
217 is_array{is_array}, is_shadow{is_shadow}, is_bindless{true} {}
218
219 // Use this only for serialization/deserialization
220 explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
221 bool is_array, bool is_shadow, bool is_bindless)
222 : offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
223 is_bindless{is_bindless} {}
202 224
203 std::size_t GetOffset() const { 225 std::size_t GetOffset() const {
204 return offset; 226 return offset;
@@ -220,9 +242,18 @@ public:
220 return is_shadow; 242 return is_shadow;
221 } 243 }
222 244
245 bool IsBindless() const {
246 return is_bindless;
247 }
248
249 std::pair<u32, u32> GetBindlessCBuf() const {
250 return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
251 }
252
223 bool operator<(const Sampler& rhs) const { 253 bool operator<(const Sampler& rhs) const {
224 return std::tie(offset, index, type, is_array, is_shadow) < 254 return std::tie(index, offset, type, is_array, is_shadow, is_bindless) <
225 std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_array, rhs.is_shadow); 255 std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow,
256 rhs.is_bindless);
226 } 257 }
227 258
228private: 259private:
@@ -231,8 +262,9 @@ private:
231 std::size_t offset{}; 262 std::size_t offset{};
232 std::size_t index{}; ///< Value used to index into the generated GLSL sampler array. 263 std::size_t index{}; ///< Value used to index into the generated GLSL sampler array.
233 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) 264 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
234 bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. 265 bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
235 bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. 266 bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
267 bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
236}; 268};
237 269
238class ConstBuffer { 270class ConstBuffer {
@@ -276,15 +308,13 @@ struct GlobalMemoryBase {
276 } 308 }
277}; 309};
278 310
279struct MetaArithmetic { 311struct GlobalMemoryUsage {
280 bool precise{}; 312 bool is_read{};
313 bool is_written{};
281}; 314};
282 315
283struct MetaHalfArithmetic { 316struct MetaArithmetic {
284 bool precise{}; 317 bool precise{};
285 std::array<Tegra::Shader::HalfType, 3> types = {Tegra::Shader::HalfType::H0_H1,
286 Tegra::Shader::HalfType::H0_H1,
287 Tegra::Shader::HalfType::H0_H1};
288}; 318};
289 319
290struct MetaTexture { 320struct MetaTexture {
@@ -298,11 +328,10 @@ struct MetaTexture {
298 u32 element{}; 328 u32 element{};
299}; 329};
300 330
301constexpr MetaArithmetic PRECISE = {true}; 331inline constexpr MetaArithmetic PRECISE = {true};
302constexpr MetaArithmetic NO_PRECISE = {false}; 332inline constexpr MetaArithmetic NO_PRECISE = {false};
303constexpr MetaHalfArithmetic HALF_NO_PRECISE = {false};
304 333
305using Meta = std::variant<MetaArithmetic, MetaHalfArithmetic, MetaTexture>; 334using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>;
306 335
307/// Holds any kind of operation that can be done in the IR 336/// Holds any kind of operation that can be done in the IR
308class OperationNode final { 337class OperationNode final {
@@ -578,8 +607,8 @@ public:
578 return used_clip_distances; 607 return used_clip_distances;
579 } 608 }
580 609
581 const std::set<GlobalMemoryBase>& GetGlobalMemoryBases() const { 610 const std::map<GlobalMemoryBase, GlobalMemoryUsage>& GetGlobalMemory() const {
582 return used_global_memory_bases; 611 return used_global_memory;
583 } 612 }
584 613
585 std::size_t GetLength() const { 614 std::size_t GetLength() const {
@@ -706,10 +735,14 @@ private:
706 735
707 /// Unpacks a half immediate from an instruction 736 /// Unpacks a half immediate from an instruction
708 Node UnpackHalfImmediate(Tegra::Shader::Instruction instr, bool has_negation); 737 Node UnpackHalfImmediate(Tegra::Shader::Instruction instr, bool has_negation);
738 /// Unpacks a binary value into a half float pair with a type format
739 Node UnpackHalfFloat(Node value, Tegra::Shader::HalfType type);
709 /// Merges a half pair into another value 740 /// Merges a half pair into another value
710 Node HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge); 741 Node HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge);
711 /// Conditionally absolute/negated half float pair. Absolute is applied first 742 /// Conditionally absolute/negated half float pair. Absolute is applied first
712 Node GetOperandAbsNegHalf(Node value, bool absolute, bool negate); 743 Node GetOperandAbsNegHalf(Node value, bool absolute, bool negate);
744 /// Conditionally saturates a half float pair
745 Node GetSaturatedHalfFloat(Node value, bool saturate = true);
713 746
714 /// Returns a predicate comparing two floats 747 /// Returns a predicate comparing two floats
715 Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b); 748 Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b);
@@ -717,8 +750,7 @@ private:
717 Node GetPredicateComparisonInteger(Tegra::Shader::PredCondition condition, bool is_signed, 750 Node GetPredicateComparisonInteger(Tegra::Shader::PredCondition condition, bool is_signed,
718 Node op_a, Node op_b); 751 Node op_a, Node op_b);
719 /// Returns a predicate comparing two half floats. meta consumes how both pairs will be compared 752 /// Returns a predicate comparing two half floats. meta consumes how both pairs will be compared
720 Node GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, 753 Node GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a, Node op_b);
721 const MetaHalfArithmetic& meta, Node op_a, Node op_b);
722 754
723 /// Returns a predicate combiner operation 755 /// Returns a predicate combiner operation
724 OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); 756 OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
@@ -730,6 +762,11 @@ private:
730 const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler, 762 const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler,
731 Tegra::Shader::TextureType type, bool is_array, bool is_shadow); 763 Tegra::Shader::TextureType type, bool is_array, bool is_shadow);
732 764
765 // Accesses a texture sampler for a bindless texture.
766 const Sampler& GetBindlessSampler(const Tegra::Shader::Register& reg,
767 Tegra::Shader::TextureType type, bool is_array,
768 bool is_shadow);
769
733 /// Extracts a sequence of bits from a node 770 /// Extracts a sequence of bits from a node
734 Node BitfieldExtract(Node value, u32 offset, u32 bits); 771 Node BitfieldExtract(Node value, u32 offset, u32 bits);
735 772
@@ -743,7 +780,8 @@ private:
743 780
744 Node4 GetTexCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 781 Node4 GetTexCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
745 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, 782 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare,
746 bool is_array, bool is_aoffi); 783 bool is_array, bool is_aoffi,
784 std::optional<Tegra::Shader::Register> bindless_reg);
747 785
748 Node4 GetTexsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 786 Node4 GetTexsCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
749 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare, 787 Tegra::Shader::TextureProcessMode process_mode, bool depth_compare,
@@ -763,7 +801,8 @@ private:
763 801
764 Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 802 Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
765 Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords, 803 Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords,
766 Node array, Node depth_compare, u32 bias_offset, std::vector<Node> aoffi); 804 Node array, Node depth_compare, u32 bias_offset, std::vector<Node> aoffi,
805 std::optional<Tegra::Shader::Register> bindless_reg);
767 806
768 Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type, 807 Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type,
769 u64 byte_height); 808 u64 byte_height);
@@ -781,6 +820,11 @@ private:
781 820
782 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor); 821 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor);
783 822
823 std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb,
824 Node addr_register,
825 u32 immediate_offset,
826 bool is_write);
827
784 template <typename... T> 828 template <typename... T>
785 Node Operation(OperationCode code, const T*... operands) { 829 Node Operation(OperationCode code, const T*... operands) {
786 return StoreNode(OperationNode(code, operands...)); 830 return StoreNode(OperationNode(code, operands...));
@@ -834,7 +878,7 @@ private:
834 std::map<u32, ConstBuffer> used_cbufs; 878 std::map<u32, ConstBuffer> used_cbufs;
835 std::set<Sampler> used_samplers; 879 std::set<Sampler> used_samplers;
836 std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; 880 std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
837 std::set<GlobalMemoryBase> used_global_memory_bases; 881 std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
838 882
839 Tegra::Shader::Header header; 883 Tegra::Shader::Header header;
840}; 884};
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 3b022a456..6384fa8d2 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -178,39 +178,44 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
178 return PixelFormat::ABGR8S; 178 return PixelFormat::ABGR8S;
179 case Tegra::Texture::ComponentType::UINT: 179 case Tegra::Texture::ComponentType::UINT:
180 return PixelFormat::ABGR8UI; 180 return PixelFormat::ABGR8UI;
181 default:
182 break;
181 } 183 }
182 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 184 break;
183 UNREACHABLE();
184 case Tegra::Texture::TextureFormat::B5G6R5: 185 case Tegra::Texture::TextureFormat::B5G6R5:
185 switch (component_type) { 186 switch (component_type) {
186 case Tegra::Texture::ComponentType::UNORM: 187 case Tegra::Texture::ComponentType::UNORM:
187 return PixelFormat::B5G6R5U; 188 return PixelFormat::B5G6R5U;
189 default:
190 break;
188 } 191 }
189 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 192 break;
190 UNREACHABLE();
191 case Tegra::Texture::TextureFormat::A2B10G10R10: 193 case Tegra::Texture::TextureFormat::A2B10G10R10:
192 switch (component_type) { 194 switch (component_type) {
193 case Tegra::Texture::ComponentType::UNORM: 195 case Tegra::Texture::ComponentType::UNORM:
194 return PixelFormat::A2B10G10R10U; 196 return PixelFormat::A2B10G10R10U;
197 default:
198 break;
195 } 199 }
196 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 200 break;
197 UNREACHABLE();
198 case Tegra::Texture::TextureFormat::A1B5G5R5: 201 case Tegra::Texture::TextureFormat::A1B5G5R5:
199 switch (component_type) { 202 switch (component_type) {
200 case Tegra::Texture::ComponentType::UNORM: 203 case Tegra::Texture::ComponentType::UNORM:
201 return PixelFormat::A1B5G5R5U; 204 return PixelFormat::A1B5G5R5U;
205 default:
206 break;
202 } 207 }
203 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 208 break;
204 UNREACHABLE();
205 case Tegra::Texture::TextureFormat::R8: 209 case Tegra::Texture::TextureFormat::R8:
206 switch (component_type) { 210 switch (component_type) {
207 case Tegra::Texture::ComponentType::UNORM: 211 case Tegra::Texture::ComponentType::UNORM:
208 return PixelFormat::R8U; 212 return PixelFormat::R8U;
209 case Tegra::Texture::ComponentType::UINT: 213 case Tegra::Texture::ComponentType::UINT:
210 return PixelFormat::R8UI; 214 return PixelFormat::R8UI;
215 default:
216 break;
211 } 217 }
212 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 218 break;
213 UNREACHABLE();
214 case Tegra::Texture::TextureFormat::G8R8: 219 case Tegra::Texture::TextureFormat::G8R8:
215 // TextureFormat::G8R8 is actually ordered red then green, as such we can use 220 // TextureFormat::G8R8 is actually ordered red then green, as such we can use
216 // PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath 221 // PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath
@@ -220,50 +225,55 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
220 return PixelFormat::RG8U; 225 return PixelFormat::RG8U;
221 case Tegra::Texture::ComponentType::SNORM: 226 case Tegra::Texture::ComponentType::SNORM:
222 return PixelFormat::RG8S; 227 return PixelFormat::RG8S;
228 default:
229 break;
223 } 230 }
224 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 231 break;
225 UNREACHABLE();
226 case Tegra::Texture::TextureFormat::R16_G16_B16_A16: 232 case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
227 switch (component_type) { 233 switch (component_type) {
228 case Tegra::Texture::ComponentType::UNORM: 234 case Tegra::Texture::ComponentType::UNORM:
229 return PixelFormat::RGBA16U; 235 return PixelFormat::RGBA16U;
230 case Tegra::Texture::ComponentType::FLOAT: 236 case Tegra::Texture::ComponentType::FLOAT:
231 return PixelFormat::RGBA16F; 237 return PixelFormat::RGBA16F;
238 default:
239 break;
232 } 240 }
233 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 241 break;
234 UNREACHABLE();
235 case Tegra::Texture::TextureFormat::BF10GF11RF11: 242 case Tegra::Texture::TextureFormat::BF10GF11RF11:
236 switch (component_type) { 243 switch (component_type) {
237 case Tegra::Texture::ComponentType::FLOAT: 244 case Tegra::Texture::ComponentType::FLOAT:
238 return PixelFormat::R11FG11FB10F; 245 return PixelFormat::R11FG11FB10F;
246 default:
247 break;
239 } 248 }
240 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
241 UNREACHABLE();
242 case Tegra::Texture::TextureFormat::R32_G32_B32_A32: 249 case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
243 switch (component_type) { 250 switch (component_type) {
244 case Tegra::Texture::ComponentType::FLOAT: 251 case Tegra::Texture::ComponentType::FLOAT:
245 return PixelFormat::RGBA32F; 252 return PixelFormat::RGBA32F;
246 case Tegra::Texture::ComponentType::UINT: 253 case Tegra::Texture::ComponentType::UINT:
247 return PixelFormat::RGBA32UI; 254 return PixelFormat::RGBA32UI;
255 default:
256 break;
248 } 257 }
249 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 258 break;
250 UNREACHABLE();
251 case Tegra::Texture::TextureFormat::R32_G32: 259 case Tegra::Texture::TextureFormat::R32_G32:
252 switch (component_type) { 260 switch (component_type) {
253 case Tegra::Texture::ComponentType::FLOAT: 261 case Tegra::Texture::ComponentType::FLOAT:
254 return PixelFormat::RG32F; 262 return PixelFormat::RG32F;
255 case Tegra::Texture::ComponentType::UINT: 263 case Tegra::Texture::ComponentType::UINT:
256 return PixelFormat::RG32UI; 264 return PixelFormat::RG32UI;
265 default:
266 break;
257 } 267 }
258 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 268 break;
259 UNREACHABLE();
260 case Tegra::Texture::TextureFormat::R32_G32_B32: 269 case Tegra::Texture::TextureFormat::R32_G32_B32:
261 switch (component_type) { 270 switch (component_type) {
262 case Tegra::Texture::ComponentType::FLOAT: 271 case Tegra::Texture::ComponentType::FLOAT:
263 return PixelFormat::RGB32F; 272 return PixelFormat::RGB32F;
273 default:
274 break;
264 } 275 }
265 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 276 break;
266 UNREACHABLE();
267 case Tegra::Texture::TextureFormat::R16: 277 case Tegra::Texture::TextureFormat::R16:
268 switch (component_type) { 278 switch (component_type) {
269 case Tegra::Texture::ComponentType::FLOAT: 279 case Tegra::Texture::ComponentType::FLOAT:
@@ -276,18 +286,20 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
276 return PixelFormat::R16UI; 286 return PixelFormat::R16UI;
277 case Tegra::Texture::ComponentType::SINT: 287 case Tegra::Texture::ComponentType::SINT:
278 return PixelFormat::R16I; 288 return PixelFormat::R16I;
289 default:
290 break;
279 } 291 }
280 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 292 break;
281 UNREACHABLE();
282 case Tegra::Texture::TextureFormat::R32: 293 case Tegra::Texture::TextureFormat::R32:
283 switch (component_type) { 294 switch (component_type) {
284 case Tegra::Texture::ComponentType::FLOAT: 295 case Tegra::Texture::ComponentType::FLOAT:
285 return PixelFormat::R32F; 296 return PixelFormat::R32F;
286 case Tegra::Texture::ComponentType::UINT: 297 case Tegra::Texture::ComponentType::UINT:
287 return PixelFormat::R32UI; 298 return PixelFormat::R32UI;
299 default:
300 break;
288 } 301 }
289 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 302 break;
290 UNREACHABLE();
291 case Tegra::Texture::TextureFormat::ZF32: 303 case Tegra::Texture::TextureFormat::ZF32:
292 return PixelFormat::Z32F; 304 return PixelFormat::Z32F;
293 case Tegra::Texture::TextureFormat::Z16: 305 case Tegra::Texture::TextureFormat::Z16:
@@ -310,9 +322,10 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
310 return PixelFormat::DXN2UNORM; 322 return PixelFormat::DXN2UNORM;
311 case Tegra::Texture::ComponentType::SNORM: 323 case Tegra::Texture::ComponentType::SNORM:
312 return PixelFormat::DXN2SNORM; 324 return PixelFormat::DXN2SNORM;
325 default:
326 break;
313 } 327 }
314 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 328 break;
315 UNREACHABLE();
316 case Tegra::Texture::TextureFormat::BC7U: 329 case Tegra::Texture::TextureFormat::BC7U:
317 return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U; 330 return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U;
318 case Tegra::Texture::TextureFormat::BC6H_UF16: 331 case Tegra::Texture::TextureFormat::BC6H_UF16:
@@ -343,15 +356,17 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
343 return PixelFormat::RG16UI; 356 return PixelFormat::RG16UI;
344 case Tegra::Texture::ComponentType::SINT: 357 case Tegra::Texture::ComponentType::SINT:
345 return PixelFormat::RG16I; 358 return PixelFormat::RG16I;
359 default:
360 break;
346 } 361 }
347 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); 362 break;
348 UNREACHABLE();
349 default: 363 default:
350 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format), 364 break;
351 static_cast<u32>(component_type));
352 UNREACHABLE();
353 return PixelFormat::ABGR8U;
354 } 365 }
366 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
367 static_cast<u32>(component_type));
368 UNREACHABLE();
369 return PixelFormat::ABGR8U;
355} 370}
356 371
357ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { 372ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
@@ -513,8 +528,9 @@ bool IsFormatBCn(PixelFormat format) {
513 case PixelFormat::DXT45_SRGB: 528 case PixelFormat::DXT45_SRGB:
514 case PixelFormat::BC7U_SRGB: 529 case PixelFormat::BC7U_SRGB:
515 return true; 530 return true;
531 default:
532 return false;
516 } 533 }
517 return false;
518} 534}
519 535
520} // namespace VideoCore::Surface 536} // namespace VideoCore::Surface
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 995d0e068..217805386 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -288,6 +288,29 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
288 } 288 }
289} 289}
290 290
291void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
292 const u32 block_height, const std::size_t copy_size, const u8* source_data,
293 u8* swizzle_data) {
294 const u32 image_width_in_gobs{(width + gob_size_x - 1) / gob_size_x};
295 std::size_t count = 0;
296 for (std::size_t y = dst_y; y < height && count < copy_size; ++y) {
297 const std::size_t gob_address_y =
298 (y / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
299 ((y % (gob_size_y * block_height)) / gob_size_y) * gob_size;
300 const auto& table = legacy_swizzle_table[y % gob_size_y];
301 for (std::size_t x = dst_x; x < width && count < copy_size; ++x) {
302 const std::size_t gob_address =
303 gob_address_y + (x / gob_size_x) * gob_size * block_height;
304 const std::size_t swizzled_offset = gob_address + table[x % gob_size_x];
305 const u8* source_line = source_data + count;
306 u8* dest_addr = swizzle_data + swizzled_offset;
307 count++;
308
309 std::memcpy(dest_addr, source_line, 1);
310 }
311 }
312}
313
291std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 314std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
292 u32 height) { 315 u32 height) {
293 std::vector<u8> rgba_data; 316 std::vector<u8> rgba_data;
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index e078fa274..e072d8401 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -51,4 +51,8 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
51 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, 51 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
52 u32 offset_x, u32 offset_y); 52 u32 offset_x, u32 offset_y);
53 53
54void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
55 const u32 block_height, const std::size_t copy_size, const u8* source_data,
56 u8* swizzle_data);
57
54} // namespace Tegra::Texture 58} // namespace Tegra::Texture
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index cb82ecf3f..60cda0ca3 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -5,6 +5,8 @@
5#include <memory> 5#include <memory>
6#include "core/core.h" 6#include "core/core.h"
7#include "core/settings.h" 7#include "core/settings.h"
8#include "video_core/gpu_asynch.h"
9#include "video_core/gpu_synch.h"
8#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
9#include "video_core/renderer_opengl/renderer_opengl.h" 11#include "video_core/renderer_opengl/renderer_opengl.h"
10#include "video_core/video_core.h" 12#include "video_core/video_core.h"
@@ -16,6 +18,14 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
16 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); 18 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
17} 19}
18 20
21std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
22 if (Settings::values.use_asynchronous_gpu_emulation) {
23 return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
24 }
25
26 return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
27}
28
19u16 GetResolutionScaleFactor(const RendererBase& renderer) { 29u16 GetResolutionScaleFactor(const RendererBase& renderer) {
20 return static_cast<u16>( 30 return static_cast<u16>(
21 Settings::values.resolution_factor 31 Settings::values.resolution_factor
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 3c583f195..b8e0ac372 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -14,6 +14,10 @@ namespace Core::Frontend {
14class EmuWindow; 14class EmuWindow;
15} 15}
16 16
17namespace Tegra {
18class GPU;
19}
20
17namespace VideoCore { 21namespace VideoCore {
18 22
19class RendererBase; 23class RendererBase;
@@ -27,6 +31,9 @@ class RendererBase;
27std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 31std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
28 Core::System& system); 32 Core::System& system);
29 33
34/// Creates an emulated GPU instance using the given system context.
35std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
36
30u16 GetResolutionScaleFactor(const RendererBase& renderer); 37u16 GetResolutionScaleFactor(const RendererBase& renderer);
31 38
32} // namespace VideoCore 39} // namespace VideoCore
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 2eb86d6e5..5138bd9a3 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -7,6 +7,8 @@ add_executable(yuzu
7 Info.plist 7 Info.plist
8 about_dialog.cpp 8 about_dialog.cpp
9 about_dialog.h 9 about_dialog.h
10 applets/error.cpp
11 applets/error.h
10 applets/profile_select.cpp 12 applets/profile_select.cpp
11 applets/profile_select.h 13 applets/profile_select.h
12 applets/software_keyboard.cpp 14 applets/software_keyboard.cpp
@@ -151,6 +153,12 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core)
151target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) 153target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
152target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 154target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
153 155
156target_compile_definitions(yuzu PRIVATE
157 # Use QStringBuilder for string concatenation to reduce
158 # the overall number of temporary strings created.
159 -DQT_USE_QSTRINGBUILDER
160)
161
154if (YUZU_ENABLE_COMPATIBILITY_REPORTING) 162if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
155 target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING) 163 target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING)
156endif() 164endif()
diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp
new file mode 100644
index 000000000..1fb2fe277
--- /dev/null
+++ b/src/yuzu/applets/error.cpp
@@ -0,0 +1,59 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QDateTime>
6#include "core/hle/lock.h"
7#include "yuzu/applets/error.h"
8#include "yuzu/main.h"
9
10QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) {
11 connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent,
12 &GMainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection);
13 connect(&parent, &GMainWindow::ErrorDisplayFinished, this,
14 &QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection);
15}
16
17QtErrorDisplay::~QtErrorDisplay() = default;
18
19void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
20 this->callback = std::move(finished);
21 emit MainWindowDisplayError(
22 tr("An error has occured.\nPlease try again or contact the developer of the "
23 "software.\n\nError Code: %1-%2 (0x%3)")
24 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
25 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
26 .arg(error.raw, 8, 16, QChar::fromLatin1('0')));
27}
28
29void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
30 std::function<void()> finished) const {
31 this->callback = std::move(finished);
32 emit MainWindowDisplayError(
33 tr("An error occured on %1 at %2.\nPlease try again or contact the "
34 "developer of the software.\n\nError Code: %3-%4 (0x%5)")
35 .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy"))
36 .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A"))
37 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
38 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
39 .arg(error.raw, 8, 16, QChar::fromLatin1('0')));
40}
41
42void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text,
43 std::string fullscreen_text,
44 std::function<void()> finished) const {
45 this->callback = std::move(finished);
46 emit MainWindowDisplayError(
47 tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5")
48 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
49 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
50 .arg(error.raw, 8, 16, QChar::fromLatin1('0'))
51 .arg(QString::fromStdString(dialog_text))
52 .arg(QString::fromStdString(fullscreen_text)));
53}
54
55void QtErrorDisplay::MainWindowFinishedError() {
56 // Acquire the HLE mutex
57 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
58 callback();
59}
diff --git a/src/yuzu/applets/error.h b/src/yuzu/applets/error.h
new file mode 100644
index 000000000..b0932d895
--- /dev/null
+++ b/src/yuzu/applets/error.h
@@ -0,0 +1,33 @@
1// Copyright 2019 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 <QObject>
8
9#include "core/frontend/applets/error.h"
10
11class GMainWindow;
12
13class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet {
14 Q_OBJECT
15
16public:
17 explicit QtErrorDisplay(GMainWindow& parent);
18 ~QtErrorDisplay() override;
19
20 void ShowError(ResultCode error, std::function<void()> finished) const override;
21 void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
22 std::function<void()> finished) const override;
23 void ShowCustomErrorText(ResultCode error, std::string dialog_text, std::string fullscreen_text,
24 std::function<void()> finished) const override;
25
26signals:
27 void MainWindowDisplayError(QString error) const;
28
29private:
30 void MainWindowFinishedError();
31
32 mutable std::function<void()> callback;
33};
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index c29f2d2dc..5c98636c5 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -91,8 +91,8 @@ void EmuThread::run() {
91 91
92class GGLContext : public Core::Frontend::GraphicsContext { 92class GGLContext : public Core::Frontend::GraphicsContext {
93public: 93public:
94 explicit GGLContext(QOpenGLContext* shared_context) : surface() { 94 explicit GGLContext(QOpenGLContext* shared_context)
95 context = std::make_unique<QOpenGLContext>(shared_context); 95 : context{std::make_unique<QOpenGLContext>(shared_context)} {
96 surface.setFormat(shared_context->format()); 96 surface.setFormat(shared_context->format());
97 surface.create(); 97 surface.create();
98 } 98 }
@@ -186,8 +186,7 @@ private:
186}; 186};
187 187
188GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 188GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
189 : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) { 189 : QWidget(parent), emu_thread(emu_thread) {
190
191 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 190 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
192 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 191 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
193 setAttribute(Qt::WA_AcceptTouchEvents); 192 setAttribute(Qt::WA_AcceptTouchEvents);
@@ -378,7 +377,11 @@ void GRenderWindow::InitRenderTarget() {
378 // WA_DontShowOnScreen, WA_DeleteOnClose 377 // WA_DontShowOnScreen, WA_DeleteOnClose
379 QSurfaceFormat fmt; 378 QSurfaceFormat fmt;
380 fmt.setVersion(4, 3); 379 fmt.setVersion(4, 3);
381 fmt.setProfile(QSurfaceFormat::CoreProfile); 380 if (Settings::values.use_compatibility_profile) {
381 fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
382 } else {
383 fmt.setProfile(QSurfaceFormat::CoreProfile);
384 }
382 // TODO: expose a setting for buffer value (ie default/single/double/triple) 385 // TODO: expose a setting for buffer value (ie default/single/double/triple)
383 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); 386 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
384 shared_context = std::make_unique<QOpenGLContext>(); 387 shared_context = std::make_unique<QOpenGLContext>();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 9608b959f..3df33aca1 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -10,7 +10,6 @@
10#include <QImage> 10#include <QImage>
11#include <QThread> 11#include <QThread>
12#include <QWidget> 12#include <QWidget>
13#include "common/thread.h"
14#include "core/core.h" 13#include "core/core.h"
15#include "core/frontend/emu_window.h" 14#include "core/frontend/emu_window.h"
16 15
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 8725a78dc..6c6f047d8 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -389,6 +389,8 @@ void Config::ReadValues() {
389 Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat(); 389 Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat();
390 Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool(); 390 Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool();
391 Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt(); 391 Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt();
392 Settings::values.use_compatibility_profile =
393 ReadSetting("use_compatibility_profile", true).toBool();
392 Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool(); 394 Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool();
393 Settings::values.use_accurate_gpu_emulation = 395 Settings::values.use_accurate_gpu_emulation =
394 ReadSetting("use_accurate_gpu_emulation", false).toBool(); 396 ReadSetting("use_accurate_gpu_emulation", false).toBool();
@@ -661,6 +663,7 @@ void Config::SaveValues() {
661 WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0); 663 WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0);
662 WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true); 664 WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true);
663 WriteSetting("frame_limit", Settings::values.frame_limit, 100); 665 WriteSetting("frame_limit", Settings::values.frame_limit, 100);
666 WriteSetting("use_compatibility_profile", Settings::values.use_compatibility_profile, true);
664 WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true); 667 WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true);
665 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); 668 WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
666 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, 669 WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 51bd1f121..a5218b051 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -12,7 +12,7 @@
12#include "yuzu/hotkeys.h" 12#include "yuzu/hotkeys.h"
13 13
14ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) 14ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
15 : QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) { 15 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
16 ui->setupUi(this); 16 ui->setupUi(this);
17 ui->hotkeysTab->Populate(registry); 17 ui->hotkeysTab->Populate(registry);
18 this->setConfiguration(); 18 this->setConfiguration();
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 0a9883d37..c299c0b5b 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -73,6 +73,7 @@ void ConfigureGraphics::setConfiguration() {
73 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 73 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
74 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 74 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
75 ui->frame_limit->setValue(Settings::values.frame_limit); 75 ui->frame_limit->setValue(Settings::values.frame_limit);
76 ui->use_compatibility_profile->setChecked(Settings::values.use_compatibility_profile);
76 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); 77 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
77 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); 78 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
78 ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 79 ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
@@ -88,6 +89,7 @@ void ConfigureGraphics::applyConfiguration() {
88 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 89 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
89 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 90 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
90 Settings::values.frame_limit = ui->frame_limit->value(); 91 Settings::values.frame_limit = ui->frame_limit->value();
92 Settings::values.use_compatibility_profile = ui->use_compatibility_profile->isChecked();
91 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); 93 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
92 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); 94 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
93 Settings::values.use_asynchronous_gpu_emulation = 95 Settings::values.use_asynchronous_gpu_emulation =
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 15ab18ecd..0f6f6c003 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -50,6 +50,13 @@
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_compatibility_profile">
54 <property name="text">
55 <string>Use OpenGL compatibility profile</string>
56 </property>
57 </widget>
58 </item>
59 <item>
53 <widget class="QCheckBox" name="use_disk_shader_cache"> 60 <widget class="QCheckBox" name="use_disk_shader_cache">
54 <property name="text"> 61 <property name="text">
55 <string>Use disk shader cache</string> 62 <string>Use disk shader cache</string>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index bfb562535..a7a8752e5 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -66,20 +66,21 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
66} 66}
67 67
68void ConfigureHotkeys::Configure(QModelIndex index) { 68void ConfigureHotkeys::Configure(QModelIndex index) {
69 if (index.parent() == QModelIndex()) 69 if (!index.parent().isValid()) {
70 return; 70 return;
71 }
71 72
72 index = index.sibling(index.row(), 1); 73 index = index.sibling(index.row(), 1);
73 auto* model = ui->hotkey_list->model(); 74 auto* const model = ui->hotkey_list->model();
74 auto previous_key = model->data(index); 75 const auto previous_key = model->data(index);
75
76 auto* hotkey_dialog = new SequenceDialog;
77 int return_code = hotkey_dialog->exec();
78 76
79 auto key_sequence = hotkey_dialog->GetSequence(); 77 SequenceDialog hotkey_dialog{this};
80 78
81 if (return_code == QDialog::Rejected || key_sequence.isEmpty()) 79 const int return_code = hotkey_dialog.exec();
80 const auto key_sequence = hotkey_dialog.GetSequence();
81 if (return_code == QDialog::Rejected || key_sequence.isEmpty()) {
82 return; 82 return;
83 }
83 84
84 if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { 85 if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) {
85 QMessageBox::critical(this, tr("Error in inputted key"), 86 QMessageBox::critical(this, tr("Error in inputted key"),
@@ -90,7 +91,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
90 } 91 }
91} 92}
92 93
93bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) { 94bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
94 return GetUsedKeyList().contains(key_sequence); 95 return GetUsedKeyList().contains(key_sequence);
95} 96}
96 97
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h
index cd203aad6..73fb8a175 100644
--- a/src/yuzu/configuration/configure_hotkeys.h
+++ b/src/yuzu/configuration/configure_hotkeys.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9#include "core/settings.h"
10 9
11namespace Ui { 10namespace Ui {
12class ConfigureHotkeys; 11class ConfigureHotkeys;
@@ -39,7 +38,7 @@ signals:
39 38
40private: 39private:
41 void Configure(QModelIndex index); 40 void Configure(QModelIndex index);
42 bool IsUsedKey(QKeySequence key_sequence); 41 bool IsUsedKey(QKeySequence key_sequence) const;
43 QList<QKeySequence> GetUsedKeyList() const; 42 QList<QKeySequence> GetUsedKeyList() const;
44 43
45 std::unique_ptr<Ui::ConfigureHotkeys> ui; 44 std::unique_ptr<Ui::ConfigureHotkeys> ui;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 593bb681f..85b095688 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -227,8 +227,7 @@ QString WaitTreeThread::GetText() const {
227 case Kernel::ThreadStatus::WaitIPC: 227 case Kernel::ThreadStatus::WaitIPC:
228 status = tr("waiting for IPC reply"); 228 status = tr("waiting for IPC reply");
229 break; 229 break;
230 case Kernel::ThreadStatus::WaitSynchAll: 230 case Kernel::ThreadStatus::WaitSynch:
231 case Kernel::ThreadStatus::WaitSynchAny:
232 status = tr("waiting for objects"); 231 status = tr("waiting for objects");
233 break; 232 break;
234 case Kernel::ThreadStatus::WaitMutex: 233 case Kernel::ThreadStatus::WaitMutex:
@@ -269,8 +268,7 @@ QColor WaitTreeThread::GetColor() const {
269 return QColor(Qt::GlobalColor::darkRed); 268 return QColor(Qt::GlobalColor::darkRed);
270 case Kernel::ThreadStatus::WaitSleep: 269 case Kernel::ThreadStatus::WaitSleep:
271 return QColor(Qt::GlobalColor::darkYellow); 270 return QColor(Qt::GlobalColor::darkYellow);
272 case Kernel::ThreadStatus::WaitSynchAll: 271 case Kernel::ThreadStatus::WaitSynch:
273 case Kernel::ThreadStatus::WaitSynchAny:
274 case Kernel::ThreadStatus::WaitMutex: 272 case Kernel::ThreadStatus::WaitMutex:
275 case Kernel::ThreadStatus::WaitCondVar: 273 case Kernel::ThreadStatus::WaitCondVar:
276 case Kernel::ThreadStatus::WaitArb: 274 case Kernel::ThreadStatus::WaitArb:
@@ -325,10 +323,9 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
325 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); 323 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
326 } 324 }
327 325
328 if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAny || 326 if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) {
329 thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAll) {
330 list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(), 327 list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(),
331 thread.IsSleepingOnWaitAll())); 328 thread.IsSleepingOnWait()));
332 } 329 }
333 330
334 list.push_back(std::make_unique<WaitTreeCallstack>(thread)); 331 list.push_back(std::make_unique<WaitTreeCallstack>(thread));
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 3db0e90da..2cf5c58a0 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -95,7 +95,7 @@ public:
95 if (row2.isEmpty()) 95 if (row2.isEmpty())
96 return row1; 96 return row1;
97 97
98 return row1 + "\n " + row2; 98 return QString(row1 + "\n " + row2);
99 } 99 }
100 100
101 return GameListItem::data(role); 101 return GameListItem::data(role);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bdee44b04..e33e3aaaf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,6 +8,7 @@
8#include <thread> 8#include <thread>
9 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 "applets/error.h"
11#include "applets/profile_select.h" 12#include "applets/profile_select.h"
12#include "applets/software_keyboard.h" 13#include "applets/software_keyboard.h"
13#include "applets/web_browser.h" 14#include "applets/web_browser.h"
@@ -15,6 +16,7 @@
15#include "configuration/configure_per_general.h" 16#include "configuration/configure_per_general.h"
16#include "core/file_sys/vfs.h" 17#include "core/file_sys/vfs.h"
17#include "core/file_sys/vfs_real.h" 18#include "core/file_sys/vfs_real.h"
19#include "core/frontend/applets/general_frontend.h"
18#include "core/frontend/scope_acquire_window_context.h" 20#include "core/frontend/scope_acquire_window_context.h"
19#include "core/hle/service/acc/profile_manager.h" 21#include "core/hle/service/acc/profile_manager.h"
20#include "core/hle/service/am/applets/applets.h" 22#include "core/hle/service/am/applets/applets.h"
@@ -795,9 +797,13 @@ bool GMainWindow::LoadROM(const QString& filename) {
795 797
796 system.SetGPUDebugContext(debug_context); 798 system.SetGPUDebugContext(debug_context);
797 799
798 system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this)); 800 system.SetAppletFrontendSet({
799 system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this)); 801 std::make_unique<QtErrorDisplay>(*this),
800 system.SetWebBrowser(std::make_unique<QtWebBrowser>(*this)); 802 nullptr,
803 std::make_unique<QtProfileSelector>(*this),
804 std::make_unique<QtSoftwareKeyboard>(*this),
805 std::make_unique<QtWebBrowser>(*this),
806 });
801 807
802 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 808 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
803 809
@@ -1583,6 +1589,11 @@ void GMainWindow::OnLoadComplete() {
1583 loading_screen->OnLoadComplete(); 1589 loading_screen->OnLoadComplete();
1584} 1590}
1585 1591
1592void GMainWindow::ErrorDisplayDisplayError(QString body) {
1593 QMessageBox::critical(this, tr("Error Display"), body);
1594 emit ErrorDisplayFinished();
1595}
1596
1586void GMainWindow::OnMenuReportCompatibility() { 1597void GMainWindow::OnMenuReportCompatibility() {
1587 if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { 1598 if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) {
1588 CompatDB compatdb{this}; 1599 CompatDB compatdb{this};
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index ce5045819..fb2a193cb 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -102,6 +102,8 @@ signals:
102 // Signal that tells widgets to update icons to use the current theme 102 // Signal that tells widgets to update icons to use the current theme
103 void UpdateThemedIcons(); 103 void UpdateThemedIcons();
104 104
105 void ErrorDisplayFinished();
106
105 void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid); 107 void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid);
106 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); 108 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
107 void SoftwareKeyboardFinishedCheckDialog(); 109 void SoftwareKeyboardFinishedCheckDialog();
@@ -111,6 +113,7 @@ signals:
111 113
112public slots: 114public slots:
113 void OnLoadComplete(); 115 void OnLoadComplete();
116 void ErrorDisplayDisplayError(QString body);
114 void ProfileSelectorSelectProfile(); 117 void ProfileSelectorSelectProfile();
115 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); 118 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
116 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); 119 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index f24cc77fe..d0ae058fd 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -349,6 +349,8 @@ void Config::ReadValues() {
349 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 349 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
350 Settings::values.frame_limit = 350 Settings::values.frame_limit =
351 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 351 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
352 Settings::values.use_compatibility_profile =
353 sdl2_config->GetBoolean("Renderer", "use_compatibility_profile", true);
352 Settings::values.use_disk_shader_cache = 354 Settings::values.use_disk_shader_cache =
353 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); 355 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
354 Settings::values.use_accurate_gpu_emulation = 356 Settings::values.use_accurate_gpu_emulation =
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 7ea4a1b18..a1d7879b1 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -32,11 +32,7 @@
32#include "yuzu_cmd/config.h" 32#include "yuzu_cmd/config.h"
33#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 33#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
34 34
35#include <getopt.h>
36#include "core/file_sys/registered_cache.h" 35#include "core/file_sys/registered_cache.h"
37#ifndef _MSC_VER
38#include <unistd.h>
39#endif
40 36
41#ifdef _WIN32 37#ifdef _WIN32
42// windows.h needs to be included before shellapi.h 38// windows.h needs to be included before shellapi.h
@@ -45,6 +41,12 @@
45#include <shellapi.h> 41#include <shellapi.h>
46#endif 42#endif
47 43
44#undef _UNICODE
45#include <getopt.h>
46#ifndef _MSC_VER
47#include <unistd.h>
48#endif
49
48#ifdef _WIN32 50#ifdef _WIN32
49extern "C" { 51extern "C" {
50// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable 52// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable