summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/common_funcs.h4
-rw-r--r--src/common/common_paths.h3
-rw-r--r--src/common/file_util.cpp113
-rw-r--r--src/common/file_util.h11
-rw-r--r--src/common/logging/backend.cpp157
-rw-r--r--src/common/logging/backend.h87
-rw-r--r--src/common/logging/filter.cpp8
-rw-r--r--src/common/logging/log.h14
-rw-r--r--src/common/memory_util.cpp24
-rw-r--r--src/common/param_package.cpp12
-rw-r--r--src/common/string_util.cpp8
-rw-r--r--src/common/swap.h2
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp4
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core_cpu.cpp4
-rw-r--r--src/core/core_timing.cpp16
-rw-r--r--src/core/file_sys/disk_filesystem.cpp20
-rw-r--r--src/core/file_sys/filesystem.cpp6
-rw-r--r--src/core/file_sys/partition_filesystem.cpp34
-rw-r--r--src/core/file_sys/partition_filesystem.h8
-rw-r--r--src/core/file_sys/program_metadata.cpp38
-rw-r--r--src/core/file_sys/romfs_factory.cpp6
-rw-r--r--src/core/file_sys/romfs_filesystem.cpp34
-rw-r--r--src/core/file_sys/savedata_factory.cpp13
-rw-r--r--src/core/file_sys/sdmc_factory.cpp4
-rw-r--r--src/core/frontend/input.h6
-rw-r--r--src/core/gdbstub/gdbstub.cpp71
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp173
-rw-r--r--src/core/hle/kernel/address_arbiter.h32
-rw-r--r--src/core/hle/kernel/errors.h12
-rw-r--r--src/core/hle/kernel/handle_table.cpp4
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp11
-rw-r--r--src/core/hle/kernel/mutex.cpp4
-rw-r--r--src/core/hle/kernel/process.cpp8
-rw-r--r--src/core/hle/kernel/resource_limit.cpp6
-rw-r--r--src/core/hle/kernel/scheduler.cpp6
-rw-r--r--src/core/hle/kernel/server_session.cpp6
-rw-r--r--src/core/hle/kernel/shared_memory.cpp10
-rw-r--r--src/core/hle/kernel/svc.cpp193
-rw-r--r--src/core/hle/kernel/svc_wrap.h14
-rw-r--r--src/core/hle/kernel/thread.cpp18
-rw-r--r--src/core/hle/kernel/thread.h4
-rw-r--r--src/core/hle/kernel/timer.cpp4
-rw-r--r--src/core/hle/kernel/vm_manager.cpp20
-rw-r--r--src/core/hle/service/acc/acc.cpp20
-rw-r--r--src/core/hle/service/am/am.cpp86
-rw-r--r--src/core/hle/service/am/applet_ae.cpp44
-rw-r--r--src/core/hle/service/am/applet_oe.cpp18
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp4
-rw-r--r--src/core/hle/service/apm/interface.cpp6
-rw-r--r--src/core/hle/service/audio/audio.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp16
-rw-r--r--src/core/hle/service/audio/audren_u.cpp268
-rw-r--r--src/core/hle/service/audio/audren_u.h37
-rw-r--r--src/core/hle/service/audio/hwopus.cpp29
-rw-r--r--src/core/hle/service/audio/hwopus.h20
-rw-r--r--src/core/hle/service/bcat/module.cpp2
-rw-r--r--src/core/hle/service/fatal/fatal.cpp4
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp8
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp50
-rw-r--r--src/core/hle/service/friend/friend.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp61
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/lm/lm.cpp12
-rw-r--r--src/core/hle/service/mm/mm_u.cpp6
-rw-r--r--src/core/hle/service/nfp/nfp.cpp18
-rw-r--r--src/core/hle/service/nifm/nifm.cpp24
-rw-r--r--src/core/hle/service/ns/pl_u.cpp14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp39
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp33
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp22
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp45
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp25
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h2
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp14
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp4
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp2
-rw-r--r--src/core/hle/service/pctl/module.cpp6
-rw-r--r--src/core/hle/service/prepo/prepo.cpp2
-rw-r--r--src/core/hle/service/service.cpp8
-rw-r--r--src/core/hle/service/set/set.cpp7
-rw-r--r--src/core/hle/service/set/set_sys.cpp2
-rw-r--r--src/core/hle/service/sm/controller.cpp8
-rw-r--r--src/core/hle/service/sm/sm.cpp7
-rw-r--r--src/core/hle/service/sockets/bsd.cpp13
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp2
-rw-r--r--src/core/hle/service/spl/module.cpp2
-rw-r--r--src/core/hle/service/ssl/ssl.cpp8
-rw-r--r--src/core/hle/service/time/time.cpp26
-rw-r--r--src/core/hle/service/vi/vi.cpp54
-rw-r--r--src/core/hw/hw.cpp8
-rw-r--r--src/core/hw/lcd.cpp8
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp8
-rw-r--r--src/core/loader/elf.cpp26
-rw-r--r--src/core/loader/linker.cpp4
-rw-r--r--src/core/loader/loader.cpp18
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nca.cpp303
-rw-r--r--src/core/loader/nca.h49
-rw-r--r--src/core/loader/nso.cpp83
-rw-r--r--src/core/loader/nso.h3
-rw-r--r--src/core/memory.cpp52
-rw-r--r--src/core/memory.h7
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp8
-rw-r--r--src/core/tracer/recorder.cpp2
-rw-r--r--src/input_common/sdl/sdl.cpp6
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/command_processor.cpp18
-rw-r--r--src/video_core/debug_utils/debug_utils.h6
-rw-r--r--src/video_core/engines/fermi_2d.cpp5
-rw-r--r--src/video_core/engines/maxwell_3d.cpp21
-rw-r--r--src/video_core/engines/maxwell_3d.h156
-rw-r--r--src/video_core/engines/maxwell_dma.cpp73
-rw-r--r--src/video_core/engines/maxwell_dma.h155
-rw-r--r--src/video_core/engines/shader_bytecode.h91
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/gpu.h13
-rw-r--r--src/video_core/memory_manager.cpp4
-rw-r--r--src/video_core/rasterizer_interface.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp308
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h16
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp1555
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h426
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp460
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h8
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp83
-rw-r--r--src/video_core/renderer_opengl/gl_state.h43
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h89
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp17
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h2
-rw-r--r--src/video_core/textures/astc.cpp1646
-rw-r--r--src/video_core/textures/astc.h15
-rw-r--r--src/video_core/textures/decoders.cpp45
-rw-r--r--src/video_core/textures/decoders.h6
-rw-r--r--src/video_core/video_core.cpp6
-rw-r--r--src/yuzu/CMakeLists.txt13
-rw-r--r--src/yuzu/bootmanager.cpp13
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_debug.cpp22
-rw-r--r--src/yuzu/configuration/configure_debug.ui41
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/debugger/console.cpp45
-rw-r--r--src/yuzu/debugger/console.h14
-rw-r--r--src/yuzu/debugger/registers.cpp190
-rw-r--r--src/yuzu/debugger/registers.h42
-rw-r--r--src/yuzu/debugger/registers.ui40
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/game_list.cpp37
-rw-r--r--src/yuzu/main.cpp55
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu/ui_settings.h3
-rw-r--r--src/yuzu_cmd/config.cpp8
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp20
-rw-r--r--src/yuzu_cmd/yuzu.cpp34
166 files changed, 5740 insertions, 3114 deletions
diff --git a/src/common/assert.h b/src/common/assert.h
index 3ee07f6a2..0d4eddc19 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -30,15 +30,14 @@ __declspec(noinline, noreturn)
30#define ASSERT(_a_) \ 30#define ASSERT(_a_) \
31 do \ 31 do \
32 if (!(_a_)) { \ 32 if (!(_a_)) { \
33 assert_noinline_call([] { NGLOG_CRITICAL(Debug, "Assertion Failed!"); }); \ 33 assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
34 } \ 34 } \
35 while (0) 35 while (0)
36 36
37#define ASSERT_MSG(_a_, ...) \ 37#define ASSERT_MSG(_a_, ...) \
38 do \ 38 do \
39 if (!(_a_)) { \ 39 if (!(_a_)) { \
40 assert_noinline_call( \ 40 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
41 [&] { NGLOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
42 } \ 41 } \
43 while (0) 42 while (0)
44 43
@@ -53,5 +52,5 @@ __declspec(noinline, noreturn)
53#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) 52#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
54#endif 53#endif
55 54
56#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!") 55#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!")
57#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) 56#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 7cf7b7997..995938d0b 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) 7#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM)
8#include <cstdlib> // for exit 8#include <cstdlib> // for exit
9#endif 9#endif
10#include "common/common_types.h" 10#include "common/common_types.h"
@@ -30,7 +30,7 @@
30 30
31#ifdef ARCHITECTURE_x86_64 31#ifdef ARCHITECTURE_x86_64
32#define Crash() __asm__ __volatile__("int $3") 32#define Crash() __asm__ __volatile__("int $3")
33#elif defined(_M_ARM) 33#elif defined(ARCHITECTURE_ARM)
34#define Crash() __asm__ __volatile__("trap") 34#define Crash() __asm__ __volatile__("trap")
35#else 35#else
36#define Crash() exit(1) 36#define Crash() exit(1)
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 0a6132dab..9bf3efaf2 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -32,12 +32,15 @@
32#define SDMC_DIR "sdmc" 32#define SDMC_DIR "sdmc"
33#define NAND_DIR "nand" 33#define NAND_DIR "nand"
34#define SYSDATA_DIR "sysdata" 34#define SYSDATA_DIR "sysdata"
35#define LOG_DIR "log"
35 36
36// Filenames 37// Filenames
37// Files in the directory returned by GetUserPath(D_CONFIG_IDX) 38// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
38#define EMU_CONFIG "emu.ini" 39#define EMU_CONFIG "emu.ini"
39#define DEBUGGER_CONFIG "debugger.ini" 40#define DEBUGGER_CONFIG "debugger.ini"
40#define LOGGER_CONFIG "logger.ini" 41#define LOGGER_CONFIG "logger.ini"
42// Files in the directory returned by GetUserPath(D_LOGS_IDX)
43#define LOG_FILE "yuzu_log.txt"
41 44
42// Sys files 45// Sys files
43#define SHARED_FONT "shared_font.bin" 46#define SHARED_FONT "shared_font.bin"
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 2d0b81c6e..7213abe18 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -118,7 +118,7 @@ bool IsDirectory(const std::string& filename) {
118#endif 118#endif
119 119
120 if (result < 0) { 120 if (result < 0) {
121 NGLOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); 121 LOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg());
122 return false; 122 return false;
123 } 123 }
124 124
@@ -128,29 +128,29 @@ bool IsDirectory(const std::string& filename) {
128// Deletes a given filename, return true on success 128// Deletes a given filename, return true on success
129// Doesn't supports deleting a directory 129// Doesn't supports deleting a directory
130bool Delete(const std::string& filename) { 130bool Delete(const std::string& filename) {
131 NGLOG_TRACE(Common_Filesystem, "file {}", filename); 131 LOG_TRACE(Common_Filesystem, "file {}", filename);
132 132
133 // Return true because we care about the file no 133 // Return true because we care about the file no
134 // being there, not the actual delete. 134 // being there, not the actual delete.
135 if (!Exists(filename)) { 135 if (!Exists(filename)) {
136 NGLOG_DEBUG(Common_Filesystem, "{} does not exist", filename); 136 LOG_DEBUG(Common_Filesystem, "{} does not exist", filename);
137 return true; 137 return true;
138 } 138 }
139 139
140 // We can't delete a directory 140 // We can't delete a directory
141 if (IsDirectory(filename)) { 141 if (IsDirectory(filename)) {
142 NGLOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); 142 LOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename);
143 return false; 143 return false;
144 } 144 }
145 145
146#ifdef _WIN32 146#ifdef _WIN32
147 if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) { 147 if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) {
148 NGLOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); 148 LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg());
149 return false; 149 return false;
150 } 150 }
151#else 151#else
152 if (unlink(filename.c_str()) == -1) { 152 if (unlink(filename.c_str()) == -1) {
153 NGLOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); 153 LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg());
154 return false; 154 return false;
155 } 155 }
156#endif 156#endif
@@ -160,16 +160,16 @@ bool Delete(const std::string& filename) {
160 160
161// Returns true if successful, or path already exists. 161// Returns true if successful, or path already exists.
162bool CreateDir(const std::string& path) { 162bool CreateDir(const std::string& path) {
163 NGLOG_TRACE(Common_Filesystem, "directory {}", path); 163 LOG_TRACE(Common_Filesystem, "directory {}", path);
164#ifdef _WIN32 164#ifdef _WIN32
165 if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) 165 if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr))
166 return true; 166 return true;
167 DWORD error = GetLastError(); 167 DWORD error = GetLastError();
168 if (error == ERROR_ALREADY_EXISTS) { 168 if (error == ERROR_ALREADY_EXISTS) {
169 NGLOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); 169 LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path);
170 return true; 170 return true;
171 } 171 }
172 NGLOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); 172 LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error);
173 return false; 173 return false;
174#else 174#else
175 if (mkdir(path.c_str(), 0755) == 0) 175 if (mkdir(path.c_str(), 0755) == 0)
@@ -178,11 +178,11 @@ bool CreateDir(const std::string& path) {
178 int err = errno; 178 int err = errno;
179 179
180 if (err == EEXIST) { 180 if (err == EEXIST) {
181 NGLOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); 181 LOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path);
182 return true; 182 return true;
183 } 183 }
184 184
185 NGLOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); 185 LOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err));
186 return false; 186 return false;
187#endif 187#endif
188} 188}
@@ -190,10 +190,10 @@ bool CreateDir(const std::string& path) {
190// Creates the full path of fullPath returns true on success 190// Creates the full path of fullPath returns true on success
191bool CreateFullPath(const std::string& fullPath) { 191bool CreateFullPath(const std::string& fullPath) {
192 int panicCounter = 100; 192 int panicCounter = 100;
193 NGLOG_TRACE(Common_Filesystem, "path {}", fullPath); 193 LOG_TRACE(Common_Filesystem, "path {}", fullPath);
194 194
195 if (FileUtil::Exists(fullPath)) { 195 if (FileUtil::Exists(fullPath)) {
196 NGLOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); 196 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
197 return true; 197 return true;
198 } 198 }
199 199
@@ -209,14 +209,14 @@ bool CreateFullPath(const std::string& fullPath) {
209 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") 209 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
210 std::string const subPath(fullPath.substr(0, position + 1)); 210 std::string const subPath(fullPath.substr(0, position + 1));
211 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { 211 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
212 NGLOG_ERROR(Common, "CreateFullPath: directory creation failed"); 212 LOG_ERROR(Common, "CreateFullPath: directory creation failed");
213 return false; 213 return false;
214 } 214 }
215 215
216 // A safety check 216 // A safety check
217 panicCounter--; 217 panicCounter--;
218 if (panicCounter <= 0) { 218 if (panicCounter <= 0) {
219 NGLOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); 219 LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
220 return false; 220 return false;
221 } 221 }
222 position++; 222 position++;
@@ -225,11 +225,11 @@ bool CreateFullPath(const std::string& fullPath) {
225 225
226// Deletes a directory filename, returns true on success 226// Deletes a directory filename, returns true on success
227bool DeleteDir(const std::string& filename) { 227bool DeleteDir(const std::string& filename) {
228 NGLOG_TRACE(Common_Filesystem, "directory {}", filename); 228 LOG_TRACE(Common_Filesystem, "directory {}", filename);
229 229
230 // check if a directory 230 // check if a directory
231 if (!FileUtil::IsDirectory(filename)) { 231 if (!FileUtil::IsDirectory(filename)) {
232 NGLOG_ERROR(Common_Filesystem, "Not a directory {}", filename); 232 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
233 return false; 233 return false;
234 } 234 }
235 235
@@ -240,14 +240,14 @@ bool DeleteDir(const std::string& filename) {
240 if (rmdir(filename.c_str()) == 0) 240 if (rmdir(filename.c_str()) == 0)
241 return true; 241 return true;
242#endif 242#endif
243 NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); 243 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
244 244
245 return false; 245 return false;
246} 246}
247 247
248// renames file srcFilename to destFilename, returns true on success 248// renames file srcFilename to destFilename, returns true on success
249bool Rename(const std::string& srcFilename, const std::string& destFilename) { 249bool Rename(const std::string& srcFilename, const std::string& destFilename) {
250 NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); 250 LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
251#ifdef _WIN32 251#ifdef _WIN32
252 if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), 252 if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(),
253 Common::UTF8ToUTF16W(destFilename).c_str()) == 0) 253 Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
@@ -256,21 +256,21 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
256 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) 256 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
257 return true; 257 return true;
258#endif 258#endif
259 NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, 259 LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename,
260 GetLastErrorMsg()); 260 GetLastErrorMsg());
261 return false; 261 return false;
262} 262}
263 263
264// copies file srcFilename to destFilename, returns true on success 264// copies file srcFilename to destFilename, returns true on success
265bool Copy(const std::string& srcFilename, const std::string& destFilename) { 265bool Copy(const std::string& srcFilename, const std::string& destFilename) {
266 NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); 266 LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
267#ifdef _WIN32 267#ifdef _WIN32
268 if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), 268 if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(),
269 Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) 269 Common::UTF8ToUTF16W(destFilename).c_str(), FALSE))
270 return true; 270 return true;
271 271
272 NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, 272 LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename,
273 GetLastErrorMsg()); 273 GetLastErrorMsg());
274 return false; 274 return false;
275#else 275#else
276 276
@@ -282,8 +282,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
282 // Open input file 282 // Open input file
283 FILE* input = fopen(srcFilename.c_str(), "rb"); 283 FILE* input = fopen(srcFilename.c_str(), "rb");
284 if (!input) { 284 if (!input) {
285 NGLOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, 285 LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename,
286 destFilename, GetLastErrorMsg()); 286 destFilename, GetLastErrorMsg());
287 return false; 287 return false;
288 } 288 }
289 289
@@ -291,8 +291,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
291 FILE* output = fopen(destFilename.c_str(), "wb"); 291 FILE* output = fopen(destFilename.c_str(), "wb");
292 if (!output) { 292 if (!output) {
293 fclose(input); 293 fclose(input);
294 NGLOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, 294 LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename,
295 destFilename, GetLastErrorMsg()); 295 destFilename, GetLastErrorMsg());
296 return false; 296 return false;
297 } 297 }
298 298
@@ -302,8 +302,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
302 size_t rnum = fread(buffer, sizeof(char), BSIZE, input); 302 size_t rnum = fread(buffer, sizeof(char), BSIZE, input);
303 if (rnum != BSIZE) { 303 if (rnum != BSIZE) {
304 if (ferror(input) != 0) { 304 if (ferror(input) != 0) {
305 NGLOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", 305 LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
306 srcFilename, destFilename, GetLastErrorMsg()); 306 srcFilename, destFilename, GetLastErrorMsg());
307 goto bail; 307 goto bail;
308 } 308 }
309 } 309 }
@@ -311,8 +311,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
311 // write output 311 // write output
312 size_t wnum = fwrite(buffer, sizeof(char), rnum, output); 312 size_t wnum = fwrite(buffer, sizeof(char), rnum, output);
313 if (wnum != rnum) { 313 if (wnum != rnum) {
314 NGLOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, 314 LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
315 destFilename, GetLastErrorMsg()); 315 destFilename, GetLastErrorMsg());
316 goto bail; 316 goto bail;
317 } 317 }
318 } 318 }
@@ -332,12 +332,12 @@ bail:
332// Returns the size of filename (64bit) 332// Returns the size of filename (64bit)
333u64 GetSize(const std::string& filename) { 333u64 GetSize(const std::string& filename) {
334 if (!Exists(filename)) { 334 if (!Exists(filename)) {
335 NGLOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); 335 LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename);
336 return 0; 336 return 0;
337 } 337 }
338 338
339 if (IsDirectory(filename)) { 339 if (IsDirectory(filename)) {
340 NGLOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); 340 LOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename);
341 return 0; 341 return 0;
342 } 342 }
343 343
@@ -348,11 +348,11 @@ u64 GetSize(const std::string& filename) {
348 if (stat(filename.c_str(), &buf) == 0) 348 if (stat(filename.c_str(), &buf) == 0)
349#endif 349#endif
350 { 350 {
351 NGLOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); 351 LOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size);
352 return buf.st_size; 352 return buf.st_size;
353 } 353 }
354 354
355 NGLOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); 355 LOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg());
356 return 0; 356 return 0;
357} 357}
358 358
@@ -360,7 +360,7 @@ u64 GetSize(const std::string& filename) {
360u64 GetSize(const int fd) { 360u64 GetSize(const int fd) {
361 struct stat buf; 361 struct stat buf;
362 if (fstat(fd, &buf) != 0) { 362 if (fstat(fd, &buf) != 0) {
363 NGLOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); 363 LOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg());
364 return 0; 364 return 0;
365 } 365 }
366 return buf.st_size; 366 return buf.st_size;
@@ -371,14 +371,12 @@ u64 GetSize(FILE* f) {
371 // can't use off_t here because it can be 32-bit 371 // can't use off_t here because it can be 32-bit
372 u64 pos = ftello(f); 372 u64 pos = ftello(f);
373 if (fseeko(f, 0, SEEK_END) != 0) { 373 if (fseeko(f, 0, SEEK_END) != 0) {
374 NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), 374 LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg());
375 GetLastErrorMsg());
376 return 0; 375 return 0;
377 } 376 }
378 u64 size = ftello(f); 377 u64 size = ftello(f);
379 if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { 378 if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
380 NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), 379 LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg());
381 GetLastErrorMsg());
382 return 0; 380 return 0;
383 } 381 }
384 return size; 382 return size;
@@ -386,10 +384,10 @@ u64 GetSize(FILE* f) {
386 384
387// creates an empty file filename, returns true on success 385// creates an empty file filename, returns true on success
388bool CreateEmptyFile(const std::string& filename) { 386bool CreateEmptyFile(const std::string& filename) {
389 NGLOG_TRACE(Common_Filesystem, "{}", filename); 387 LOG_TRACE(Common_Filesystem, "{}", filename);
390 388
391 if (!FileUtil::IOFile(filename, "wb")) { 389 if (!FileUtil::IOFile(filename, "wb")) {
392 NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); 390 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
393 return false; 391 return false;
394 } 392 }
395 393
@@ -398,7 +396,7 @@ bool CreateEmptyFile(const std::string& filename) {
398 396
399bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory, 397bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
400 DirectoryEntryCallable callback) { 398 DirectoryEntryCallable callback) {
401 NGLOG_TRACE(Common_Filesystem, "directory {}", directory); 399 LOG_TRACE(Common_Filesystem, "directory {}", directory);
402 400
403 // How many files + directories we found 401 // How many files + directories we found
404 unsigned found_entries = 0; 402 unsigned found_entries = 0;
@@ -556,7 +554,7 @@ std::string GetCurrentDir() {
556 char* dir; 554 char* dir;
557 if (!(dir = getcwd(nullptr, 0))) { 555 if (!(dir = getcwd(nullptr, 0))) {
558#endif 556#endif
559 NGLOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); 557 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
560 return nullptr; 558 return nullptr;
561 } 559 }
562#ifdef _WIN32 560#ifdef _WIN32
@@ -676,11 +674,11 @@ std::string GetSysDirectory() {
676#endif 674#endif
677 sysDir += DIR_SEP; 675 sysDir += DIR_SEP;
678 676
679 NGLOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir); 677 LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir);
680 return sysDir; 678 return sysDir;
681} 679}
682 680
683// Returns a string with a Citra data dir or file in the user's home 681// Returns a string with a yuzu data dir or file in the user's home
684// directory. To be used in "multi-user" mode (that is, installed). 682// directory. To be used in "multi-user" mode (that is, installed).
685const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) { 683const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) {
686 static std::string paths[NUM_PATH_INDICES]; 684 static std::string paths[NUM_PATH_INDICES];
@@ -692,7 +690,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
692 if (!FileUtil::IsDirectory(paths[D_USER_IDX])) { 690 if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
693 paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; 691 paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
694 } else { 692 } else {
695 NGLOG_INFO(Common_Filesystem, "Using the local user directory"); 693 LOG_INFO(Common_Filesystem, "Using the local user directory");
696 } 694 }
697 695
698 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; 696 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
@@ -715,11 +713,13 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
715 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; 713 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
716 paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; 714 paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
717 paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; 715 paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
716 // TODO: Put the logs in a better location for each OS
717 paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP;
718 } 718 }
719 719
720 if (!newPath.empty()) { 720 if (!newPath.empty()) {
721 if (!FileUtil::IsDirectory(newPath)) { 721 if (!FileUtil::IsDirectory(newPath)) {
722 NGLOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath); 722 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath);
723 return paths[DirIDX]; 723 return paths[DirIDX];
724 } else { 724 } else {
725 paths[DirIDX] = newPath; 725 paths[DirIDX] = newPath;
@@ -801,8 +801,8 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
801 801
802IOFile::IOFile() {} 802IOFile::IOFile() {}
803 803
804IOFile::IOFile(const std::string& filename, const char openmode[]) { 804IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
805 Open(filename, openmode); 805 Open(filename, openmode, flags);
806} 806}
807 807
808IOFile::~IOFile() { 808IOFile::~IOFile() {
@@ -823,11 +823,16 @@ void IOFile::Swap(IOFile& other) noexcept {
823 std::swap(m_good, other.m_good); 823 std::swap(m_good, other.m_good);
824} 824}
825 825
826bool IOFile::Open(const std::string& filename, const char openmode[]) { 826bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
827 Close(); 827 Close();
828#ifdef _WIN32 828#ifdef _WIN32
829 _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), 829 if (flags != 0) {
830 Common::UTF8ToUTF16W(openmode).c_str()); 830 m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
831 Common::UTF8ToUTF16W(openmode).c_str(), flags);
832 } else {
833 _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
834 Common::UTF8ToUTF16W(openmode).c_str());
835 }
831#else 836#else
832 m_file = fopen(filename.c_str(), openmode); 837 m_file = fopen(filename.c_str(), openmode);
833#endif 838#endif
diff --git a/src/common/file_util.h b/src/common/file_util.h
index fc6b3ea46..5bc7fbf7c 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -156,7 +156,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
156class IOFile : public NonCopyable { 156class IOFile : public NonCopyable {
157public: 157public:
158 IOFile(); 158 IOFile();
159 IOFile(const std::string& filename, const char openmode[]); 159 // flags is used for windows specific file open mode flags, which
160 // allows yuzu to open the logs in shared write mode, so that the file
161 // isn't considered "locked" while yuzu is open and people can open the log file and view it
162 IOFile(const std::string& filename, const char openmode[], int flags = 0);
160 163
161 ~IOFile(); 164 ~IOFile();
162 165
@@ -165,7 +168,7 @@ public:
165 168
166 void Swap(IOFile& other) noexcept; 169 void Swap(IOFile& other) noexcept;
167 170
168 bool Open(const std::string& filename, const char openmode[]); 171 bool Open(const std::string& filename, const char openmode[], int flags = 0);
169 bool Close(); 172 bool Close();
170 173
171 template <typename T> 174 template <typename T>
@@ -220,6 +223,10 @@ public:
220 return WriteArray(&object, 1); 223 return WriteArray(&object, 1);
221 } 224 }
222 225
226 size_t WriteString(const std::string& str) {
227 return WriteArray(str.c_str(), str.length());
228 }
229
223 bool IsOpen() const { 230 bool IsOpen() const {
224 return nullptr != m_file; 231 return nullptr != m_file;
225 } 232 }
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index c26b20062..242914c6a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -2,16 +2,145 @@
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 <utility> 5#include <algorithm>
6#include <array>
7#include <chrono>
8#include <condition_variable>
9#include <memory>
10#include <thread>
11#ifdef _WIN32
12#include <share.h> // For _SH_DENYWR
13#else
14#define _SH_DENYWR 0
15#endif
6#include "common/assert.h" 16#include "common/assert.h"
17#include "common/common_funcs.h" // snprintf compatibility define
7#include "common/logging/backend.h" 18#include "common/logging/backend.h"
8#include "common/logging/filter.h"
9#include "common/logging/log.h" 19#include "common/logging/log.h"
10#include "common/logging/text_formatter.h" 20#include "common/logging/text_formatter.h"
11#include "common/string_util.h" 21#include "common/string_util.h"
22#include "common/threadsafe_queue.h"
12 23
13namespace Log { 24namespace Log {
14 25
26/**
27 * Static state as a singleton.
28 */
29class Impl {
30public:
31 static Impl& Instance() {
32 static Impl backend;
33 return backend;
34 }
35
36 Impl(Impl const&) = delete;
37 const Impl& operator=(Impl const&) = delete;
38
39 void PushEntry(Entry e) {
40 std::lock_guard<std::mutex> lock(message_mutex);
41 message_queue.Push(std::move(e));
42 message_cv.notify_one();
43 }
44
45 void AddBackend(std::unique_ptr<Backend> backend) {
46 std::lock_guard<std::mutex> lock(writing_mutex);
47 backends.push_back(std::move(backend));
48 }
49
50 void RemoveBackend(const std::string& backend_name) {
51 std::lock_guard<std::mutex> lock(writing_mutex);
52 auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
53 return !strcmp(i->GetName(), backend_name.c_str());
54 });
55 backends.erase(it, backends.end());
56 }
57
58 const Filter& GetGlobalFilter() const {
59 return filter;
60 }
61
62 void SetGlobalFilter(const Filter& f) {
63 filter = f;
64 }
65
66 Backend* GetBackend(const std::string& backend_name) {
67 auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
68 return !strcmp(i->GetName(), backend_name.c_str());
69 });
70 if (it == backends.end())
71 return nullptr;
72 return it->get();
73 }
74
75private:
76 Impl() {
77 backend_thread = std::thread([&] {
78 Entry entry;
79 auto write_logs = [&](Entry& e) {
80 std::lock_guard<std::mutex> lock(writing_mutex);
81 for (const auto& backend : backends) {
82 backend->Write(e);
83 }
84 };
85 while (true) {
86 std::unique_lock<std::mutex> lock(message_mutex);
87 message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
88 if (!running) {
89 break;
90 }
91 write_logs(entry);
92 }
93 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
94 // where a system is repeatedly spamming logs even on close.
95 constexpr int MAX_LOGS_TO_WRITE = 100;
96 int logs_written = 0;
97 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
98 write_logs(entry);
99 }
100 });
101 }
102
103 ~Impl() {
104 running = false;
105 message_cv.notify_one();
106 backend_thread.join();
107 }
108
109 std::atomic_bool running{true};
110 std::mutex message_mutex, writing_mutex;
111 std::condition_variable message_cv;
112 std::thread backend_thread;
113 std::vector<std::unique_ptr<Backend>> backends;
114 Common::MPSCQueue<Log::Entry> message_queue;
115 Filter filter;
116};
117
118void ConsoleBackend::Write(const Entry& entry) {
119 PrintMessage(entry);
120}
121
122void ColorConsoleBackend::Write(const Entry& entry) {
123 PrintColoredMessage(entry);
124}
125
126// _SH_DENYWR allows read only access to the file for other programs.
127// It is #defined to 0 on other platforms
128FileBackend::FileBackend(const std::string& filename)
129 : file(filename, "w", _SH_DENYWR), bytes_written(0) {}
130
131void FileBackend::Write(const Entry& entry) {
132 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
133 // know)
134 constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
135 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
136 return;
137 }
138 bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
139 if (entry.log_level >= Level::Error) {
140 file.Flush();
141 }
142}
143
15/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. 144/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
16#define ALL_LOG_CLASSES() \ 145#define ALL_LOG_CLASSES() \
17 CLS(Log) \ 146 CLS(Log) \
@@ -125,20 +254,32 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign
125 return entry; 254 return entry;
126} 255}
127 256
128static Filter* filter = nullptr; 257void SetGlobalFilter(const Filter& filter) {
258 Impl::Instance().SetGlobalFilter(filter);
259}
260
261void AddBackend(std::unique_ptr<Backend> backend) {
262 Impl::Instance().AddBackend(std::move(backend));
263}
129 264
130void SetFilter(Filter* new_filter) { 265void RemoveBackend(const std::string& backend_name) {
131 filter = new_filter; 266 Impl::Instance().RemoveBackend(backend_name);
267}
268
269Backend* GetBackend(const std::string& backend_name) {
270 return Impl::Instance().GetBackend(backend_name);
132} 271}
133 272
134void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, 273void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
135 unsigned int line_num, const char* function, const char* format, 274 unsigned int line_num, const char* function, const char* format,
136 const fmt::format_args& args) { 275 const fmt::format_args& args) {
137 if (filter && !filter->CheckMessage(log_class, log_level)) 276 auto filter = Impl::Instance().GetGlobalFilter();
277 if (!filter.CheckMessage(log_class, log_level))
138 return; 278 return;
279
139 Entry entry = 280 Entry entry =
140 CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args)); 281 CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
141 282
142 PrintColoredMessage(entry); 283 Impl::Instance().PushEntry(std::move(entry));
143} 284}
144} // namespace Log 285} // namespace Log \ No newline at end of file
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 7e81efb23..57cdf6b2d 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -1,13 +1,15 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
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
5#pragma once 4#pragma once
6 5
7#include <chrono> 6#include <chrono>
8#include <cstdarg> 7#include <cstdarg>
8#include <memory>
9#include <string> 9#include <string>
10#include <utility> 10#include <utility>
11#include "common/file_util.h"
12#include "common/logging/filter.h"
11#include "common/logging/log.h" 13#include "common/logging/log.h"
12 14
13namespace Log { 15namespace Log {
@@ -35,6 +37,80 @@ struct Entry {
35}; 37};
36 38
37/** 39/**
40 * Interface for logging backends. As loggers can be created and removed at runtime, this can be
41 * used by a frontend for adding a custom logging backend as needed
42 */
43class Backend {
44public:
45 virtual ~Backend() = default;
46 virtual void SetFilter(const Filter& new_filter) {
47 filter = new_filter;
48 }
49 virtual const char* GetName() const = 0;
50 virtual void Write(const Entry& entry) = 0;
51
52private:
53 Filter filter;
54};
55
56/**
57 * Backend that writes to stderr without any color commands
58 */
59class ConsoleBackend : public Backend {
60public:
61 static const char* Name() {
62 return "console";
63 }
64 const char* GetName() const override {
65 return Name();
66 }
67 void Write(const Entry& entry) override;
68};
69
70/**
71 * Backend that writes to stderr and with color
72 */
73class ColorConsoleBackend : public Backend {
74public:
75 static const char* Name() {
76 return "color_console";
77 }
78
79 const char* GetName() const override {
80 return Name();
81 }
82 void Write(const Entry& entry) override;
83};
84
85/**
86 * Backend that writes to a file passed into the constructor
87 */
88class FileBackend : public Backend {
89public:
90 explicit FileBackend(const std::string& filename);
91
92 static const char* Name() {
93 return "file";
94 }
95
96 const char* GetName() const override {
97 return Name();
98 }
99
100 void Write(const Entry& entry) override;
101
102private:
103 FileUtil::IOFile file;
104 size_t bytes_written;
105};
106
107void AddBackend(std::unique_ptr<Backend> backend);
108
109void RemoveBackend(const std::string& backend_name);
110
111Backend* GetBackend(const std::string& backend_name);
112
113/**
38 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods 114 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
39 * instead of underscores as in the enumeration. 115 * instead of underscores as in the enumeration.
40 */ 116 */
@@ -49,5 +125,10 @@ const char* GetLevelName(Level log_level);
49Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, 125Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
50 const char* function, std::string message); 126 const char* function, std::string message);
51 127
52void SetFilter(Filter* filter); 128/**
53} // namespace Log 129 * The global filter will prevent any messages from even being processed if they are filtered. Each
130 * backend can have a filter, but if the level is lower than the global filter, the backend will
131 * never get the message
132 */
133void SetGlobalFilter(const Filter& filter);
134} // namespace Log \ No newline at end of file
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 428723dce..4e783a577 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
65 const std::string::const_iterator end) { 65 const std::string::const_iterator end) {
66 auto level_separator = std::find(begin, end, ':'); 66 auto level_separator = std::find(begin, end, ':');
67 if (level_separator == end) { 67 if (level_separator == end) {
68 NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s", 68 LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
69 std::string(begin, end).c_str()); 69 std::string(begin, end));
70 return false; 70 return false;
71 } 71 }
72 72
73 const Level level = GetLevelByName(level_separator + 1, end); 73 const Level level = GetLevelByName(level_separator + 1, end);
74 if (level == Level::Count) { 74 if (level == Level::Count) {
75 NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str()); 75 LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
76 return false; 76 return false;
77 } 77 }
78 78
@@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
83 83
84 const Class log_class = GetClassByName(begin, level_separator); 84 const Class log_class = GetClassByName(begin, level_separator);
85 if (log_class == Class::Count) { 85 if (log_class == Class::Count) {
86 NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str()); 86 LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end));
87 return false; 87 return false;
88 } 88 }
89 89
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index c5015531c..e96c90e16 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -109,25 +109,25 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig
109} // namespace Log 109} // namespace Log
110 110
111#ifdef _DEBUG 111#ifdef _DEBUG
112#define NGLOG_TRACE(log_class, ...) \ 112#define LOG_TRACE(log_class, ...) \
113 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \ 113 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \
114 __func__, __VA_ARGS__) 114 __func__, __VA_ARGS__)
115#else 115#else
116#define NGLOG_TRACE(log_class, fmt, ...) (void(0)) 116#define LOG_TRACE(log_class, fmt, ...) (void(0))
117#endif 117#endif
118 118
119#define NGLOG_DEBUG(log_class, ...) \ 119#define LOG_DEBUG(log_class, ...) \
120 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \ 120 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \
121 __func__, __VA_ARGS__) 121 __func__, __VA_ARGS__)
122#define NGLOG_INFO(log_class, ...) \ 122#define LOG_INFO(log_class, ...) \
123 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \ 123 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \
124 __func__, __VA_ARGS__) 124 __func__, __VA_ARGS__)
125#define NGLOG_WARNING(log_class, ...) \ 125#define LOG_WARNING(log_class, ...) \
126 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \ 126 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \
127 __func__, __VA_ARGS__) 127 __func__, __VA_ARGS__)
128#define NGLOG_ERROR(log_class, ...) \ 128#define LOG_ERROR(log_class, ...) \
129 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \ 129 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \
130 __func__, __VA_ARGS__) 130 __func__, __VA_ARGS__)
131#define NGLOG_CRITICAL(log_class, ...) \ 131#define LOG_CRITICAL(log_class, ...) \
132 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \ 132 ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \
133 __func__, __VA_ARGS__) 133 __func__, __VA_ARGS__)
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
index 4d1ec8fb9..09462ccee 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -16,7 +16,7 @@
16#include <sys/mman.h> 16#include <sys/mman.h>
17#endif 17#endif
18 18
19#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) 19#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
20#include <unistd.h> 20#include <unistd.h>
21#define PAGE_MASK (getpagesize() - 1) 21#define PAGE_MASK (getpagesize() - 1)
22#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) 22#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
@@ -30,7 +30,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
30 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 30 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
31#else 31#else
32 static char* map_hint = nullptr; 32 static char* map_hint = nullptr;
33#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) 33#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
34 // This OS has no flag to enforce allocation below the 4 GB boundary, 34 // This OS has no flag to enforce allocation below the 4 GB boundary,
35 // but if we hint that we want a low address it is very likely we will 35 // but if we hint that we want a low address it is very likely we will
36 // get one. 36 // get one.
@@ -42,7 +42,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
42#endif 42#endif
43 void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, 43 void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
44 MAP_ANON | MAP_PRIVATE 44 MAP_ANON | MAP_PRIVATE
45#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT) 45#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
46 | (low ? MAP_32BIT : 0) 46 | (low ? MAP_32BIT : 0)
47#endif 47#endif
48 , 48 ,
@@ -55,9 +55,9 @@ void* AllocateExecutableMemory(size_t size, bool low) {
55 if (ptr == MAP_FAILED) { 55 if (ptr == MAP_FAILED) {
56 ptr = nullptr; 56 ptr = nullptr;
57#endif 57#endif
58 NGLOG_ERROR(Common_Memory, "Failed to allocate executable memory"); 58 LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
59 } 59 }
60#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) 60#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
61 else { 61 else {
62 if (low) { 62 if (low) {
63 map_hint += size; 63 map_hint += size;
@@ -68,7 +68,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
68 68
69#if EMU_ARCH_BITS == 64 69#if EMU_ARCH_BITS == 64
70 if ((u64)ptr >= 0x80000000 && low == true) 70 if ((u64)ptr >= 0x80000000 && low == true)
71 NGLOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); 71 LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
72#endif 72#endif
73 73
74 return ptr; 74 return ptr;
@@ -85,7 +85,7 @@ void* AllocateMemoryPages(size_t size) {
85#endif 85#endif
86 86
87 if (ptr == nullptr) 87 if (ptr == nullptr)
88 NGLOG_ERROR(Common_Memory, "Failed to allocate raw memory"); 88 LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
89 89
90 return ptr; 90 return ptr;
91} 91}
@@ -99,12 +99,12 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) {
99 ptr = memalign(alignment, size); 99 ptr = memalign(alignment, size);
100#else 100#else
101 if (posix_memalign(&ptr, alignment, size) != 0) 101 if (posix_memalign(&ptr, alignment, size) != 0)
102 NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); 102 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
103#endif 103#endif
104#endif 104#endif
105 105
106 if (ptr == nullptr) 106 if (ptr == nullptr)
107 NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); 107 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
108 108
109 return ptr; 109 return ptr;
110} 110}
@@ -113,7 +113,7 @@ void FreeMemoryPages(void* ptr, size_t size) {
113 if (ptr) { 113 if (ptr) {
114#ifdef _WIN32 114#ifdef _WIN32
115 if (!VirtualFree(ptr, 0, MEM_RELEASE)) 115 if (!VirtualFree(ptr, 0, MEM_RELEASE))
116 NGLOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg()); 116 LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
117#else 117#else
118 munmap(ptr, size); 118 munmap(ptr, size);
119#endif 119#endif
@@ -134,7 +134,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
134#ifdef _WIN32 134#ifdef _WIN32
135 DWORD oldValue; 135 DWORD oldValue;
136 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) 136 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
137 NGLOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg()); 137 LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
138#else 138#else
139 mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); 139 mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
140#endif 140#endif
@@ -145,7 +145,7 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
145 DWORD oldValue; 145 DWORD oldValue;
146 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, 146 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
147 &oldValue)) 147 &oldValue))
148 NGLOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg()); 148 LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
149#else 149#else
150 mprotect(ptr, size, 150 mprotect(ptr, size,
151 allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); 151 allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index ab0154133..e0df430ab 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -25,7 +25,7 @@ ParamPackage::ParamPackage(const std::string& serialized) {
25 std::vector<std::string> key_value; 25 std::vector<std::string> key_value;
26 Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value); 26 Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
27 if (key_value.size() != 2) { 27 if (key_value.size() != 2) {
28 NGLOG_ERROR(Common, "invalid key pair {}", pair); 28 LOG_ERROR(Common, "invalid key pair {}", pair);
29 continue; 29 continue;
30 } 30 }
31 31
@@ -64,7 +64,7 @@ std::string ParamPackage::Serialize() const {
64std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { 64std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
65 auto pair = data.find(key); 65 auto pair = data.find(key);
66 if (pair == data.end()) { 66 if (pair == data.end()) {
67 NGLOG_DEBUG(Common, "key '{}' not found", key); 67 LOG_DEBUG(Common, "key '{}' not found", key);
68 return default_value; 68 return default_value;
69 } 69 }
70 70
@@ -74,14 +74,14 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default
74int ParamPackage::Get(const std::string& key, int default_value) const { 74int ParamPackage::Get(const std::string& key, int default_value) const {
75 auto pair = data.find(key); 75 auto pair = data.find(key);
76 if (pair == data.end()) { 76 if (pair == data.end()) {
77 NGLOG_DEBUG(Common, "key '{}' not found", key); 77 LOG_DEBUG(Common, "key '{}' not found", key);
78 return default_value; 78 return default_value;
79 } 79 }
80 80
81 try { 81 try {
82 return std::stoi(pair->second); 82 return std::stoi(pair->second);
83 } catch (const std::logic_error&) { 83 } catch (const std::logic_error&) {
84 NGLOG_ERROR(Common, "failed to convert {} to int", pair->second); 84 LOG_ERROR(Common, "failed to convert {} to int", pair->second);
85 return default_value; 85 return default_value;
86 } 86 }
87} 87}
@@ -89,14 +89,14 @@ int ParamPackage::Get(const std::string& key, int default_value) const {
89float ParamPackage::Get(const std::string& key, float default_value) const { 89float ParamPackage::Get(const std::string& key, float default_value) const {
90 auto pair = data.find(key); 90 auto pair = data.find(key);
91 if (pair == data.end()) { 91 if (pair == data.end()) {
92 NGLOG_DEBUG(Common, "key {} not found", key); 92 LOG_DEBUG(Common, "key {} not found", key);
93 return default_value; 93 return default_value;
94 } 94 }
95 95
96 try { 96 try {
97 return std::stof(pair->second); 97 return std::stof(pair->second);
98 } catch (const std::logic_error&) { 98 } catch (const std::logic_error&) {
99 NGLOG_ERROR(Common, "failed to convert {} to float", pair->second); 99 LOG_ERROR(Common, "failed to convert {} to float", pair->second);
100 return default_value; 100 return default_value;
101 } 101 }
102} 102}
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 646400db0..ea9d8f77c 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -281,7 +281,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
281 281
282 iconv_t const conv_desc = iconv_open("UTF-8", fromcode); 282 iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
283 if ((iconv_t)(-1) == conv_desc) { 283 if ((iconv_t)(-1) == conv_desc) {
284 NGLOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); 284 LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno));
285 iconv_close(conv_desc); 285 iconv_close(conv_desc);
286 return {}; 286 return {};
287 } 287 }
@@ -310,7 +310,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
310 ++src_buffer; 310 ++src_buffer;
311 } 311 }
312 } else { 312 } else {
313 NGLOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); 313 LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno));
314 break; 314 break;
315 } 315 }
316 } 316 }
@@ -329,7 +329,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
329 329
330 iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); 330 iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
331 if ((iconv_t)(-1) == conv_desc) { 331 if ((iconv_t)(-1) == conv_desc) {
332 NGLOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); 332 LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno));
333 iconv_close(conv_desc); 333 iconv_close(conv_desc);
334 return {}; 334 return {};
335 } 335 }
@@ -358,7 +358,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
358 ++src_buffer; 358 ++src_buffer;
359 } 359 }
360 } else { 360 } else {
361 NGLOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); 361 LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno));
362 break; 362 break;
363 } 363 }
364 } 364 }
diff --git a/src/common/swap.h b/src/common/swap.h
index 4a4012d1a..f025f7450 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) {
69inline u64 swap64(u64 _data) { 69inline u64 swap64(u64 _data) {
70 return _byteswap_uint64(_data); 70 return _byteswap_uint64(_data);
71} 71}
72#elif _M_ARM 72#elif ARCHITECTURE_ARM
73inline u16 swap16(u16 _data) { 73inline u16 swap16(u16 _data) {
74 u32 data = _data; 74 u32 data = _data;
75 __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); 75 __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ba5b02174..3dff068df 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -40,6 +40,8 @@ add_library(core STATIC
40 hle/config_mem.h 40 hle/config_mem.h
41 hle/ipc.h 41 hle/ipc.h
42 hle/ipc_helpers.h 42 hle/ipc_helpers.h
43 hle/kernel/address_arbiter.cpp
44 hle/kernel/address_arbiter.h
43 hle/kernel/client_port.cpp 45 hle/kernel/client_port.cpp
44 hle/kernel/client_port.h 46 hle/kernel/client_port.h
45 hle/kernel/client_session.cpp 47 hle/kernel/client_session.cpp
@@ -124,6 +126,8 @@ add_library(core STATIC
124 hle/service/audio/audren_u.h 126 hle/service/audio/audren_u.h
125 hle/service/audio/codecctl.cpp 127 hle/service/audio/codecctl.cpp
126 hle/service/audio/codecctl.h 128 hle/service/audio/codecctl.h
129 hle/service/audio/hwopus.cpp
130 hle/service/audio/hwopus.h
127 hle/service/bcat/module.cpp 131 hle/service/bcat/module.cpp
128 hle/service/bcat/module.h 132 hle/service/bcat/module.h
129 hle/service/bcat/bcat.cpp 133 hle/service/bcat/bcat.cpp
@@ -257,6 +261,8 @@ add_library(core STATIC
257 loader/linker.h 261 loader/linker.h
258 loader/loader.cpp 262 loader/loader.cpp
259 loader/loader.h 263 loader/loader.h
264 loader/nca.cpp
265 loader/nca.h
260 loader/nro.cpp 266 loader/nro.cpp
261 loader/nro.h 267 loader/nro.h
262 loader/nso.cpp 268 loader/nso.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index b5db47667..42605374b 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -55,8 +55,8 @@ public:
55 } 55 }
56 56
57 void InterpreterFallback(u64 pc, size_t num_instructions) override { 57 void InterpreterFallback(u64 pc, size_t num_instructions) override {
58 NGLOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, 58 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
59 num_instructions, MemoryReadCode(pc)); 59 num_instructions, MemoryReadCode(pc));
60 60
61 ARM_Interface::ThreadContext ctx; 61 ARM_Interface::ThreadContext ctx;
62 parent.SaveContext(ctx); 62 parent.SaveContext(ctx);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 84ab876cc..8335d502e 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -87,15 +87,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
87 app_loader = Loader::GetLoader(filepath); 87 app_loader = Loader::GetLoader(filepath);
88 88
89 if (!app_loader) { 89 if (!app_loader) {
90 NGLOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 90 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
91 return ResultStatus::ErrorGetLoader; 91 return ResultStatus::ErrorGetLoader;
92 } 92 }
93 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = 93 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
94 app_loader->LoadKernelSystemMode(); 94 app_loader->LoadKernelSystemMode();
95 95
96 if (system_mode.second != Loader::ResultStatus::Success) { 96 if (system_mode.second != Loader::ResultStatus::Success) {
97 NGLOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", 97 LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
98 static_cast<int>(system_mode.second)); 98 static_cast<int>(system_mode.second));
99 99
100 switch (system_mode.second) { 100 switch (system_mode.second) {
101 case Loader::ResultStatus::ErrorEncrypted: 101 case Loader::ResultStatus::ErrorEncrypted:
@@ -111,15 +111,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
111 111
112 ResultStatus init_result{Init(emu_window, system_mode.first.get())}; 112 ResultStatus init_result{Init(emu_window, system_mode.first.get())};
113 if (init_result != ResultStatus::Success) { 113 if (init_result != ResultStatus::Success) {
114 NGLOG_CRITICAL(Core, "Failed to initialize system (Error {})!", 114 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
115 static_cast<int>(init_result)); 115 static_cast<int>(init_result));
116 System::Shutdown(); 116 System::Shutdown();
117 return init_result; 117 return init_result;
118 } 118 }
119 119
120 const Loader::ResultStatus load_result{app_loader->Load(current_process)}; 120 const Loader::ResultStatus load_result{app_loader->Load(current_process)};
121 if (Loader::ResultStatus::Success != load_result) { 121 if (Loader::ResultStatus::Success != load_result) {
122 NGLOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 122 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
123 System::Shutdown(); 123 System::Shutdown();
124 124
125 switch (load_result) { 125 switch (load_result) {
@@ -161,7 +161,7 @@ Cpu& System::CpuCore(size_t core_index) {
161} 161}
162 162
163System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 163System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
164 NGLOG_DEBUG(HW_Memory, "initialized OK"); 164 LOG_DEBUG(HW_Memory, "initialized OK");
165 165
166 CoreTiming::Init(); 166 CoreTiming::Init();
167 167
@@ -196,7 +196,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
196 } 196 }
197 } 197 }
198 198
199 NGLOG_DEBUG(Core, "Initialized OK"); 199 LOG_DEBUG(Core, "Initialized OK");
200 200
201 // Reset counters and set time origin to current frame 201 // Reset counters and set time origin to current frame
202 GetAndResetPerfStats(); 202 GetAndResetPerfStats();
@@ -245,7 +245,7 @@ void System::Shutdown() {
245 // Close app loader 245 // Close app loader
246 app_loader.reset(); 246 app_loader.reset();
247 247
248 NGLOG_DEBUG(Core, "Shutdown OK"); 248 LOG_DEBUG(Core, "Shutdown OK");
249} 249}
250 250
251Service::SM::ServiceManager& System::ServiceManager() { 251Service::SM::ServiceManager& System::ServiceManager() {
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 099f2bb1a..f22d6a9d0 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -56,7 +56,7 @@ Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
56 arm_interface = std::make_shared<ARM_Dynarmic>(); 56 arm_interface = std::make_shared<ARM_Dynarmic>();
57#else 57#else
58 cpu_core = std::make_shared<ARM_Unicorn>(); 58 cpu_core = std::make_shared<ARM_Unicorn>();
59 NGLOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 59 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
60#endif 60#endif
61 } else { 61 } else {
62 arm_interface = std::make_shared<ARM_Unicorn>(); 62 arm_interface = std::make_shared<ARM_Unicorn>();
@@ -75,7 +75,7 @@ void Cpu::RunLoop(bool tight_loop) {
75 // If we don't have a currently active thread then don't execute instructions, 75 // If we don't have a currently active thread then don't execute instructions,
76 // instead advance to the next event and try to yield to the next thread 76 // instead advance to the next event and try to yield to the next thread
77 if (Kernel::GetCurrentThread() == nullptr) { 77 if (Kernel::GetCurrentThread() == nullptr) {
78 NGLOG_TRACE(Core, "Core-{} idling", core_index); 78 LOG_TRACE(Core, "Core-{} idling", core_index);
79 79
80 if (IsMainCore()) { 80 if (IsMainCore()) {
81 CoreTiming::Idle(); 81 CoreTiming::Idle();
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index dc1d8668f..50d1e3fc9 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -74,11 +74,11 @@ static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {}
74 74
75s64 usToCycles(s64 us) { 75s64 usToCycles(s64 us) {
76 if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { 76 if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
77 NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); 77 LOG_ERROR(Core_Timing, "Integer overflow, use max value");
78 return std::numeric_limits<s64>::max(); 78 return std::numeric_limits<s64>::max();
79 } 79 }
80 if (us > MAX_VALUE_TO_MULTIPLY) { 80 if (us > MAX_VALUE_TO_MULTIPLY) {
81 NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); 81 LOG_DEBUG(Core_Timing, "Time very big, do rounding");
82 return BASE_CLOCK_RATE * (us / 1000000); 82 return BASE_CLOCK_RATE * (us / 1000000);
83 } 83 }
84 return (BASE_CLOCK_RATE * us) / 1000000; 84 return (BASE_CLOCK_RATE * us) / 1000000;
@@ -86,11 +86,11 @@ s64 usToCycles(s64 us) {
86 86
87s64 usToCycles(u64 us) { 87s64 usToCycles(u64 us) {
88 if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { 88 if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
89 NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); 89 LOG_ERROR(Core_Timing, "Integer overflow, use max value");
90 return std::numeric_limits<s64>::max(); 90 return std::numeric_limits<s64>::max();
91 } 91 }
92 if (us > MAX_VALUE_TO_MULTIPLY) { 92 if (us > MAX_VALUE_TO_MULTIPLY) {
93 NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); 93 LOG_DEBUG(Core_Timing, "Time very big, do rounding");
94 return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000); 94 return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000);
95 } 95 }
96 return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000; 96 return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000;
@@ -98,11 +98,11 @@ s64 usToCycles(u64 us) {
98 98
99s64 nsToCycles(s64 ns) { 99s64 nsToCycles(s64 ns) {
100 if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { 100 if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
101 NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); 101 LOG_ERROR(Core_Timing, "Integer overflow, use max value");
102 return std::numeric_limits<s64>::max(); 102 return std::numeric_limits<s64>::max();
103 } 103 }
104 if (ns > MAX_VALUE_TO_MULTIPLY) { 104 if (ns > MAX_VALUE_TO_MULTIPLY) {
105 NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); 105 LOG_DEBUG(Core_Timing, "Time very big, do rounding");
106 return BASE_CLOCK_RATE * (ns / 1000000000); 106 return BASE_CLOCK_RATE * (ns / 1000000000);
107 } 107 }
108 return (BASE_CLOCK_RATE * ns) / 1000000000; 108 return (BASE_CLOCK_RATE * ns) / 1000000000;
@@ -110,11 +110,11 @@ s64 nsToCycles(s64 ns) {
110 110
111s64 nsToCycles(u64 ns) { 111s64 nsToCycles(u64 ns) {
112 if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { 112 if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
113 NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); 113 LOG_ERROR(Core_Timing, "Integer overflow, use max value");
114 return std::numeric_limits<s64>::max(); 114 return std::numeric_limits<s64>::max();
115 } 115 }
116 if (ns > MAX_VALUE_TO_MULTIPLY) { 116 if (ns > MAX_VALUE_TO_MULTIPLY) {
117 NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); 117 LOG_DEBUG(Core_Timing, "Time very big, do rounding");
118 return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000); 118 return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000);
119 } 119 }
120 return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000; 120 return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp
index 8aa0e0aa4..8c6f15bb5 100644
--- a/src/core/file_sys/disk_filesystem.cpp
+++ b/src/core/file_sys/disk_filesystem.cpp
@@ -80,19 +80,19 @@ ResultCode Disk_FileSystem::RenameFile(const std::string& src_path,
80} 80}
81 81
82ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { 82ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const {
83 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 83 LOG_WARNING(Service_FS, "(STUBBED) called");
84 // TODO(wwylele): Use correct error code 84 // TODO(wwylele): Use correct error code
85 return ResultCode(-1); 85 return ResultCode(-1);
86} 86}
87 87
88ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { 88ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
89 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 89 LOG_WARNING(Service_FS, "(STUBBED) called");
90 // TODO(wwylele): Use correct error code 90 // TODO(wwylele): Use correct error code
91 return ResultCode(-1); 91 return ResultCode(-1);
92} 92}
93 93
94ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { 94ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const {
95 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 95 LOG_WARNING(Service_FS, "(STUBBED) called");
96 96
97 std::string full_path = base_directory + path; 97 std::string full_path = base_directory + path;
98 if (size == 0) { 98 if (size == 0) {
@@ -107,7 +107,7 @@ ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const
107 return RESULT_SUCCESS; 107 return RESULT_SUCCESS;
108 } 108 }
109 109
110 NGLOG_ERROR(Service_FS, "Too large file"); 110 LOG_ERROR(Service_FS, "Too large file");
111 // TODO(Subv): Find out the correct error code 111 // TODO(Subv): Find out the correct error code
112 return ResultCode(-1); 112 return ResultCode(-1);
113} 113}
@@ -120,13 +120,13 @@ ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const {
120 return RESULT_SUCCESS; 120 return RESULT_SUCCESS;
121 } 121 }
122 122
123 NGLOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); 123 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path);
124 // TODO(wwylele): Use correct error code 124 // TODO(wwylele): Use correct error code
125 return ResultCode(-1); 125 return ResultCode(-1);
126} 126}
127 127
128ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { 128ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
129 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 129 LOG_WARNING(Service_FS, "(STUBBED) called");
130 // TODO(wwylele): Use correct error code 130 // TODO(wwylele): Use correct error code
131 return ResultCode(-1); 131 return ResultCode(-1);
132} 132}
@@ -146,7 +146,7 @@ ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
146} 146}
147 147
148u64 Disk_FileSystem::GetFreeSpaceSize() const { 148u64 Disk_FileSystem::GetFreeSpaceSize() const {
149 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 149 LOG_WARNING(Service_FS, "(STUBBED) called");
150 return 0; 150 return 0;
151} 151}
152 152
@@ -163,14 +163,14 @@ ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& p
163} 163}
164 164
165ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { 165ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
166 NGLOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); 166 LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length);
167 file->Seek(offset, SEEK_SET); 167 file->Seek(offset, SEEK_SET);
168 return MakeResult<size_t>(file->ReadBytes(buffer, length)); 168 return MakeResult<size_t>(file->ReadBytes(buffer, length));
169} 169}
170 170
171ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, 171ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush,
172 const u8* buffer) const { 172 const u8* buffer) const {
173 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 173 LOG_WARNING(Service_FS, "(STUBBED) called");
174 file->Seek(offset, SEEK_SET); 174 file->Seek(offset, SEEK_SET);
175 size_t written = file->WriteBytes(buffer, length); 175 size_t written = file->WriteBytes(buffer, length);
176 if (flush) { 176 if (flush) {
@@ -204,7 +204,7 @@ u64 Disk_Directory::Read(const u64 count, Entry* entries) {
204 const std::string& filename = file.virtualName; 204 const std::string& filename = file.virtualName;
205 Entry& entry = entries[entries_read]; 205 Entry& entry = entries[entries_read];
206 206
207 NGLOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); 207 LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory);
208 208
209 // TODO(Link Mauve): use a proper conversion to UTF-16. 209 // TODO(Link Mauve): use a proper conversion to UTF-16.
210 for (size_t j = 0; j < FILENAME_LENGTH; ++j) { 210 for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
diff --git a/src/core/file_sys/filesystem.cpp b/src/core/file_sys/filesystem.cpp
index 87083878b..82fdb3c46 100644
--- a/src/core/file_sys/filesystem.cpp
+++ b/src/core/file_sys/filesystem.cpp
@@ -71,7 +71,7 @@ std::string Path::AsString() const {
71 case Binary: 71 case Binary:
72 default: 72 default:
73 // TODO(yuriks): Add assert 73 // TODO(yuriks): Add assert
74 NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); 74 LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
75 return {}; 75 return {};
76 } 76 }
77} 77}
@@ -87,7 +87,7 @@ std::u16string Path::AsU16Str() const {
87 case Invalid: 87 case Invalid:
88 case Binary: 88 case Binary:
89 // TODO(yuriks): Add assert 89 // TODO(yuriks): Add assert
90 NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); 90 LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
91 return {}; 91 return {};
92 } 92 }
93 93
@@ -115,7 +115,7 @@ std::vector<u8> Path::AsBinary() const {
115 case Invalid: 115 case Invalid:
116 default: 116 default:
117 // TODO(yuriks): Add assert 117 // TODO(yuriks): Add assert
118 NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); 118 LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!");
119 return {}; 119 return {};
120 } 120 }
121} 121}
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 808254ecc..46d438aca 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -19,13 +19,20 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, siz
19 if (file.GetSize() < sizeof(Header)) 19 if (file.GetSize() < sizeof(Header))
20 return Loader::ResultStatus::Error; 20 return Loader::ResultStatus::Error;
21 21
22 file.Seek(offset, SEEK_SET);
22 // For cartridges, HFSs can get very large, so we need to calculate the size up to 23 // For cartridges, HFSs can get very large, so we need to calculate the size up to
23 // the actual content itself instead of just blindly reading in the entire file. 24 // the actual content itself instead of just blindly reading in the entire file.
24 Header pfs_header; 25 Header pfs_header;
25 if (!file.ReadBytes(&pfs_header, sizeof(Header))) 26 if (!file.ReadBytes(&pfs_header, sizeof(Header)))
26 return Loader::ResultStatus::Error; 27 return Loader::ResultStatus::Error;
27 28
28 bool is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); 29 if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
30 pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
31 return Loader::ResultStatus::ErrorInvalidFormat;
32 }
33
34 bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
35
29 size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); 36 size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
30 size_t metadata_size = 37 size_t metadata_size =
31 sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; 38 sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size;
@@ -39,7 +46,7 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, siz
39 46
40 Loader::ResultStatus result = Load(file_data); 47 Loader::ResultStatus result = Load(file_data);
41 if (result != Loader::ResultStatus::Success) 48 if (result != Loader::ResultStatus::Success)
42 NGLOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path); 49 LOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path);
43 50
44 return result; 51 return result;
45} 52}
@@ -50,7 +57,12 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data,
50 return Loader::ResultStatus::Error; 57 return Loader::ResultStatus::Error;
51 58
52 memcpy(&pfs_header, &file_data[offset], sizeof(Header)); 59 memcpy(&pfs_header, &file_data[offset], sizeof(Header));
53 is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); 60 if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
61 pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
62 return Loader::ResultStatus::ErrorInvalidFormat;
63 }
64
65 is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
54 66
55 size_t entries_offset = offset + sizeof(Header); 67 size_t entries_offset = offset + sizeof(Header);
56 size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); 68 size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
@@ -73,21 +85,21 @@ u32 PartitionFilesystem::GetNumEntries() const {
73 return pfs_header.num_entries; 85 return pfs_header.num_entries;
74} 86}
75 87
76u64 PartitionFilesystem::GetEntryOffset(int index) const { 88u64 PartitionFilesystem::GetEntryOffset(u32 index) const {
77 if (index > GetNumEntries()) 89 if (index > GetNumEntries())
78 return 0; 90 return 0;
79 91
80 return content_offset + pfs_entries[index].fs_entry.offset; 92 return content_offset + pfs_entries[index].fs_entry.offset;
81} 93}
82 94
83u64 PartitionFilesystem::GetEntrySize(int index) const { 95u64 PartitionFilesystem::GetEntrySize(u32 index) const {
84 if (index > GetNumEntries()) 96 if (index > GetNumEntries())
85 return 0; 97 return 0;
86 98
87 return pfs_entries[index].fs_entry.size; 99 return pfs_entries[index].fs_entry.size;
88} 100}
89 101
90std::string PartitionFilesystem::GetEntryName(int index) const { 102std::string PartitionFilesystem::GetEntryName(u32 index) const {
91 if (index > GetNumEntries()) 103 if (index > GetNumEntries())
92 return ""; 104 return "";
93 105
@@ -113,12 +125,12 @@ u64 PartitionFilesystem::GetFileSize(const std::string& name) const {
113} 125}
114 126
115void PartitionFilesystem::Print() const { 127void PartitionFilesystem::Print() const {
116 NGLOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic.data()); 128 LOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic);
117 NGLOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); 129 LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries);
118 for (u32 i = 0; i < pfs_header.num_entries; i++) { 130 for (u32 i = 0; i < pfs_header.num_entries; i++) {
119 NGLOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, 131 LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i,
120 pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, 132 pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size,
121 GetFileOffset(pfs_entries[i].name)); 133 GetFileOffset(pfs_entries[i].name));
122 } 134 }
123} 135}
124} // namespace FileSys 136} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index 573c90057..9c5810cf1 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -27,9 +27,9 @@ public:
27 Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); 27 Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0);
28 28
29 u32 GetNumEntries() const; 29 u32 GetNumEntries() const;
30 u64 GetEntryOffset(int index) const; 30 u64 GetEntryOffset(u32 index) const;
31 u64 GetEntrySize(int index) const; 31 u64 GetEntrySize(u32 index) const;
32 std::string GetEntryName(int index) const; 32 std::string GetEntryName(u32 index) const;
33 u64 GetFileOffset(const std::string& name) const; 33 u64 GetFileOffset(const std::string& name) const;
34 u64 GetFileSize(const std::string& name) const; 34 u64 GetFileSize(const std::string& name) const;
35 35
@@ -37,7 +37,7 @@ public:
37 37
38private: 38private:
39 struct Header { 39 struct Header {
40 std::array<char, 4> magic; 40 u32_le magic;
41 u32_le num_entries; 41 u32_le num_entries;
42 u32_le strtab_size; 42 u32_le strtab_size;
43 INSERT_PADDING_BYTES(0x4); 43 INSERT_PADDING_BYTES(0x4);
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 25a822891..226811115 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -21,7 +21,7 @@ Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
21 21
22 Loader::ResultStatus result = Load(file_data); 22 Loader::ResultStatus result = Load(file_data);
23 if (result != Loader::ResultStatus::Success) 23 if (result != Loader::ResultStatus::Success)
24 NGLOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path); 24 LOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path);
25 25
26 return result; 26 return result;
27} 27}
@@ -76,14 +76,14 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
76} 76}
77 77
78void ProgramMetadata::Print() const { 78void ProgramMetadata::Print() const {
79 NGLOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data()); 79 LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
80 NGLOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority); 80 LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
81 NGLOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu); 81 LOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu);
82 NGLOG_DEBUG(Service_FS, "Main thread stack size: 0x{:X} bytes", npdm_header.main_stack_size); 82 LOG_DEBUG(Service_FS, "Main thread stack size: 0x{:X} bytes", npdm_header.main_stack_size);
83 NGLOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category); 83 LOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category);
84 NGLOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags); 84 LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags);
85 NGLOG_DEBUG(Service_FS, " > 64-bit instructions: {}", 85 LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
86 npdm_header.has_64_bit_instructions ? "YES" : "NO"); 86 npdm_header.has_64_bit_instructions ? "YES" : "NO");
87 87
88 auto address_space = "Unknown"; 88 auto address_space = "Unknown";
89 switch (npdm_header.address_space_type) { 89 switch (npdm_header.address_space_type) {
@@ -95,19 +95,19 @@ void ProgramMetadata::Print() const {
95 break; 95 break;
96 } 96 }
97 97
98 NGLOG_DEBUG(Service_FS, " > Address space: {}\n", address_space); 98 LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space);
99 99
100 // Begin ACID printing (potential perms, signed) 100 // Begin ACID printing (potential perms, signed)
101 NGLOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data()); 101 LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
102 NGLOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags); 102 LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
103 NGLOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); 103 LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
104 NGLOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); 104 LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
105 NGLOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); 105 LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
106 NGLOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); 106 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
107 107
108 // Begin ACI0 printing (actual perms, unsigned) 108 // Begin ACI0 printing (actual perms, unsigned)
109 NGLOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data()); 109 LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data());
110 NGLOG_DEBUG(Service_FS, "Title ID: 0x{:016X}", aci_header.title_id); 110 LOG_DEBUG(Service_FS, "Title ID: 0x{:016X}", aci_header.title_id);
111 NGLOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", aci_file_access.permissions); 111 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", aci_file_access.permissions);
112} 112}
113} // namespace FileSys 113} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index dc7591aca..84ae0d99b 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -14,7 +14,7 @@ namespace FileSys {
14RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { 14RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
15 // Load the RomFS from the app 15 // Load the RomFS from the app
16 if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { 16 if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
17 NGLOG_ERROR(Service_FS, "Unable to read RomFS!"); 17 LOG_ERROR(Service_FS, "Unable to read RomFS!");
18 } 18 }
19} 19}
20 20
@@ -24,13 +24,13 @@ ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& pa
24} 24}
25 25
26ResultCode RomFS_Factory::Format(const Path& path) { 26ResultCode RomFS_Factory::Format(const Path& path) {
27 NGLOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); 27 LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
28 // TODO(bunnei): Find the right error code for this 28 // TODO(bunnei): Find the right error code for this
29 return ResultCode(-1); 29 return ResultCode(-1);
30} 30}
31 31
32ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { 32ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const {
33 NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); 33 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
34 // TODO(bunnei): Find the right error code for this 34 // TODO(bunnei): Find the right error code for this
35 return ResultCode(-1); 35 return ResultCode(-1);
36} 36}
diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp
index 8e2bce687..83162622b 100644
--- a/src/core/file_sys/romfs_filesystem.cpp
+++ b/src/core/file_sys/romfs_filesystem.cpp
@@ -21,72 +21,70 @@ ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std:
21} 21}
22 22
23ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const { 23ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const {
24 NGLOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName()); 24 LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName());
25 // TODO(bunnei): Use correct error code 25 // TODO(bunnei): Use correct error code
26 return ResultCode(-1); 26 return ResultCode(-1);
27} 27}
28 28
29ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path, 29ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path,
30 const std::string& dest_path) const { 30 const std::string& dest_path) const {
31 NGLOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", 31 LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
32 GetName());
33 // TODO(wwylele): Use correct error code 32 // TODO(wwylele): Use correct error code
34 return ResultCode(-1); 33 return ResultCode(-1);
35} 34}
36 35
37ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { 36ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const {
38 NGLOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", 37 LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
39 GetName()); 38 GetName());
40 // TODO(wwylele): Use correct error code 39 // TODO(wwylele): Use correct error code
41 return ResultCode(-1); 40 return ResultCode(-1);
42} 41}
43 42
44ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { 43ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
45 NGLOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", 44 LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
46 GetName()); 45 GetName());
47 // TODO(wwylele): Use correct error code 46 // TODO(wwylele): Use correct error code
48 return ResultCode(-1); 47 return ResultCode(-1);
49} 48}
50 49
51ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const { 50ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const {
52 NGLOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName()); 51 LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName());
53 // TODO(bunnei): Use correct error code 52 // TODO(bunnei): Use correct error code
54 return ResultCode(-1); 53 return ResultCode(-1);
55} 54}
56 55
57ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const { 56ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const {
58 NGLOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).", 57 LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).",
59 GetName()); 58 GetName());
60 // TODO(wwylele): Use correct error code 59 // TODO(wwylele): Use correct error code
61 return ResultCode(-1); 60 return ResultCode(-1);
62} 61}
63 62
64ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { 63ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
65 NGLOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", 64 LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
66 GetName());
67 // TODO(wwylele): Use correct error code 65 // TODO(wwylele): Use correct error code
68 return ResultCode(-1); 66 return ResultCode(-1);
69} 67}
70 68
71ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( 69ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
72 const std::string& path) const { 70 const std::string& path) const {
73 NGLOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive"); 71 LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive");
74 return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); 72 return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
75} 73}
76 74
77u64 RomFS_FileSystem::GetFreeSpaceSize() const { 75u64 RomFS_FileSystem::GetFreeSpaceSize() const {
78 NGLOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); 76 LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive");
79 return 0; 77 return 0;
80} 78}
81 79
82ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const { 80ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const {
83 NGLOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path); 81 LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path);
84 // TODO(wwylele): Use correct error code 82 // TODO(wwylele): Use correct error code
85 return ResultCode(-1); 83 return ResultCode(-1);
86} 84}
87 85
88ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { 86ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
89 NGLOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); 87 LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length);
90 romfs_file->Seek(data_offset + offset, SEEK_SET); 88 romfs_file->Seek(data_offset + offset, SEEK_SET);
91 size_t read_length = (size_t)std::min((u64)length, data_size - offset); 89 size_t read_length = (size_t)std::min((u64)length, data_size - offset);
92 90
@@ -95,7 +93,7 @@ ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8*
95 93
96ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, 94ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush,
97 const u8* buffer) const { 95 const u8* buffer) const {
98 NGLOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); 96 LOG_ERROR(Service_FS, "Attempted to write to ROMFS file");
99 // TODO(Subv): Find error code 97 // TODO(Subv): Find error code
100 return MakeResult<size_t>(0); 98 return MakeResult<size_t>(0);
101} 99}
@@ -105,7 +103,7 @@ u64 RomFS_Storage::GetSize() const {
105} 103}
106 104
107bool RomFS_Storage::SetSize(const u64 size) const { 105bool RomFS_Storage::SetSize(const u64 size) const {
108 NGLOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); 106 LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file");
109 return false; 107 return false;
110} 108}
111 109
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index c1be8fee4..f3aa213af 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -17,6 +17,15 @@ SaveData_Factory::SaveData_Factory(std::string nand_directory)
17 17
18ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) { 18ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
19 std::string save_directory = GetFullPath(); 19 std::string save_directory = GetFullPath();
20
21 if (!FileUtil::Exists(save_directory)) {
22 // TODO(bunnei): This is a work-around to always create a save data directory if it does not
23 // already exist. This is a hack, as we do not understand yet how this works on hardware.
24 // Without a save data directory, many games will assert on boot. This should not have any
25 // bad side-effects.
26 FileUtil::CreateFullPath(save_directory);
27 }
28
20 // Return an error if the save data doesn't actually exist. 29 // Return an error if the save data doesn't actually exist.
21 if (!FileUtil::IsDirectory(save_directory)) { 30 if (!FileUtil::IsDirectory(save_directory)) {
22 // TODO(Subv): Find out correct error code. 31 // TODO(Subv): Find out correct error code.
@@ -28,7 +37,7 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path&
28} 37}
29 38
30ResultCode SaveData_Factory::Format(const Path& path) { 39ResultCode SaveData_Factory::Format(const Path& path) {
31 NGLOG_WARNING(Service_FS, "Format archive {}", GetName()); 40 LOG_WARNING(Service_FS, "Format archive {}", GetName());
32 // Create the save data directory. 41 // Create the save data directory.
33 if (!FileUtil::CreateFullPath(GetFullPath())) { 42 if (!FileUtil::CreateFullPath(GetFullPath())) {
34 // TODO(Subv): Find the correct error code. 43 // TODO(Subv): Find the correct error code.
@@ -39,7 +48,7 @@ ResultCode SaveData_Factory::Format(const Path& path) {
39} 48}
40 49
41ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const { 50ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const {
42 NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); 51 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
43 // TODO(bunnei): Find the right error code for this 52 // TODO(bunnei): Find the right error code for this
44 return ResultCode(-1); 53 return ResultCode(-1);
45} 54}
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index 59ac3e0be..2e5ffb764 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -25,13 +25,13 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& pat
25} 25}
26 26
27ResultCode SDMC_Factory::Format(const Path& path) { 27ResultCode SDMC_Factory::Format(const Path& path) {
28 NGLOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); 28 LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
29 // TODO(Subv): Find the right error code for this 29 // TODO(Subv): Find the right error code for this
30 return ResultCode(-1); 30 return ResultCode(-1);
31} 31}
32 32
33ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const { 33ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const {
34 NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); 34 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
35 // TODO(bunnei): Find the right error code for this 35 // TODO(bunnei): Find the right error code for this
36 return ResultCode(-1); 36 return ResultCode(-1);
37} 37}
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 79e52488f..39bdf4e21 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -59,7 +59,7 @@ template <typename InputDeviceType>
59void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) { 59void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
60 auto pair = std::make_pair(name, std::move(factory)); 60 auto pair = std::make_pair(name, std::move(factory));
61 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) { 61 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
62 NGLOG_ERROR(Input, "Factory '{}' already registered", name); 62 LOG_ERROR(Input, "Factory '{}' already registered", name);
63 } 63 }
64} 64}
65 65
@@ -71,7 +71,7 @@ void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDevic
71template <typename InputDeviceType> 71template <typename InputDeviceType>
72void UnregisterFactory(const std::string& name) { 72void UnregisterFactory(const std::string& name) {
73 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) { 73 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
74 NGLOG_ERROR(Input, "Factory '{}' not registered", name); 74 LOG_ERROR(Input, "Factory '{}' not registered", name);
75 } 75 }
76} 76}
77 77
@@ -88,7 +88,7 @@ std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
88 const auto pair = factory_list.find(engine); 88 const auto pair = factory_list.find(engine);
89 if (pair == factory_list.end()) { 89 if (pair == factory_list.end()) {
90 if (engine != "null") { 90 if (engine != "null") {
91 NGLOG_ERROR(Input, "Unknown engine name: {}", engine); 91 LOG_ERROR(Input, "Unknown engine name: {}", engine);
92 } 92 }
93 return std::make_unique<InputDeviceType>(); 93 return std::make_unique<InputDeviceType>();
94 } 94 }
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 2603192fe..938852a1a 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -232,7 +232,7 @@ static u8 HexCharToValue(u8 hex) {
232 return hex - 'A' + 0xA; 232 return hex - 'A' + 0xA;
233 } 233 }
234 234
235 NGLOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", hex, hex); 235 LOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", hex, hex);
236 return 0; 236 return 0;
237} 237}
238 238
@@ -372,7 +372,7 @@ static u8 ReadByte() {
372 u8 c; 372 u8 c;
373 size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); 373 size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
374 if (received_size != 1) { 374 if (received_size != 1) {
375 NGLOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); 375 LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size);
376 Shutdown(); 376 Shutdown();
377 } 377 }
378 378
@@ -413,8 +413,8 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
413 413
414 auto bp = p.find(static_cast<u64>(addr)); 414 auto bp = p.find(static_cast<u64>(addr));
415 if (bp != p.end()) { 415 if (bp != p.end()) {
416 NGLOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", 416 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
417 bp->second.len, bp->second.addr, static_cast<int>(type)); 417 bp->second.len, bp->second.addr, static_cast<int>(type));
418 p.erase(static_cast<u64>(addr)); 418 p.erase(static_cast<u64>(addr));
419 } 419 }
420} 420}
@@ -459,10 +459,10 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
459 } 459 }
460 460
461 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { 461 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
462 NGLOG_DEBUG(Debug_GDBStub, 462 LOG_DEBUG(Debug_GDBStub,
463 "Found breakpoint type {} @ {:016X}, range: {:016X}" 463 "Found breakpoint type {} @ {:016X}, range: {:016X}"
464 " - {:016X} ({:X} bytes)", 464 " - {:016X} ({:X} bytes)",
465 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); 465 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
466 return true; 466 return true;
467 } 467 }
468 } 468 }
@@ -478,7 +478,7 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
478static void SendPacket(const char packet) { 478static void SendPacket(const char packet) {
479 size_t sent_size = send(gdbserver_socket, &packet, 1, 0); 479 size_t sent_size = send(gdbserver_socket, &packet, 1, 0);
480 if (sent_size != 1) { 480 if (sent_size != 1) {
481 NGLOG_ERROR(Debug_GDBStub, "send failed"); 481 LOG_ERROR(Debug_GDBStub, "send failed");
482 } 482 }
483} 483}
484 484
@@ -492,13 +492,13 @@ static void SendReply(const char* reply) {
492 return; 492 return;
493 } 493 }
494 494
495 NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply); 495 LOG_DEBUG(Debug_GDBStub, "Reply: {}", reply);
496 496
497 memset(command_buffer, 0, sizeof(command_buffer)); 497 memset(command_buffer, 0, sizeof(command_buffer));
498 498
499 command_length = static_cast<u32>(strlen(reply)); 499 command_length = static_cast<u32>(strlen(reply));
500 if (command_length + 4 > sizeof(command_buffer)) { 500 if (command_length + 4 > sizeof(command_buffer)) {
501 NGLOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply"); 501 LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
502 return; 502 return;
503 } 503 }
504 504
@@ -515,7 +515,7 @@ static void SendReply(const char* reply) {
515 while (left > 0) { 515 while (left > 0) {
516 int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0); 516 int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
517 if (sent_size < 0) { 517 if (sent_size < 0) {
518 NGLOG_ERROR(Debug_GDBStub, "gdb: send failed"); 518 LOG_ERROR(Debug_GDBStub, "gdb: send failed");
519 return Shutdown(); 519 return Shutdown();
520 } 520 }
521 521
@@ -526,7 +526,7 @@ static void SendReply(const char* reply) {
526 526
527/// Handle query command from gdb client. 527/// Handle query command from gdb client.
528static void HandleQuery() { 528static void HandleQuery() {
529 NGLOG_DEBUG(Debug_GDBStub, "gdb: query '{}'", command_buffer + 1); 529 LOG_DEBUG(Debug_GDBStub, "gdb: query '{}'", command_buffer + 1);
530 530
531 const char* query = reinterpret_cast<const char*>(command_buffer + 1); 531 const char* query = reinterpret_cast<const char*>(command_buffer + 1);
532 532
@@ -634,18 +634,18 @@ static void ReadCommand() {
634 // ignore ack 634 // ignore ack
635 return; 635 return;
636 } else if (c == 0x03) { 636 } else if (c == 0x03) {
637 NGLOG_INFO(Debug_GDBStub, "gdb: found break command"); 637 LOG_INFO(Debug_GDBStub, "gdb: found break command");
638 halt_loop = true; 638 halt_loop = true;
639 SendSignal(current_thread, SIGTRAP); 639 SendSignal(current_thread, SIGTRAP);
640 return; 640 return;
641 } else if (c != GDB_STUB_START) { 641 } else if (c != GDB_STUB_START) {
642 NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); 642 LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c);
643 return; 643 return;
644 } 644 }
645 645
646 while ((c = ReadByte()) != GDB_STUB_END) { 646 while ((c = ReadByte()) != GDB_STUB_END) {
647 if (command_length >= sizeof(command_buffer)) { 647 if (command_length >= sizeof(command_buffer)) {
648 NGLOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow"); 648 LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow");
649 SendPacket(GDB_STUB_NACK); 649 SendPacket(GDB_STUB_NACK);
650 return; 650 return;
651 } 651 }
@@ -658,10 +658,9 @@ static void ReadCommand() {
658 u8 checksum_calculated = CalculateChecksum(command_buffer, command_length); 658 u8 checksum_calculated = CalculateChecksum(command_buffer, command_length);
659 659
660 if (checksum_received != checksum_calculated) { 660 if (checksum_received != checksum_calculated) {
661 NGLOG_ERROR( 661 LOG_ERROR(Debug_GDBStub,
662 Debug_GDBStub, 662 "gdb: invalid checksum: calculated {:02X} and read {:02X} for ${}# (length: {})",
663 "gdb: invalid checksum: calculated {:02X} and read {:02X} for ${}# (length: {})", 663 checksum_calculated, checksum_received, command_buffer, command_length);
664 checksum_calculated, checksum_received, command_buffer, command_length);
665 664
666 command_length = 0; 665 command_length = 0;
667 666
@@ -688,7 +687,7 @@ static bool IsDataAvailable() {
688 t.tv_usec = 0; 687 t.tv_usec = 0;
689 688
690 if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) { 689 if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) {
691 NGLOG_ERROR(Debug_GDBStub, "select failed"); 690 LOG_ERROR(Debug_GDBStub, "select failed");
692 return false; 691 return false;
693 } 692 }
694 693
@@ -801,7 +800,7 @@ static void ReadMemory() {
801 u64 len = 800 u64 len =
802 HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); 801 HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
803 802
804 NGLOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len); 803 LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len);
805 804
806 if (len * 2 > sizeof(reply)) { 805 if (len * 2 > sizeof(reply)) {
807 SendReply("E01"); 806 SendReply("E01");
@@ -888,8 +887,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
888 breakpoint.len = len; 887 breakpoint.len = len;
889 p.insert({addr, breakpoint}); 888 p.insert({addr, breakpoint});
890 889
891 NGLOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", 890 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
892 static_cast<int>(type), breakpoint.len, breakpoint.addr); 891 static_cast<int>(type), breakpoint.len, breakpoint.addr);
893 892
894 return true; 893 return true;
895} 894}
@@ -996,7 +995,7 @@ void HandlePacket() {
996 return; 995 return;
997 } 996 }
998 997
999 NGLOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer); 998 LOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer);
1000 999
1001 switch (command_buffer[0]) { 1000 switch (command_buffer[0]) {
1002 case 'q': 1001 case 'q':
@@ -1010,7 +1009,7 @@ void HandlePacket() {
1010 break; 1009 break;
1011 case 'k': 1010 case 'k':
1012 Shutdown(); 1011 Shutdown();
1013 NGLOG_INFO(Debug_GDBStub, "killed by gdb"); 1012 LOG_INFO(Debug_GDBStub, "killed by gdb");
1014 return; 1013 return;
1015 case 'g': 1014 case 'g':
1016 ReadRegisters(); 1015 ReadRegisters();
@@ -1092,7 +1091,7 @@ static void Init(u16 port) {
1092 breakpoints_write.clear(); 1091 breakpoints_write.clear();
1093 1092
1094 // Start gdb server 1093 // Start gdb server
1095 NGLOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port); 1094 LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port);
1096 1095
1097 sockaddr_in saddr_server = {}; 1096 sockaddr_in saddr_server = {};
1098 saddr_server.sin_family = AF_INET; 1097 saddr_server.sin_family = AF_INET;
@@ -1105,28 +1104,28 @@ static void Init(u16 port) {
1105 1104
1106 int tmpsock = static_cast<int>(socket(PF_INET, SOCK_STREAM, 0)); 1105 int tmpsock = static_cast<int>(socket(PF_INET, SOCK_STREAM, 0));
1107 if (tmpsock == -1) { 1106 if (tmpsock == -1) {
1108 NGLOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); 1107 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
1109 } 1108 }
1110 1109
1111 // Set socket to SO_REUSEADDR so it can always bind on the same port 1110 // Set socket to SO_REUSEADDR so it can always bind on the same port
1112 int reuse_enabled = 1; 1111 int reuse_enabled = 1;
1113 if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, 1112 if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled,
1114 sizeof(reuse_enabled)) < 0) { 1113 sizeof(reuse_enabled)) < 0) {
1115 NGLOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option"); 1114 LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option");
1116 } 1115 }
1117 1116
1118 const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server); 1117 const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
1119 socklen_t server_addrlen = sizeof(saddr_server); 1118 socklen_t server_addrlen = sizeof(saddr_server);
1120 if (bind(tmpsock, server_addr, server_addrlen) < 0) { 1119 if (bind(tmpsock, server_addr, server_addrlen) < 0) {
1121 NGLOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket"); 1120 LOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket");
1122 } 1121 }
1123 1122
1124 if (listen(tmpsock, 1) < 0) { 1123 if (listen(tmpsock, 1) < 0) {
1125 NGLOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket"); 1124 LOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket");
1126 } 1125 }
1127 1126
1128 // Wait for gdb to connect 1127 // Wait for gdb to connect
1129 NGLOG_INFO(Debug_GDBStub, "Waiting for gdb to connect..."); 1128 LOG_INFO(Debug_GDBStub, "Waiting for gdb to connect...");
1130 sockaddr_in saddr_client; 1129 sockaddr_in saddr_client;
1131 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client); 1130 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
1132 socklen_t client_addrlen = sizeof(saddr_client); 1131 socklen_t client_addrlen = sizeof(saddr_client);
@@ -1137,9 +1136,9 @@ static void Init(u16 port) {
1137 halt_loop = false; 1136 halt_loop = false;
1138 step_loop = false; 1137 step_loop = false;
1139 1138
1140 NGLOG_ERROR(Debug_GDBStub, "Failed to accept gdb client"); 1139 LOG_ERROR(Debug_GDBStub, "Failed to accept gdb client");
1141 } else { 1140 } else {
1142 NGLOG_INFO(Debug_GDBStub, "Client connected."); 1141 LOG_INFO(Debug_GDBStub, "Client connected.");
1143 saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr); 1142 saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
1144 } 1143 }
1145 1144
@@ -1158,7 +1157,7 @@ void Shutdown() {
1158 return; 1157 return;
1159 } 1158 }
1160 1159
1161 NGLOG_INFO(Debug_GDBStub, "Stopping GDB ..."); 1160 LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
1162 if (gdbserver_socket != -1) { 1161 if (gdbserver_socket != -1) {
1163 shutdown(gdbserver_socket, SHUT_RDWR); 1162 shutdown(gdbserver_socket, SHUT_RDWR);
1164 gdbserver_socket = -1; 1163 gdbserver_socket = -1;
@@ -1168,7 +1167,7 @@ void Shutdown() {
1168 WSACleanup(); 1167 WSACleanup();
1169#endif 1168#endif
1170 1169
1171 NGLOG_INFO(Debug_GDBStub, "GDB stopped."); 1170 LOG_INFO(Debug_GDBStub, "GDB stopped.");
1172} 1171}
1173 1172
1174bool IsServerEnabled() { 1173bool IsServerEnabled() {
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
new file mode 100644
index 000000000..e9c8369d7
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -0,0 +1,173 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "core/core.h"
9#include "core/hle/kernel/errors.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/thread.h"
13#include "core/hle/lock.h"
14#include "core/memory.h"
15
16namespace Kernel {
17namespace AddressArbiter {
18
19// Performs actual address waiting logic.
20static ResultCode WaitForAddress(VAddr address, s64 timeout) {
21 SharedPtr<Thread> current_thread = GetCurrentThread();
22 current_thread->arb_wait_address = address;
23 current_thread->status = THREADSTATUS_WAIT_ARB;
24 current_thread->wakeup_callback = nullptr;
25
26 current_thread->WakeAfterDelay(timeout);
27
28 Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
29 return RESULT_TIMEOUT;
30}
31
32// Gets the threads waiting on an address.
33static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads,
34 VAddr address) {
35 auto RetrieveWaitingThreads =
36 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
37 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
38 auto& thread_list = scheduler->GetThreadList();
39
40 for (auto& thread : thread_list) {
41 if (thread->arb_wait_address == arb_addr)
42 waiting_threads.push_back(thread);
43 }
44 };
45
46 // Retrieve a list of all threads that are waiting for this address.
47 RetrieveWaitingThreads(0, waiting_threads, address);
48 RetrieveWaitingThreads(1, waiting_threads, address);
49 RetrieveWaitingThreads(2, waiting_threads, address);
50 RetrieveWaitingThreads(3, waiting_threads, address);
51 // Sort them by priority, such that the highest priority ones come first.
52 std::sort(waiting_threads.begin(), waiting_threads.end(),
53 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
54 return lhs->current_priority < rhs->current_priority;
55 });
56}
57
58// Wake up num_to_wake (or all) threads in a vector.
59static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
60 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
61 // them all.
62 size_t last = waiting_threads.size();
63 if (num_to_wake > 0)
64 last = num_to_wake;
65
66 // Signal the waiting threads.
67 for (size_t i = 0; i < last; i++) {
68 ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
69 waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
70 waiting_threads[i]->arb_wait_address = 0;
71 waiting_threads[i]->ResumeFromWait();
72 }
73}
74
75// Signals an address being waited on.
76ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
77 // Get threads waiting on the address.
78 std::vector<SharedPtr<Thread>> waiting_threads;
79 GetThreadsWaitingOnAddress(waiting_threads, address);
80
81 WakeThreads(waiting_threads, num_to_wake);
82 return RESULT_SUCCESS;
83}
84
85// Signals an address being waited on and increments its value if equal to the value argument.
86ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
87 // Ensure that we can write to the address.
88 if (!Memory::IsValidVirtualAddress(address)) {
89 return ERR_INVALID_ADDRESS_STATE;
90 }
91
92 if (static_cast<s32>(Memory::Read32(address)) == value) {
93 Memory::Write32(address, static_cast<u32>(value + 1));
94 } else {
95 return ERR_INVALID_STATE;
96 }
97
98 return SignalToAddress(address, num_to_wake);
99}
100
101// Signals an address being waited on and modifies its value based on waiting thread count if equal
102// to the value argument.
103ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
104 s32 num_to_wake) {
105 // Ensure that we can write to the address.
106 if (!Memory::IsValidVirtualAddress(address)) {
107 return ERR_INVALID_ADDRESS_STATE;
108 }
109
110 // Get threads waiting on the address.
111 std::vector<SharedPtr<Thread>> waiting_threads;
112 GetThreadsWaitingOnAddress(waiting_threads, address);
113
114 // Determine the modified value depending on the waiting count.
115 s32 updated_value;
116 if (waiting_threads.size() == 0) {
117 updated_value = value - 1;
118 } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
119 updated_value = value + 1;
120 } else {
121 updated_value = value;
122 }
123
124 if (static_cast<s32>(Memory::Read32(address)) == value) {
125 Memory::Write32(address, static_cast<u32>(updated_value));
126 } else {
127 return ERR_INVALID_STATE;
128 }
129
130 WakeThreads(waiting_threads, num_to_wake);
131 return RESULT_SUCCESS;
132}
133
134// Waits on an address if the value passed is less than the argument value, optionally decrementing.
135ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) {
136 // Ensure that we can read the address.
137 if (!Memory::IsValidVirtualAddress(address)) {
138 return ERR_INVALID_ADDRESS_STATE;
139 }
140
141 s32 cur_value = static_cast<s32>(Memory::Read32(address));
142 if (cur_value < value) {
143 Memory::Write32(address, static_cast<u32>(cur_value - 1));
144 } else {
145 return ERR_INVALID_STATE;
146 }
147 // Short-circuit without rescheduling, if timeout is zero.
148 if (timeout == 0) {
149 return RESULT_TIMEOUT;
150 }
151
152 return WaitForAddress(address, timeout);
153}
154
155// Waits on an address if the value passed is equal to the argument value.
156ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
157 // Ensure that we can read the address.
158 if (!Memory::IsValidVirtualAddress(address)) {
159 return ERR_INVALID_ADDRESS_STATE;
160 }
161 // Only wait for the address if equal.
162 if (static_cast<s32>(Memory::Read32(address)) != value) {
163 return ERR_INVALID_STATE;
164 }
165 // Short-circuit without rescheduling, if timeout is zero.
166 if (timeout == 0) {
167 return RESULT_TIMEOUT;
168 }
169
170 return WaitForAddress(address, timeout);
171}
172} // namespace AddressArbiter
173} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
new file mode 100644
index 000000000..f20f3dbc0
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -0,0 +1,32 @@
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/result.h"
8
9namespace Kernel {
10
11namespace AddressArbiter {
12enum class ArbitrationType {
13 WaitIfLessThan = 0,
14 DecrementAndWaitIfLessThan = 1,
15 WaitIfEqual = 2,
16};
17
18enum class SignalType {
19 Signal = 0,
20 IncrementAndSignalIfEqual = 1,
21 ModifyByWaitingCountAndSignalIfEqual = 2,
22};
23
24ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
25ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
26ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
27
28ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
29ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
30} // namespace AddressArbiter
31
32} // namespace Kernel
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index e1b5430bf..221cb1bb5 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -20,13 +20,16 @@ enum {
20 MaxConnectionsReached = 52, 20 MaxConnectionsReached = 52,
21 21
22 // Confirmed Switch OS error codes 22 // Confirmed Switch OS error codes
23 MisalignedAddress = 102, 23 InvalidAddress = 102,
24 InvalidMemoryState = 106,
24 InvalidProcessorId = 113, 25 InvalidProcessorId = 113,
25 InvalidHandle = 114, 26 InvalidHandle = 114,
26 InvalidCombination = 116, 27 InvalidCombination = 116,
27 Timeout = 117, 28 Timeout = 117,
28 SynchronizationCanceled = 118, 29 SynchronizationCanceled = 118,
29 TooLarge = 119, 30 TooLarge = 119,
31 InvalidEnumValue = 120,
32 InvalidState = 125,
30}; 33};
31} 34}
32 35
@@ -39,14 +42,15 @@ constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
39constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1); 42constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1);
40constexpr ResultCode ERR_WRONG_PERMISSION(-1); 43constexpr ResultCode ERR_WRONG_PERMISSION(-1);
41constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1); 44constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
42constexpr ResultCode ERR_INVALID_ENUM_VALUE(-1); 45constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
43constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); 46constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
44constexpr ResultCode ERR_INVALID_COMBINATION(-1); 47constexpr ResultCode ERR_INVALID_COMBINATION(-1);
45constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1); 48constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1);
46constexpr ResultCode ERR_OUT_OF_MEMORY(-1); 49constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
47constexpr ResultCode ERR_INVALID_ADDRESS(-1); 50constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
48constexpr ResultCode ERR_INVALID_ADDRESS_STATE(-1); 51constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
49constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 52constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
53constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
50constexpr ResultCode ERR_INVALID_POINTER(-1); 54constexpr ResultCode ERR_INVALID_POINTER(-1);
51constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); 55constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
52constexpr ResultCode ERR_NOT_AUTHORIZED(-1); 56constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index f7a9920d8..7dd67f80f 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -26,7 +26,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
26 26
27 u16 slot = next_free_slot; 27 u16 slot = next_free_slot;
28 if (slot >= generations.size()) { 28 if (slot >= generations.size()) {
29 NGLOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); 29 LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
30 return ERR_OUT_OF_HANDLES; 30 return ERR_OUT_OF_HANDLES;
31 } 31 }
32 next_free_slot = generations[slot]; 32 next_free_slot = generations[slot];
@@ -48,7 +48,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
48ResultVal<Handle> HandleTable::Duplicate(Handle handle) { 48ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
49 SharedPtr<Object> object = GetGeneric(handle); 49 SharedPtr<Object> object = GetGeneric(handle);
50 if (object == nullptr) { 50 if (object == nullptr) {
51 NGLOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle); 51 LOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle);
52 return ERR_INVALID_HANDLE; 52 return ERR_INVALID_HANDLE;
53 } 53 }
54 return Create(std::move(object)); 54 return Create(std::move(object));
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 01904467e..609cdbff2 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -120,7 +120,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
120 std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); 120 std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
121 } else { 121 } else {
122 if (Session()->IsDomain()) 122 if (Session()->IsDomain())
123 NGLOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); 123 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
124 } 124 }
125 } 125 }
126 126
@@ -271,11 +271,16 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
271} 271}
272 272
273size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const { 273size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const {
274 if (size == 0) {
275 LOG_WARNING(Core, "skip empty buffer write");
276 return 0;
277 }
278
274 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; 279 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
275 const size_t buffer_size{GetWriteBufferSize(buffer_index)}; 280 const size_t buffer_size{GetWriteBufferSize(buffer_index)};
276 if (size > buffer_size) { 281 if (size > buffer_size) {
277 NGLOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, 282 LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
278 buffer_size); 283 buffer_size);
279 size = buffer_size; // TODO(bunnei): This needs to be HW tested 284 size = buffer_size; // TODO(bunnei): This needs to be HW tested
280 } 285 }
281 286
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index bc144f3de..65560226d 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -59,7 +59,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
59 Handle requesting_thread_handle) { 59 Handle requesting_thread_handle) {
60 // The mutex address must be 4-byte aligned 60 // The mutex address must be 4-byte aligned
61 if ((address % sizeof(u32)) != 0) { 61 if ((address % sizeof(u32)) != 0) {
62 return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); 62 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
63 } 63 }
64 64
65 SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); 65 SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
@@ -97,7 +97,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
97ResultCode Mutex::Release(VAddr address) { 97ResultCode Mutex::Release(VAddr address) {
98 // The mutex address must be 4-byte aligned 98 // The mutex address must be 4-byte aligned
99 if ((address % sizeof(u32)) != 0) { 99 if ((address % sizeof(u32)) != 0) {
100 return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); 100 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
101 } 101 }
102 102
103 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); 103 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 651d932d3..0c0506085 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -54,7 +54,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
54 continue; 54 continue;
55 } else if ((type & 0xF00) == 0xE00) { // 0x0FFF 55 } else if ((type & 0xF00) == 0xE00) { // 0x0FFF
56 // Allowed interrupts list 56 // Allowed interrupts list
57 NGLOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); 57 LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored");
58 } else if ((type & 0xF80) == 0xF00) { // 0x07FF 58 } else if ((type & 0xF80) == 0xF00) { // 0x07FF
59 // Allowed syscalls mask 59 // Allowed syscalls mask
60 unsigned int index = ((descriptor >> 24) & 7) * 24; 60 unsigned int index = ((descriptor >> 24) & 7) * 24;
@@ -74,7 +74,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
74 } else if ((type & 0xFFE) == 0xFF8) { // 0x001F 74 } else if ((type & 0xFFE) == 0xFF8) { // 0x001F
75 // Mapped memory range 75 // Mapped memory range
76 if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) { 76 if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) {
77 NGLOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); 77 LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored.");
78 continue; 78 continue;
79 } 79 }
80 u32 end_desc = kernel_caps[i + 1]; 80 u32 end_desc = kernel_caps[i + 1];
@@ -109,9 +109,9 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
109 109
110 int minor = kernel_version & 0xFF; 110 int minor = kernel_version & 0xFF;
111 int major = (kernel_version >> 8) & 0xFF; 111 int major = (kernel_version >> 8) & 0xFF;
112 NGLOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); 112 LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor);
113 } else { 113 } else {
114 NGLOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); 114 LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor);
115 } 115 }
116 } 116 }
117} 117}
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index 0ef5fc57d..17a3e8a74 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -29,7 +29,7 @@ SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory cat
29 case ResourceLimitCategory::OTHER: 29 case ResourceLimitCategory::OTHER:
30 return resource_limits[static_cast<u8>(category)]; 30 return resource_limits[static_cast<u8>(category)];
31 default: 31 default:
32 NGLOG_CRITICAL(Kernel, "Unknown resource limit category"); 32 LOG_CRITICAL(Kernel, "Unknown resource limit category");
33 UNREACHABLE(); 33 UNREACHABLE();
34 } 34 }
35} 35}
@@ -55,7 +55,7 @@ s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
55 case ResourceType::CPUTime: 55 case ResourceType::CPUTime:
56 return current_cpu_time; 56 return current_cpu_time;
57 default: 57 default:
58 NGLOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); 58 LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
59 UNIMPLEMENTED(); 59 UNIMPLEMENTED();
60 return 0; 60 return 0;
61 } 61 }
@@ -84,7 +84,7 @@ u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
84 case ResourceType::CPUTime: 84 case ResourceType::CPUTime:
85 return max_cpu_time; 85 return max_cpu_time;
86 default: 86 default:
87 NGLOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); 87 LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
88 UNIMPLEMENTED(); 88 UNIMPLEMENTED();
89 return 0; 89 return 0;
90 } 90 }
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 9cb9e0e5c..11c2cb69e 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -99,11 +99,11 @@ void Scheduler::Reschedule() {
99 Thread* next = PopNextReadyThread(); 99 Thread* next = PopNextReadyThread();
100 100
101 if (cur && next) { 101 if (cur && next) {
102 NGLOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId()); 102 LOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId());
103 } else if (cur) { 103 } else if (cur) {
104 NGLOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId()); 104 LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId());
105 } else if (next) { 105 } else if (next) {
106 NGLOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId()); 106 LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId());
107 } 107 }
108 108
109 SwitchContext(next); 109 SwitchContext(next);
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index bf812c543..0d5cba1d9 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -71,7 +71,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
71 return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); 71 return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
72 72
73 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { 73 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
74 NGLOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); 74 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
75 75
76 domain_request_handlers[object_id - 1] = nullptr; 76 domain_request_handlers[object_id - 1] = nullptr;
77 77
@@ -81,8 +81,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
81 } 81 }
82 } 82 }
83 83
84 NGLOG_CRITICAL(IPC, "Unknown domain command={}", 84 LOG_CRITICAL(IPC, "Unknown domain command={}",
85 static_cast<int>(domain_message_header->command.Value())); 85 static_cast<int>(domain_message_header->command.Value()));
86 ASSERT(false); 86 ASSERT(false);
87 } 87 }
88 88
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index ac4921298..93f7f2772 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -107,16 +107,16 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
107 107
108 // Error out if the requested permissions don't match what the creator process allows. 108 // Error out if the requested permissions don't match what the creator process allows.
109 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { 109 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
110 NGLOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", 110 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
111 GetObjectId(), address, name); 111 GetObjectId(), address, name);
112 return ERR_INVALID_COMBINATION; 112 return ERR_INVALID_COMBINATION;
113 } 113 }
114 114
115 // Error out if the provided permissions are not compatible with what the creator process needs. 115 // Error out if the provided permissions are not compatible with what the creator process needs.
116 if (other_permissions != MemoryPermission::DontCare && 116 if (other_permissions != MemoryPermission::DontCare &&
117 static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { 117 static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
118 NGLOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", 118 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
119 GetObjectId(), address, name); 119 GetObjectId(), address, name);
120 return ERR_WRONG_PERMISSION; 120 return ERR_WRONG_PERMISSION;
121 } 121 }
122 122
@@ -131,7 +131,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
131 auto result = target_process->vm_manager.MapMemoryBlock( 131 auto result = target_process->vm_manager.MapMemoryBlock(
132 target_address, backing_block, backing_block_offset, size, MemoryState::Shared); 132 target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
133 if (result.Failed()) { 133 if (result.Failed()) {
134 NGLOG_ERROR( 134 LOG_ERROR(
135 Kernel, 135 Kernel,
136 "cannot map id={}, target_address=0x{:X} name={}, error mapping to virtual memory", 136 "cannot map id={}, target_address=0x{:X} name={}, error mapping to virtual memory",
137 GetObjectId(), target_address, name); 137 GetObjectId(), target_address, name);
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index ec3601e8b..5ad923fe7 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -11,6 +11,7 @@
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/core_timing.h" 13#include "core/core_timing.h"
14#include "core/hle/kernel/address_arbiter.h"
14#include "core/hle/kernel/client_port.h" 15#include "core/hle/kernel/client_port.h"
15#include "core/hle/kernel/client_session.h" 16#include "core/hle/kernel/client_session.h"
16#include "core/hle/kernel/event.h" 17#include "core/hle/kernel/event.h"
@@ -31,7 +32,7 @@ namespace Kernel {
31 32
32/// Set the process heap to a given Size. It can both extend and shrink the heap. 33/// Set the process heap to a given Size. It can both extend and shrink the heap.
33static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { 34static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
34 NGLOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); 35 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
35 auto& process = *Core::CurrentProcess(); 36 auto& process = *Core::CurrentProcess();
36 CASCADE_RESULT(*heap_addr, 37 CASCADE_RESULT(*heap_addr,
37 process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); 38 process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
@@ -39,21 +40,21 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
39} 40}
40 41
41static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { 42static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
42 NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x{:X}", addr); 43 LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x{:X}", addr);
43 return RESULT_SUCCESS; 44 return RESULT_SUCCESS;
44} 45}
45 46
46/// Maps a memory range into a different range. 47/// Maps a memory range into a different range.
47static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 48static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
48 NGLOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 49 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
49 src_addr, size); 50 src_addr, size);
50 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); 51 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
51} 52}
52 53
53/// Unmaps a region that was previously mapped with svcMapMemory 54/// Unmaps a region that was previously mapped with svcMapMemory
54static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 55static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
55 NGLOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 56 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
56 src_addr, size); 57 src_addr, size);
57 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); 58 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
58} 59}
59 60
@@ -68,11 +69,11 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
68 if (port_name.size() > PortNameMaxLength) 69 if (port_name.size() > PortNameMaxLength)
69 return ERR_PORT_NAME_TOO_LONG; 70 return ERR_PORT_NAME_TOO_LONG;
70 71
71 NGLOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 72 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
72 73
73 auto it = Service::g_kernel_named_ports.find(port_name); 74 auto it = Service::g_kernel_named_ports.find(port_name);
74 if (it == Service::g_kernel_named_ports.end()) { 75 if (it == Service::g_kernel_named_ports.end()) {
75 NGLOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); 76 LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
76 return ERR_NOT_FOUND; 77 return ERR_NOT_FOUND;
77 } 78 }
78 79
@@ -90,11 +91,11 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
90static ResultCode SendSyncRequest(Handle handle) { 91static ResultCode SendSyncRequest(Handle handle) {
91 SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle); 92 SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle);
92 if (!session) { 93 if (!session) {
93 NGLOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); 94 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
94 return ERR_INVALID_HANDLE; 95 return ERR_INVALID_HANDLE;
95 } 96 }
96 97
97 NGLOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); 98 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
98 99
99 Core::System::GetInstance().PrepareReschedule(); 100 Core::System::GetInstance().PrepareReschedule();
100 101
@@ -105,7 +106,7 @@ static ResultCode SendSyncRequest(Handle handle) {
105 106
106/// Get the ID for the specified thread. 107/// Get the ID for the specified thread.
107static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { 108static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
108 NGLOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 109 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
109 110
110 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); 111 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
111 if (!thread) { 112 if (!thread) {
@@ -118,7 +119,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
118 119
119/// Get the ID of the specified process 120/// Get the ID of the specified process
120static ResultCode GetProcessId(u32* process_id, Handle process_handle) { 121static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
121 NGLOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); 122 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
122 123
123 const SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle); 124 const SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle);
124 if (!process) { 125 if (!process) {
@@ -148,8 +149,8 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr
148/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 149/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
149static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, 150static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count,
150 s64 nano_seconds) { 151 s64 nano_seconds) {
151 NGLOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", 152 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
152 handles_address, handle_count, nano_seconds); 153 handles_address, handle_count, nano_seconds);
153 154
154 if (!Memory::IsValidVirtualAddress(handles_address)) 155 if (!Memory::IsValidVirtualAddress(handles_address))
155 return ERR_INVALID_POINTER; 156 return ERR_INVALID_POINTER;
@@ -209,7 +210,7 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
209 210
210/// Resumes a thread waiting on WaitSynchronization 211/// Resumes a thread waiting on WaitSynchronization
211static ResultCode CancelSynchronization(Handle thread_handle) { 212static ResultCode CancelSynchronization(Handle thread_handle) {
212 NGLOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); 213 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
213 214
214 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); 215 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
215 if (!thread) { 216 if (!thread) {
@@ -226,24 +227,24 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
226/// Attempts to locks a mutex, creating it if it does not already exist 227/// Attempts to locks a mutex, creating it if it does not already exist
227static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, 228static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
228 Handle requesting_thread_handle) { 229 Handle requesting_thread_handle) {
229 NGLOG_TRACE(Kernel_SVC, 230 LOG_TRACE(Kernel_SVC,
230 "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " 231 "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, "
231 "requesting_current_thread_handle=0x{:08X}", 232 "requesting_current_thread_handle=0x{:08X}",
232 holding_thread_handle, mutex_addr, requesting_thread_handle); 233 holding_thread_handle, mutex_addr, requesting_thread_handle);
233 234
234 return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle); 235 return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle);
235} 236}
236 237
237/// Unlock a mutex 238/// Unlock a mutex
238static ResultCode ArbitrateUnlock(VAddr mutex_addr) { 239static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
239 NGLOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); 240 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
240 241
241 return Mutex::Release(mutex_addr); 242 return Mutex::Release(mutex_addr);
242} 243}
243 244
244/// Break program execution 245/// Break program execution
245static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { 246static void Break(u64 unk_0, u64 unk_1, u64 unk_2) {
246 NGLOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); 247 LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!");
247 ASSERT(false); 248 ASSERT(false);
248} 249}
249 250
@@ -251,13 +252,13 @@ static void Break(u64 unk_0, u64 unk_1, u64 unk_2) {
251static void OutputDebugString(VAddr address, s32 len) { 252static void OutputDebugString(VAddr address, s32 len) {
252 std::string str(len, '\0'); 253 std::string str(len, '\0');
253 Memory::ReadBlock(address, str.data(), str.size()); 254 Memory::ReadBlock(address, str.data(), str.size());
254 NGLOG_DEBUG(Debug_Emulated, "{}", str); 255 LOG_DEBUG(Debug_Emulated, "{}", str);
255} 256}
256 257
257/// Gets system/memory information for the current process 258/// Gets system/memory information for the current process
258static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { 259static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) {
259 NGLOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 260 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
260 info_sub_id, handle); 261 info_sub_id, handle);
261 262
262 auto& vm_manager = Core::CurrentProcess()->vm_manager; 263 auto& vm_manager = Core::CurrentProcess()->vm_manager;
263 264
@@ -308,12 +309,17 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
308 *result = Core::CurrentProcess()->is_virtual_address_memory_enabled; 309 *result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
309 break; 310 break;
310 case GetInfoType::TitleId: 311 case GetInfoType::TitleId:
311 NGLOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0"); 312 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0");
312 *result = 0; 313 *result = 0;
313 break; 314 break;
314 case GetInfoType::PrivilegedProcessId: 315 case GetInfoType::PrivilegedProcessId:
315 NGLOG_WARNING(Kernel_SVC, 316 LOG_WARNING(Kernel_SVC,
316 "(STUBBED) Attempted to query privileged process id bounds, returned 0"); 317 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
318 *result = 0;
319 break;
320 case GetInfoType::UserExceptionContextAddr:
321 LOG_WARNING(Kernel_SVC,
322 "(STUBBED) Attempted to query user exception context address, returned 0");
317 *result = 0; 323 *result = 0;
318 break; 324 break;
319 default: 325 default:
@@ -325,14 +331,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
325 331
326/// Sets the thread activity 332/// Sets the thread activity
327static ResultCode SetThreadActivity(Handle handle, u32 unknown) { 333static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
328 NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, 334 LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
329 unknown);
330 return RESULT_SUCCESS; 335 return RESULT_SUCCESS;
331} 336}
332 337
333/// Gets the thread context 338/// Gets the thread context
334static ResultCode GetThreadContext(Handle handle, VAddr addr) { 339static ResultCode GetThreadContext(Handle handle, VAddr addr) {
335 NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr); 340 LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr);
336 return RESULT_SUCCESS; 341 return RESULT_SUCCESS;
337} 342}
338 343
@@ -371,16 +376,15 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
371 376
372/// Get which CPU core is executing the current thread 377/// Get which CPU core is executing the current thread
373static u32 GetCurrentProcessorNumber() { 378static u32 GetCurrentProcessorNumber() {
374 NGLOG_TRACE(Kernel_SVC, "called"); 379 LOG_TRACE(Kernel_SVC, "called");
375 return GetCurrentThread()->processor_id; 380 return GetCurrentThread()->processor_id;
376} 381}
377 382
378static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, 383static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size,
379 u32 permissions) { 384 u32 permissions) {
380 NGLOG_TRACE( 385 LOG_TRACE(Kernel_SVC,
381 Kernel_SVC, 386 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
382 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 387 shared_memory_handle, addr, size, permissions);
383 shared_memory_handle, addr, size, permissions);
384 388
385 SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); 389 SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
386 if (!shared_memory) { 390 if (!shared_memory) {
@@ -400,15 +404,15 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
400 return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, 404 return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
401 MemoryPermission::DontCare); 405 MemoryPermission::DontCare);
402 default: 406 default:
403 NGLOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); 407 LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions);
404 } 408 }
405 409
406 return RESULT_SUCCESS; 410 return RESULT_SUCCESS;
407} 411}
408 412
409static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 413static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
410 NGLOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", 414 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
411 shared_memory_handle, addr, size); 415 shared_memory_handle, addr, size);
412 416
413 SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); 417 SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
414 418
@@ -436,19 +440,19 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
436 memory_info->type = static_cast<u32>(vma->second.meminfo_state); 440 memory_info->type = static_cast<u32>(vma->second.meminfo_state);
437 } 441 }
438 442
439 NGLOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr); 443 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr);
440 return RESULT_SUCCESS; 444 return RESULT_SUCCESS;
441} 445}
442 446
443/// Query memory 447/// Query memory
444static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { 448static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) {
445 NGLOG_TRACE(Kernel_SVC, "called, addr={:X}", addr); 449 LOG_TRACE(Kernel_SVC, "called, addr={:X}", addr);
446 return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); 450 return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr);
447} 451}
448 452
449/// Exits the current process 453/// Exits the current process
450static void ExitProcess() { 454static void ExitProcess() {
451 NGLOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id); 455 LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id);
452 456
453 ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running, 457 ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
454 "Process has already exited"); 458 "Process has already exited");
@@ -524,17 +528,17 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
524 Core::System::GetInstance().PrepareReschedule(); 528 Core::System::GetInstance().PrepareReschedule();
525 Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); 529 Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
526 530
527 NGLOG_TRACE(Kernel_SVC, 531 LOG_TRACE(Kernel_SVC,
528 "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " 532 "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
529 "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", 533 "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
530 entry_point, name, arg, stack_top, priority, processor_id, *out_handle); 534 entry_point, name, arg, stack_top, priority, processor_id, *out_handle);
531 535
532 return RESULT_SUCCESS; 536 return RESULT_SUCCESS;
533} 537}
534 538
535/// Starts the thread for the provided handle 539/// Starts the thread for the provided handle
536static ResultCode StartThread(Handle thread_handle) { 540static ResultCode StartThread(Handle thread_handle) {
537 NGLOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 541 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
538 542
539 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); 543 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
540 if (!thread) { 544 if (!thread) {
@@ -551,7 +555,7 @@ static ResultCode StartThread(Handle thread_handle) {
551 555
552/// Called when a thread exits 556/// Called when a thread exits
553static void ExitThread() { 557static void ExitThread() {
554 NGLOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); 558 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC());
555 559
556 ExitCurrentThread(); 560 ExitCurrentThread();
557 Core::System::GetInstance().PrepareReschedule(); 561 Core::System::GetInstance().PrepareReschedule();
@@ -559,7 +563,7 @@ static void ExitThread() {
559 563
560/// Sleep the current thread 564/// Sleep the current thread
561static void SleepThread(s64 nanoseconds) { 565static void SleepThread(s64 nanoseconds) {
562 NGLOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); 566 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
563 567
564 // Don't attempt to yield execution if there are no available threads to run, 568 // Don't attempt to yield execution if there are no available threads to run,
565 // this way we avoid a useless reschedule to the idle thread. 569 // this way we avoid a useless reschedule to the idle thread.
@@ -575,10 +579,10 @@ static void SleepThread(s64 nanoseconds) {
575 Core::System::GetInstance().PrepareReschedule(); 579 Core::System::GetInstance().PrepareReschedule();
576} 580}
577 581
578/// Signal process wide key atomic 582/// Wait process wide key atomic
579static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, 583static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr,
580 Handle thread_handle, s64 nano_seconds) { 584 Handle thread_handle, s64 nano_seconds) {
581 NGLOG_TRACE( 585 LOG_TRACE(
582 Kernel_SVC, 586 Kernel_SVC,
583 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 587 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
584 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 588 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
@@ -605,8 +609,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
605 609
606/// Signal process wide key 610/// Signal process wide key
607static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { 611static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) {
608 NGLOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", 612 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
609 condition_variable_addr, target); 613 condition_variable_addr, target);
610 614
611 auto RetrieveWaitingThreads = 615 auto RetrieveWaitingThreads =
612 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) { 616 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) {
@@ -684,6 +688,57 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
684 return RESULT_SUCCESS; 688 return RESULT_SUCCESS;
685} 689}
686 690
691// Wait for an address (via Address Arbiter)
692static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) {
693 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
694 address, type, value, timeout);
695 // If the passed address is a kernel virtual address, return invalid memory state.
696 if (Memory::IsKernelVirtualAddress(address)) {
697 return ERR_INVALID_ADDRESS_STATE;
698 }
699 // If the address is not properly aligned to 4 bytes, return invalid address.
700 if (address % sizeof(u32) != 0) {
701 return ERR_INVALID_ADDRESS;
702 }
703
704 switch (static_cast<AddressArbiter::ArbitrationType>(type)) {
705 case AddressArbiter::ArbitrationType::WaitIfLessThan:
706 return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false);
707 case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
708 return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true);
709 case AddressArbiter::ArbitrationType::WaitIfEqual:
710 return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
711 default:
712 return ERR_INVALID_ENUM_VALUE;
713 }
714}
715
716// Signals to an address (via Address Arbiter)
717static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) {
718 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
719 address, type, value, num_to_wake);
720 // If the passed address is a kernel virtual address, return invalid memory state.
721 if (Memory::IsKernelVirtualAddress(address)) {
722 return ERR_INVALID_ADDRESS_STATE;
723 }
724 // If the address is not properly aligned to 4 bytes, return invalid address.
725 if (address % sizeof(u32) != 0) {
726 return ERR_INVALID_ADDRESS;
727 }
728
729 switch (static_cast<AddressArbiter::SignalType>(type)) {
730 case AddressArbiter::SignalType::Signal:
731 return AddressArbiter::SignalToAddress(address, num_to_wake);
732 case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
733 return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
734 case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
735 return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
736 num_to_wake);
737 default:
738 return ERR_INVALID_ENUM_VALUE;
739 }
740}
741
687/// This returns the total CPU ticks elapsed since the CPU was powered-on 742/// This returns the total CPU ticks elapsed since the CPU was powered-on
688static u64 GetSystemTick() { 743static u64 GetSystemTick() {
689 const u64 result{CoreTiming::GetTicks()}; 744 const u64 result{CoreTiming::GetTicks()};
@@ -696,13 +751,13 @@ static u64 GetSystemTick() {
696 751
697/// Close a handle 752/// Close a handle
698static ResultCode CloseHandle(Handle handle) { 753static ResultCode CloseHandle(Handle handle) {
699 NGLOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 754 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
700 return g_handle_table.Close(handle); 755 return g_handle_table.Close(handle);
701} 756}
702 757
703/// Reset an event 758/// Reset an event
704static ResultCode ResetSignal(Handle handle) { 759static ResultCode ResetSignal(Handle handle) {
705 NGLOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 760 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
706 auto event = g_handle_table.Get<Event>(handle); 761 auto event = g_handle_table.Get<Event>(handle);
707 ASSERT(event != nullptr); 762 ASSERT(event != nullptr);
708 event->Clear(); 763 event->Clear();
@@ -711,14 +766,14 @@ static ResultCode ResetSignal(Handle handle) {
711 766
712/// Creates a TransferMemory object 767/// Creates a TransferMemory object
713static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { 768static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
714 NGLOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, 769 LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
715 size, permissions); 770 permissions);
716 *handle = 0; 771 *handle = 0;
717 return RESULT_SUCCESS; 772 return RESULT_SUCCESS;
718} 773}
719 774
720static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 775static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
721 NGLOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 776 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
722 777
723 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); 778 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
724 if (!thread) { 779 if (!thread) {
@@ -732,8 +787,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
732} 787}
733 788
734static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { 789static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
735 NGLOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, 790 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
736 mask, core); 791 mask, core);
737 792
738 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); 793 const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
739 if (!thread) { 794 if (!thread) {
@@ -744,7 +799,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
744 ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT); 799 ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT);
745 // Set the target CPU to the one specified in the process' exheader. 800 // Set the target CPU to the one specified in the process' exheader.
746 core = thread->owner_process->ideal_processor; 801 core = thread->owner_process->ideal_processor;
747 mask = 1 << core; 802 mask = 1ull << core;
748 } 803 }
749 804
750 if (mask == 0) { 805 if (mask == 0) {
@@ -761,7 +816,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
761 } 816 }
762 817
763 // Error out if the input core isn't enabled in the input mask. 818 // Error out if the input core isn't enabled in the input mask.
764 if (core < Core::NUM_CPU_CORES && (mask & (1 << core)) == 0) { 819 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
765 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 820 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
766 } 821 }
767 822
@@ -772,8 +827,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
772 827
773static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, 828static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions,
774 u32 remote_permissions) { 829 u32 remote_permissions) {
775 NGLOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, 830 LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
776 local_permissions, remote_permissions); 831 local_permissions, remote_permissions);
777 auto sharedMemHandle = 832 auto sharedMemHandle =
778 SharedMemory::Create(g_handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 833 SharedMemory::Create(g_handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
779 static_cast<MemoryPermission>(local_permissions), 834 static_cast<MemoryPermission>(local_permissions),
@@ -784,7 +839,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
784} 839}
785 840
786static ResultCode ClearEvent(Handle handle) { 841static ResultCode ClearEvent(Handle handle) {
787 NGLOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 842 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
788 843
789 SharedPtr<Event> evt = g_handle_table.Get<Event>(handle); 844 SharedPtr<Event> evt = g_handle_table.Get<Event>(handle);
790 if (evt == nullptr) 845 if (evt == nullptr)
@@ -856,8 +911,8 @@ static const FunctionDef SVC_Table[] = {
856 {0x31, nullptr, "GetResourceLimitCurrentValue"}, 911 {0x31, nullptr, "GetResourceLimitCurrentValue"},
857 {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"}, 912 {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
858 {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, 913 {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
859 {0x34, nullptr, "WaitForAddress"}, 914 {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
860 {0x35, nullptr, "SignalToAddress"}, 915 {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
861 {0x36, nullptr, "Unknown"}, 916 {0x36, nullptr, "Unknown"},
862 {0x37, nullptr, "Unknown"}, 917 {0x37, nullptr, "Unknown"},
863 {0x38, nullptr, "Unknown"}, 918 {0x38, nullptr, "Unknown"},
@@ -936,7 +991,7 @@ static const FunctionDef SVC_Table[] = {
936 991
937static const FunctionDef* GetSVCInfo(u32 func_num) { 992static const FunctionDef* GetSVCInfo(u32 func_num) {
938 if (func_num >= std::size(SVC_Table)) { 993 if (func_num >= std::size(SVC_Table)) {
939 NGLOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num); 994 LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
940 return nullptr; 995 return nullptr;
941 } 996 }
942 return &SVC_Table[func_num]; 997 return &SVC_Table[func_num];
@@ -955,10 +1010,10 @@ void CallSVC(u32 immediate) {
955 if (info->func) { 1010 if (info->func) {
956 info->func(); 1011 info->func();
957 } else { 1012 } else {
958 NGLOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); 1013 LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name);
959 } 1014 }
960 } else { 1015 } else {
961 NGLOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); 1016 LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate);
962 } 1017 }
963} 1018}
964 1019
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 40aa88cc1..79c3fe31b 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -179,6 +179,20 @@ void SvcWrap() {
179 FuncReturn(retval); 179 FuncReturn(retval);
180} 180}
181 181
182template <ResultCode func(u64, u32, s32, s64)>
183void SvcWrap() {
184 FuncReturn(
185 func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3))
186 .raw);
187}
188
189template <ResultCode func(u64, u32, s32, s32)>
190void SvcWrap() {
191 FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF),
192 (s32)(PARAM(3) & 0xFFFFFFFF))
193 .raw);
194}
195
182//////////////////////////////////////////////////////////////////////////////////////////////////// 196////////////////////////////////////////////////////////////////////////////////////////////////////
183// Function wrappers that return type u32 197// Function wrappers that return type u32
184 198
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index cffa7ca83..9a9746585 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -104,7 +104,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
104 const auto proper_handle = static_cast<Handle>(thread_handle); 104 const auto proper_handle = static_cast<Handle>(thread_handle);
105 SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle); 105 SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle);
106 if (thread == nullptr) { 106 if (thread == nullptr) {
107 NGLOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); 107 LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
108 return; 108 return;
109 } 109 }
110 110
@@ -140,6 +140,11 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
140 } 140 }
141 } 141 }
142 142
143 if (thread->arb_wait_address != 0) {
144 ASSERT(thread->status == THREADSTATUS_WAIT_ARB);
145 thread->arb_wait_address = 0;
146 }
147
143 if (resume) 148 if (resume)
144 thread->ResumeFromWait(); 149 thread->ResumeFromWait();
145} 150}
@@ -179,6 +184,7 @@ void Thread::ResumeFromWait() {
179 case THREADSTATUS_WAIT_SLEEP: 184 case THREADSTATUS_WAIT_SLEEP:
180 case THREADSTATUS_WAIT_IPC: 185 case THREADSTATUS_WAIT_IPC:
181 case THREADSTATUS_WAIT_MUTEX: 186 case THREADSTATUS_WAIT_MUTEX:
187 case THREADSTATUS_WAIT_ARB:
182 break; 188 break;
183 189
184 case THREADSTATUS_READY: 190 case THREADSTATUS_READY:
@@ -284,19 +290,19 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
284 SharedPtr<Process> owner_process) { 290 SharedPtr<Process> owner_process) {
285 // Check if priority is in ranged. Lowest priority -> highest priority id. 291 // Check if priority is in ranged. Lowest priority -> highest priority id.
286 if (priority > THREADPRIO_LOWEST) { 292 if (priority > THREADPRIO_LOWEST) {
287 NGLOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 293 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
288 return ERR_OUT_OF_RANGE; 294 return ERR_OUT_OF_RANGE;
289 } 295 }
290 296
291 if (processor_id > THREADPROCESSORID_MAX) { 297 if (processor_id > THREADPROCESSORID_MAX) {
292 NGLOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); 298 LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id);
293 return ERR_OUT_OF_RANGE_KERNEL; 299 return ERR_OUT_OF_RANGE_KERNEL;
294 } 300 }
295 301
296 // TODO(yuriks): Other checks, returning 0xD9001BEA 302 // TODO(yuriks): Other checks, returning 0xD9001BEA
297 303
298 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { 304 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
299 NGLOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 305 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
300 // TODO (bunnei): Find the correct error code to use here 306 // TODO (bunnei): Find the correct error code to use here
301 return ResultCode(-1); 307 return ResultCode(-1);
302 } 308 }
@@ -337,8 +343,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
337 auto& linheap_memory = memory_region->linear_heap_memory; 343 auto& linheap_memory = memory_region->linear_heap_memory;
338 344
339 if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { 345 if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
340 NGLOG_ERROR(Kernel_SVC, 346 LOG_ERROR(Kernel_SVC,
341 "Not enough space in region to allocate a new TLS page for thread"); 347 "Not enough space in region to allocate a new TLS page for thread");
342 return ERR_OUT_OF_MEMORY; 348 return ERR_OUT_OF_MEMORY;
343 } 349 }
344 350
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 1d2da6d50..f1e759802 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -45,6 +45,7 @@ enum ThreadStatus {
45 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false 45 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
46 THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true 46 THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
47 THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc 47 THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
48 THREADSTATUS_WAIT_ARB, ///< Waiting due to a SignalToAddress/WaitForAddress svc
48 THREADSTATUS_DORMANT, ///< Created but not yet made ready 49 THREADSTATUS_DORMANT, ///< Created but not yet made ready
49 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated 50 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
50}; 51};
@@ -230,6 +231,9 @@ public:
230 VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address 231 VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
231 Handle wait_handle; ///< The handle used to wait for the mutex. 232 Handle wait_handle; ///< The handle used to wait for the mutex.
232 233
234 // If waiting for an AddressArbiter, this is the address being waited on.
235 VAddr arb_wait_address{0};
236
233 std::string name; 237 std::string name;
234 238
235 /// Handle used by guest emulated application to access this thread 239 /// Handle used by guest emulated application to access this thread
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 661356a97..0141125e4 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -78,7 +78,7 @@ void Timer::WakeupAllWaitingThreads() {
78} 78}
79 79
80void Timer::Signal(int cycles_late) { 80void Timer::Signal(int cycles_late) {
81 NGLOG_TRACE(Kernel, "Timer {} fired", GetObjectId()); 81 LOG_TRACE(Kernel, "Timer {} fired", GetObjectId());
82 82
83 signaled = true; 83 signaled = true;
84 84
@@ -98,7 +98,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
98 timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle)); 98 timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle));
99 99
100 if (timer == nullptr) { 100 if (timer == nullptr) {
101 NGLOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle); 101 LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
102 return; 102 return;
103 } 103 }
104 104
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 676e5b282..034dd490e 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -242,12 +242,12 @@ void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
242void VMManager::LogLayout() const { 242void VMManager::LogLayout() const {
243 for (const auto& p : vma_map) { 243 for (const auto& p : vma_map) {
244 const VirtualMemoryArea& vma = p.second; 244 const VirtualMemoryArea& vma = p.second;
245 NGLOG_DEBUG(Kernel, "{:016X} - {:016X} size: {:016X} {}{}{} {}", vma.base, 245 LOG_DEBUG(Kernel, "{:016X} - {:016X} size: {:016X} {}{}{} {}", vma.base,
246 vma.base + vma.size, vma.size, 246 vma.base + vma.size, vma.size,
247 (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', 247 (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
248 (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', 248 (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
249 (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', 249 (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-',
250 GetMemoryStateName(vma.meminfo_state)); 250 GetMemoryStateName(vma.meminfo_state));
251 } 251 }
252} 252}
253 253
@@ -392,22 +392,22 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
392} 392}
393 393
394u64 VMManager::GetTotalMemoryUsage() { 394u64 VMManager::GetTotalMemoryUsage() {
395 NGLOG_WARNING(Kernel, "(STUBBED) called"); 395 LOG_WARNING(Kernel, "(STUBBED) called");
396 return 0xF8000000; 396 return 0xF8000000;
397} 397}
398 398
399u64 VMManager::GetTotalHeapUsage() { 399u64 VMManager::GetTotalHeapUsage() {
400 NGLOG_WARNING(Kernel, "(STUBBED) called"); 400 LOG_WARNING(Kernel, "(STUBBED) called");
401 return 0x0; 401 return 0x0;
402} 402}
403 403
404VAddr VMManager::GetAddressSpaceBaseAddr() { 404VAddr VMManager::GetAddressSpaceBaseAddr() {
405 NGLOG_WARNING(Kernel, "(STUBBED) called"); 405 LOG_WARNING(Kernel, "(STUBBED) called");
406 return 0x8000000; 406 return 0x8000000;
407} 407}
408 408
409u64 VMManager::GetAddressSpaceSize() { 409u64 VMManager::GetAddressSpaceSize() {
410 NGLOG_WARNING(Kernel, "(STUBBED) called"); 410 LOG_WARNING(Kernel, "(STUBBED) called");
411 return MAX_ADDRESS; 411 return MAX_ADDRESS;
412} 412}
413 413
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index f2fffa760..6bafb2dce 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -47,7 +47,7 @@ public:
47 47
48private: 48private:
49 void GetBase(Kernel::HLERequestContext& ctx) { 49 void GetBase(Kernel::HLERequestContext& ctx) {
50 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 50 LOG_WARNING(Service_ACC, "(STUBBED) called");
51 ProfileBase profile_base{}; 51 ProfileBase profile_base{};
52 IPC::ResponseBuilder rb{ctx, 16}; 52 IPC::ResponseBuilder rb{ctx, 16};
53 rb.Push(RESULT_SUCCESS); 53 rb.Push(RESULT_SUCCESS);
@@ -72,14 +72,14 @@ public:
72 72
73private: 73private:
74 void CheckAvailability(Kernel::HLERequestContext& ctx) { 74 void CheckAvailability(Kernel::HLERequestContext& ctx) {
75 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 75 LOG_WARNING(Service_ACC, "(STUBBED) called");
76 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx, 3};
77 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
78 rb.Push(true); // TODO: Check when this is supposed to return true and when not 78 rb.Push(true); // TODO: Check when this is supposed to return true and when not
79 } 79 }
80 80
81 void GetAccountId(Kernel::HLERequestContext& ctx) { 81 void GetAccountId(Kernel::HLERequestContext& ctx) {
82 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 82 LOG_WARNING(Service_ACC, "(STUBBED) called");
83 IPC::ResponseBuilder rb{ctx, 4}; 83 IPC::ResponseBuilder rb{ctx, 4};
84 rb.Push(RESULT_SUCCESS); 84 rb.Push(RESULT_SUCCESS);
85 rb.Push<u64>(0x12345678ABCDEF); 85 rb.Push<u64>(0x12345678ABCDEF);
@@ -87,14 +87,14 @@ private:
87}; 87};
88 88
89void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { 89void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
90 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 90 LOG_WARNING(Service_ACC, "(STUBBED) called");
91 IPC::ResponseBuilder rb{ctx, 3}; 91 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(RESULT_SUCCESS); 92 rb.Push(RESULT_SUCCESS);
93 rb.Push(true); // TODO: Check when this is supposed to return true and when not 93 rb.Push(true); // TODO: Check when this is supposed to return true and when not
94} 94}
95 95
96void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { 96void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
97 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 97 LOG_WARNING(Service_ACC, "(STUBBED) called");
98 constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; 98 constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
99 ctx.WriteBuffer(user_ids.data(), user_ids.size()); 99 ctx.WriteBuffer(user_ids.data(), user_ids.size());
100 IPC::ResponseBuilder rb{ctx, 2}; 100 IPC::ResponseBuilder rb{ctx, 2};
@@ -102,7 +102,7 @@ void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
102} 102}
103 103
104void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { 104void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
105 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 105 LOG_WARNING(Service_ACC, "(STUBBED) called");
106 constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; 106 constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
107 ctx.WriteBuffer(user_ids.data(), user_ids.size()); 107 ctx.WriteBuffer(user_ids.data(), user_ids.size());
108 IPC::ResponseBuilder rb{ctx, 2}; 108 IPC::ResponseBuilder rb{ctx, 2};
@@ -113,11 +113,11 @@ void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
113 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 113 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
114 rb.Push(RESULT_SUCCESS); 114 rb.Push(RESULT_SUCCESS);
115 rb.PushIpcInterface<IProfile>(); 115 rb.PushIpcInterface<IProfile>();
116 NGLOG_DEBUG(Service_ACC, "called"); 116 LOG_DEBUG(Service_ACC, "called");
117} 117}
118 118
119void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { 119void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
120 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 120 LOG_WARNING(Service_ACC, "(STUBBED) called");
121 IPC::ResponseBuilder rb{ctx, 2}; 121 IPC::ResponseBuilder rb{ctx, 2};
122 rb.Push(RESULT_SUCCESS); 122 rb.Push(RESULT_SUCCESS);
123} 123}
@@ -126,11 +126,11 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
126 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 126 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
127 rb.Push(RESULT_SUCCESS); 127 rb.Push(RESULT_SUCCESS);
128 rb.PushIpcInterface<IManagerForApplication>(); 128 rb.PushIpcInterface<IManagerForApplication>();
129 NGLOG_DEBUG(Service_ACC, "called"); 129 LOG_DEBUG(Service_ACC, "called");
130} 130}
131 131
132void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { 132void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
133 NGLOG_WARNING(Service_ACC, "(STUBBED) called"); 133 LOG_WARNING(Service_ACC, "(STUBBED) called");
134 IPC::ResponseBuilder rb{ctx, 6}; 134 IPC::ResponseBuilder rb{ctx, 6};
135 rb.Push(RESULT_SUCCESS); 135 rb.Push(RESULT_SUCCESS);
136 rb.PushRaw(DEFAULT_USER_ID); 136 rb.PushRaw(DEFAULT_USER_ID);
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index b8d6b8d4d..a871b3eaa 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -30,14 +30,14 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
30} 30}
31 31
32void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { 32void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
33 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 33 LOG_WARNING(Service_AM, "(STUBBED) called");
34 IPC::ResponseBuilder rb{ctx, 4}; 34 IPC::ResponseBuilder rb{ctx, 4};
35 rb.Push(RESULT_SUCCESS); 35 rb.Push(RESULT_SUCCESS);
36 rb.Push<u64>(0); 36 rb.Push<u64>(0);
37} 37}
38 38
39void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { 39void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
40 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 40 LOG_WARNING(Service_AM, "(STUBBED) called");
41 IPC::ResponseBuilder rb{ctx, 2}; 41 IPC::ResponseBuilder rb{ctx, 2};
42 rb.Push(RESULT_SUCCESS); 42 rb.Push(RESULT_SUCCESS);
43} 43}
@@ -56,20 +56,20 @@ IAudioController::IAudioController() : ServiceFramework("IAudioController") {
56} 56}
57 57
58void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 58void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
59 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 59 LOG_WARNING(Service_AM, "(STUBBED) called");
60 IPC::ResponseBuilder rb{ctx, 2}; 60 IPC::ResponseBuilder rb{ctx, 2};
61 rb.Push(RESULT_SUCCESS); 61 rb.Push(RESULT_SUCCESS);
62} 62}
63 63
64void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 64void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
65 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 65 LOG_WARNING(Service_AM, "(STUBBED) called");
66 IPC::ResponseBuilder rb{ctx, 3}; 66 IPC::ResponseBuilder rb{ctx, 3};
67 rb.Push(RESULT_SUCCESS); 67 rb.Push(RESULT_SUCCESS);
68 rb.Push(volume); 68 rb.Push(volume);
69} 69}
70 70
71void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 71void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
72 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 72 LOG_WARNING(Service_AM, "(STUBBED) called");
73 IPC::ResponseBuilder rb{ctx, 3}; 73 IPC::ResponseBuilder rb{ctx, 3};
74 rb.Push(RESULT_SUCCESS); 74 rb.Push(RESULT_SUCCESS);
75 rb.Push(volume); 75 rb.Push(volume);
@@ -174,14 +174,14 @@ void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
174 IPC::ResponseBuilder rb{ctx, 2}; 174 IPC::ResponseBuilder rb{ctx, 2};
175 rb.Push(RESULT_SUCCESS); 175 rb.Push(RESULT_SUCCESS);
176 176
177 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 177 LOG_WARNING(Service_AM, "(STUBBED) called");
178} 178}
179 179
180void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { 180void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
181 IPC::ResponseBuilder rb{ctx, 2}; 181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(RESULT_SUCCESS); 182 rb.Push(RESULT_SUCCESS);
183 183
184 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 184 LOG_WARNING(Service_AM, "(STUBBED) called");
185} 185}
186 186
187void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { 187void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
@@ -192,14 +192,14 @@ void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestCo
192 IPC::ResponseBuilder rb{ctx, 2}; 192 IPC::ResponseBuilder rb{ctx, 2};
193 rb.Push(RESULT_SUCCESS); 193 rb.Push(RESULT_SUCCESS);
194 194
195 NGLOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); 195 LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
196} 196}
197 197
198void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { 198void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
199 IPC::ResponseBuilder rb{ctx, 2}; 199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(RESULT_SUCCESS); 200 rb.Push(RESULT_SUCCESS);
201 201
202 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 202 LOG_WARNING(Service_AM, "(STUBBED) called");
203} 203}
204 204
205void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { 205void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) {
@@ -210,7 +210,7 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
210 IPC::ResponseBuilder rb{ctx, 2}; 210 IPC::ResponseBuilder rb{ctx, 2};
211 rb.Push(RESULT_SUCCESS); 211 rb.Push(RESULT_SUCCESS);
212 212
213 NGLOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); 213 LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
214} 214}
215 215
216void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 216void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
@@ -223,21 +223,21 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext&
223 IPC::ResponseBuilder rb{ctx, 2}; 223 IPC::ResponseBuilder rb{ctx, 2};
224 rb.Push(RESULT_SUCCESS); 224 rb.Push(RESULT_SUCCESS);
225 225
226 NGLOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); 226 LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
227} 227}
228 228
229void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { 229void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
230 IPC::ResponseBuilder rb{ctx, 2}; 230 IPC::ResponseBuilder rb{ctx, 2};
231 rb.Push(RESULT_SUCCESS); 231 rb.Push(RESULT_SUCCESS);
232 232
233 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 233 LOG_WARNING(Service_AM, "(STUBBED) called");
234} 234}
235 235
236void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { 236void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
237 IPC::ResponseBuilder rb{ctx, 2}; 237 IPC::ResponseBuilder rb{ctx, 2};
238 rb.Push(RESULT_SUCCESS); 238 rb.Push(RESULT_SUCCESS);
239 239
240 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 240 LOG_WARNING(Service_AM, "(STUBBED) called");
241} 241}
242 242
243void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { 243void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
@@ -247,7 +247,7 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
247 rb.Push(RESULT_SUCCESS); 247 rb.Push(RESULT_SUCCESS);
248 rb.PushCopyObjects(launchable_event); 248 rb.PushCopyObjects(launchable_event);
249 249
250 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 250 LOG_WARNING(Service_AM, "(STUBBED) called");
251} 251}
252 252
253void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 253void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
@@ -260,14 +260,14 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
260 rb.Push(RESULT_SUCCESS); 260 rb.Push(RESULT_SUCCESS);
261 rb.Push(layer_id); 261 rb.Push(layer_id);
262 262
263 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 263 LOG_WARNING(Service_AM, "(STUBBED) called");
264} 264}
265 265
266void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) { 266void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
267 IPC::ResponseBuilder rb{ctx, 2}; 267 IPC::ResponseBuilder rb{ctx, 2};
268 rb.Push(RESULT_SUCCESS); 268 rb.Push(RESULT_SUCCESS);
269 269
270 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 270 LOG_WARNING(Service_AM, "(STUBBED) called");
271} 271}
272 272
273ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { 273ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
@@ -311,7 +311,7 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
311 rb.Push(RESULT_SUCCESS); 311 rb.Push(RESULT_SUCCESS);
312 rb.PushCopyObjects(event); 312 rb.PushCopyObjects(event);
313 313
314 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 314 LOG_WARNING(Service_AM, "(STUBBED) called");
315} 315}
316 316
317void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { 317void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
@@ -319,7 +319,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
319 rb.Push(RESULT_SUCCESS); 319 rb.Push(RESULT_SUCCESS);
320 rb.Push<u32>(15); 320 rb.Push<u32>(15);
321 321
322 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 322 LOG_WARNING(Service_AM, "(STUBBED) called");
323} 323}
324 324
325void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { 325void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
@@ -327,7 +327,7 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
327 rb.Push(RESULT_SUCCESS); 327 rb.Push(RESULT_SUCCESS);
328 rb.Push(static_cast<u8>(FocusState::InFocus)); 328 rb.Push(static_cast<u8>(FocusState::InFocus));
329 329
330 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 330 LOG_WARNING(Service_AM, "(STUBBED) called");
331} 331}
332 332
333void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 333void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
@@ -336,7 +336,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
336 rb.Push(RESULT_SUCCESS); 336 rb.Push(RESULT_SUCCESS);
337 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); 337 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
338 338
339 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 339 LOG_WARNING(Service_AM, "(STUBBED) called");
340} 340}
341 341
342void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { 342void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
@@ -346,7 +346,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
346 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked 346 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
347 : APM::PerformanceMode::Handheld)); 347 : APM::PerformanceMode::Handheld));
348 348
349 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 349 LOG_WARNING(Service_AM, "(STUBBED) called");
350} 350}
351 351
352class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 352class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
@@ -370,7 +370,7 @@ private:
370 rb.Push(RESULT_SUCCESS); 370 rb.Push(RESULT_SUCCESS);
371 rb.Push(static_cast<u64>(buffer.size())); 371 rb.Push(static_cast<u64>(buffer.size()));
372 372
373 NGLOG_DEBUG(Service_AM, "called"); 373 LOG_DEBUG(Service_AM, "called");
374 } 374 }
375 375
376 void Write(Kernel::HLERequestContext& ctx) { 376 void Write(Kernel::HLERequestContext& ctx) {
@@ -386,7 +386,7 @@ private:
386 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; 386 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
387 rb.Push(RESULT_SUCCESS); 387 rb.Push(RESULT_SUCCESS);
388 388
389 NGLOG_DEBUG(Service_AM, "called, offset={}", offset); 389 LOG_DEBUG(Service_AM, "called, offset={}", offset);
390 } 390 }
391 391
392 void Read(Kernel::HLERequestContext& ctx) { 392 void Read(Kernel::HLERequestContext& ctx) {
@@ -402,7 +402,7 @@ private:
402 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; 402 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
403 rb.Push(RESULT_SUCCESS); 403 rb.Push(RESULT_SUCCESS);
404 404
405 NGLOG_DEBUG(Service_AM, "called, offset={}", offset); 405 LOG_DEBUG(Service_AM, "called, offset={}", offset);
406 } 406 }
407}; 407};
408 408
@@ -426,7 +426,7 @@ private:
426 rb.Push(RESULT_SUCCESS); 426 rb.Push(RESULT_SUCCESS);
427 rb.PushIpcInterface<AM::IStorageAccessor>(buffer); 427 rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
428 428
429 NGLOG_DEBUG(Service_AM, "called"); 429 LOG_DEBUG(Service_AM, "called");
430 } 430 }
431}; 431};
432 432
@@ -467,21 +467,21 @@ private:
467 rb.Push(RESULT_SUCCESS); 467 rb.Push(RESULT_SUCCESS);
468 rb.PushCopyObjects(state_changed_event); 468 rb.PushCopyObjects(state_changed_event);
469 469
470 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 470 LOG_WARNING(Service_AM, "(STUBBED) called");
471 } 471 }
472 472
473 void GetResult(Kernel::HLERequestContext& ctx) { 473 void GetResult(Kernel::HLERequestContext& ctx) {
474 IPC::ResponseBuilder rb{ctx, 2}; 474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(RESULT_SUCCESS); 475 rb.Push(RESULT_SUCCESS);
476 476
477 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 477 LOG_WARNING(Service_AM, "(STUBBED) called");
478 } 478 }
479 479
480 void Start(Kernel::HLERequestContext& ctx) { 480 void Start(Kernel::HLERequestContext& ctx) {
481 IPC::ResponseBuilder rb{ctx, 2}; 481 IPC::ResponseBuilder rb{ctx, 2};
482 rb.Push(RESULT_SUCCESS); 482 rb.Push(RESULT_SUCCESS);
483 483
484 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 484 LOG_WARNING(Service_AM, "(STUBBED) called");
485 } 485 }
486 486
487 void PushInData(Kernel::HLERequestContext& ctx) { 487 void PushInData(Kernel::HLERequestContext& ctx) {
@@ -491,7 +491,7 @@ private:
491 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; 491 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
492 rb.Push(RESULT_SUCCESS); 492 rb.Push(RESULT_SUCCESS);
493 493
494 NGLOG_DEBUG(Service_AM, "called"); 494 LOG_DEBUG(Service_AM, "called");
495 } 495 }
496 496
497 void PopOutData(Kernel::HLERequestContext& ctx) { 497 void PopOutData(Kernel::HLERequestContext& ctx) {
@@ -501,7 +501,7 @@ private:
501 501
502 storage_stack.pop(); 502 storage_stack.pop();
503 503
504 NGLOG_DEBUG(Service_AM, "called"); 504 LOG_DEBUG(Service_AM, "called");
505 } 505 }
506 506
507 std::stack<std::shared_ptr<AM::IStorage>> storage_stack; 507 std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
@@ -526,7 +526,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
526 rb.Push(RESULT_SUCCESS); 526 rb.Push(RESULT_SUCCESS);
527 rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); 527 rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
528 528
529 NGLOG_DEBUG(Service_AM, "called"); 529 LOG_DEBUG(Service_AM, "called");
530} 530}
531 531
532void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { 532void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
@@ -538,7 +538,7 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
538 rb.Push(RESULT_SUCCESS); 538 rb.Push(RESULT_SUCCESS);
539 rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); 539 rb.PushIpcInterface<AM::IStorage>(std::move(buffer));
540 540
541 NGLOG_DEBUG(Service_AM, "called, size={}", size); 541 LOG_DEBUG(Service_AM, "called, size={}", size);
542} 542}
543 543
544IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 544IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
@@ -602,21 +602,21 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
602 rb.Push(RESULT_SUCCESS); 602 rb.Push(RESULT_SUCCESS);
603 rb.PushIpcInterface<AM::IStorage>(buffer); 603 rb.PushIpcInterface<AM::IStorage>(buffer);
604 604
605 NGLOG_DEBUG(Service_AM, "called"); 605 LOG_DEBUG(Service_AM, "called");
606} 606}
607 607
608void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( 608void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
609 Kernel::HLERequestContext& ctx) { 609 Kernel::HLERequestContext& ctx) {
610 IPC::ResponseBuilder rb{ctx, 2}; 610 IPC::ResponseBuilder rb{ctx, 2};
611 rb.Push(RESULT_SUCCESS); 611 rb.Push(RESULT_SUCCESS);
612 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 612 LOG_WARNING(Service_AM, "(STUBBED) called");
613} 613}
614 614
615void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { 615void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
616 IPC::RequestParser rp{ctx}; 616 IPC::RequestParser rp{ctx};
617 u128 uid = rp.PopRaw<u128>(); 617 u128 uid = rp.PopRaw<u128>();
618 618
619 NGLOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); 619 LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
620 620
621 IPC::ResponseBuilder rb{ctx, 4}; 621 IPC::ResponseBuilder rb{ctx, 4};
622 622
@@ -644,7 +644,7 @@ void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
644 IPC::ResponseBuilder rb{ctx, 2}; 644 IPC::ResponseBuilder rb{ctx, 2};
645 rb.Push(RESULT_SUCCESS); 645 rb.Push(RESULT_SUCCESS);
646 646
647 NGLOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); 647 LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
648} 648}
649 649
650void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) { 650void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
@@ -652,7 +652,7 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
652 rb.Push(RESULT_SUCCESS); 652 rb.Push(RESULT_SUCCESS);
653 rb.Push<u64>(1); 653 rb.Push<u64>(1);
654 rb.Push<u64>(0); 654 rb.Push<u64>(0);
655 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 655 LOG_WARNING(Service_AM, "(STUBBED) called");
656} 656}
657 657
658void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { 658void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
@@ -660,20 +660,20 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
660 IPC::ResponseBuilder rb{ctx, 4}; 660 IPC::ResponseBuilder rb{ctx, 4};
661 rb.Push(RESULT_SUCCESS); 661 rb.Push(RESULT_SUCCESS);
662 rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US)); 662 rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US));
663 NGLOG_DEBUG(Service_AM, "called"); 663 LOG_DEBUG(Service_AM, "called");
664} 664}
665 665
666void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { 666void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
667 IPC::ResponseBuilder rb{ctx, 2}; 667 IPC::ResponseBuilder rb{ctx, 2};
668 rb.Push(RESULT_SUCCESS); 668 rb.Push(RESULT_SUCCESS);
669 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 669 LOG_WARNING(Service_AM, "(STUBBED) called");
670} 670}
671 671
672void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) { 672void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) {
673 IPC::ResponseBuilder rb{ctx, 2}; 673 IPC::ResponseBuilder rb{ctx, 2};
674 rb.Push(RESULT_SUCCESS); 674 rb.Push(RESULT_SUCCESS);
675 675
676 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 676 LOG_WARNING(Service_AM, "(STUBBED) called");
677} 677}
678 678
679void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { 679void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) {
@@ -681,7 +681,7 @@ void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) {
681 rb.Push(RESULT_SUCCESS); 681 rb.Push(RESULT_SUCCESS);
682 rb.Push<u8>(0); // Unknown, seems to be ignored by official processes 682 rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
683 683
684 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 684 LOG_WARNING(Service_AM, "(STUBBED) called");
685} 685}
686 686
687void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { 687void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
@@ -692,7 +692,7 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
692 rb.Push<u64>(0); 692 rb.Push<u64>(0);
693 rb.Push<u64>(0); 693 rb.Push<u64>(0);
694 694
695 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 695 LOG_WARNING(Service_AM, "(STUBBED) called");
696} 696}
697 697
698void InstallInterfaces(SM::ServiceManager& service_manager, 698void InstallInterfaces(SM::ServiceManager& service_manager,
@@ -717,7 +717,7 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
717void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) { 717void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) {
718 IPC::ResponseBuilder rb{ctx, 2}; 718 IPC::ResponseBuilder rb{ctx, 2};
719 rb.Push(RESULT_SUCCESS); 719 rb.Push(RESULT_SUCCESS);
720 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 720 LOG_WARNING(Service_AM, "(STUBBED) called");
721} 721}
722 722
723IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { 723IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 7ce551de3..180057ec2 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -33,63 +33,63 @@ private:
33 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 33 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
34 rb.Push(RESULT_SUCCESS); 34 rb.Push(RESULT_SUCCESS);
35 rb.PushIpcInterface<ICommonStateGetter>(); 35 rb.PushIpcInterface<ICommonStateGetter>();
36 NGLOG_DEBUG(Service_AM, "called"); 36 LOG_DEBUG(Service_AM, "called");
37 } 37 }
38 38
39 void GetSelfController(Kernel::HLERequestContext& ctx) { 39 void GetSelfController(Kernel::HLERequestContext& ctx) {
40 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 40 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
41 rb.Push(RESULT_SUCCESS); 41 rb.Push(RESULT_SUCCESS);
42 rb.PushIpcInterface<ISelfController>(nvflinger); 42 rb.PushIpcInterface<ISelfController>(nvflinger);
43 NGLOG_DEBUG(Service_AM, "called"); 43 LOG_DEBUG(Service_AM, "called");
44 } 44 }
45 45
46 void GetWindowController(Kernel::HLERequestContext& ctx) { 46 void GetWindowController(Kernel::HLERequestContext& ctx) {
47 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 47 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
48 rb.Push(RESULT_SUCCESS); 48 rb.Push(RESULT_SUCCESS);
49 rb.PushIpcInterface<IWindowController>(); 49 rb.PushIpcInterface<IWindowController>();
50 NGLOG_DEBUG(Service_AM, "called"); 50 LOG_DEBUG(Service_AM, "called");
51 } 51 }
52 52
53 void GetAudioController(Kernel::HLERequestContext& ctx) { 53 void GetAudioController(Kernel::HLERequestContext& ctx) {
54 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 54 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
55 rb.Push(RESULT_SUCCESS); 55 rb.Push(RESULT_SUCCESS);
56 rb.PushIpcInterface<IAudioController>(); 56 rb.PushIpcInterface<IAudioController>();
57 NGLOG_DEBUG(Service_AM, "called"); 57 LOG_DEBUG(Service_AM, "called");
58 } 58 }
59 59
60 void GetDisplayController(Kernel::HLERequestContext& ctx) { 60 void GetDisplayController(Kernel::HLERequestContext& ctx) {
61 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 61 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
62 rb.Push(RESULT_SUCCESS); 62 rb.Push(RESULT_SUCCESS);
63 rb.PushIpcInterface<IDisplayController>(); 63 rb.PushIpcInterface<IDisplayController>();
64 NGLOG_DEBUG(Service_AM, "called"); 64 LOG_DEBUG(Service_AM, "called");
65 } 65 }
66 66
67 void GetProcessWindingController(Kernel::HLERequestContext& ctx) { 67 void GetProcessWindingController(Kernel::HLERequestContext& ctx) {
68 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 68 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
69 rb.Push(RESULT_SUCCESS); 69 rb.Push(RESULT_SUCCESS);
70 rb.PushIpcInterface<IProcessWindingController>(); 70 rb.PushIpcInterface<IProcessWindingController>();
71 NGLOG_DEBUG(Service_AM, "called"); 71 LOG_DEBUG(Service_AM, "called");
72 } 72 }
73 73
74 void GetDebugFunctions(Kernel::HLERequestContext& ctx) { 74 void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
75 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 75 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
76 rb.Push(RESULT_SUCCESS); 76 rb.Push(RESULT_SUCCESS);
77 rb.PushIpcInterface<IDebugFunctions>(); 77 rb.PushIpcInterface<IDebugFunctions>();
78 NGLOG_DEBUG(Service_AM, "called"); 78 LOG_DEBUG(Service_AM, "called");
79 } 79 }
80 80
81 void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { 81 void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
82 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 82 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
83 rb.Push(RESULT_SUCCESS); 83 rb.Push(RESULT_SUCCESS);
84 rb.PushIpcInterface<ILibraryAppletCreator>(); 84 rb.PushIpcInterface<ILibraryAppletCreator>();
85 NGLOG_DEBUG(Service_AM, "called"); 85 LOG_DEBUG(Service_AM, "called");
86 } 86 }
87 87
88 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { 88 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
89 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 89 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
90 rb.Push(RESULT_SUCCESS); 90 rb.Push(RESULT_SUCCESS);
91 rb.PushIpcInterface<IApplicationFunctions>(); 91 rb.PushIpcInterface<IApplicationFunctions>();
92 NGLOG_DEBUG(Service_AM, "called"); 92 LOG_DEBUG(Service_AM, "called");
93 } 93 }
94 94
95 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 95 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
@@ -120,70 +120,70 @@ private:
120 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 120 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
121 rb.Push(RESULT_SUCCESS); 121 rb.Push(RESULT_SUCCESS);
122 rb.PushIpcInterface<ICommonStateGetter>(); 122 rb.PushIpcInterface<ICommonStateGetter>();
123 NGLOG_DEBUG(Service_AM, "called"); 123 LOG_DEBUG(Service_AM, "called");
124 } 124 }
125 125
126 void GetSelfController(Kernel::HLERequestContext& ctx) { 126 void GetSelfController(Kernel::HLERequestContext& ctx) {
127 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 127 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
128 rb.Push(RESULT_SUCCESS); 128 rb.Push(RESULT_SUCCESS);
129 rb.PushIpcInterface<ISelfController>(nvflinger); 129 rb.PushIpcInterface<ISelfController>(nvflinger);
130 NGLOG_DEBUG(Service_AM, "called"); 130 LOG_DEBUG(Service_AM, "called");
131 } 131 }
132 132
133 void GetWindowController(Kernel::HLERequestContext& ctx) { 133 void GetWindowController(Kernel::HLERequestContext& ctx) {
134 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 134 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
135 rb.Push(RESULT_SUCCESS); 135 rb.Push(RESULT_SUCCESS);
136 rb.PushIpcInterface<IWindowController>(); 136 rb.PushIpcInterface<IWindowController>();
137 NGLOG_DEBUG(Service_AM, "called"); 137 LOG_DEBUG(Service_AM, "called");
138 } 138 }
139 139
140 void GetAudioController(Kernel::HLERequestContext& ctx) { 140 void GetAudioController(Kernel::HLERequestContext& ctx) {
141 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 141 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
142 rb.Push(RESULT_SUCCESS); 142 rb.Push(RESULT_SUCCESS);
143 rb.PushIpcInterface<IAudioController>(); 143 rb.PushIpcInterface<IAudioController>();
144 NGLOG_DEBUG(Service_AM, "called"); 144 LOG_DEBUG(Service_AM, "called");
145 } 145 }
146 146
147 void GetDisplayController(Kernel::HLERequestContext& ctx) { 147 void GetDisplayController(Kernel::HLERequestContext& ctx) {
148 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 148 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
149 rb.Push(RESULT_SUCCESS); 149 rb.Push(RESULT_SUCCESS);
150 rb.PushIpcInterface<IDisplayController>(); 150 rb.PushIpcInterface<IDisplayController>();
151 NGLOG_DEBUG(Service_AM, "called"); 151 LOG_DEBUG(Service_AM, "called");
152 } 152 }
153 153
154 void GetDebugFunctions(Kernel::HLERequestContext& ctx) { 154 void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
155 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
156 rb.Push(RESULT_SUCCESS); 156 rb.Push(RESULT_SUCCESS);
157 rb.PushIpcInterface<IDebugFunctions>(); 157 rb.PushIpcInterface<IDebugFunctions>();
158 NGLOG_DEBUG(Service_AM, "called"); 158 LOG_DEBUG(Service_AM, "called");
159 } 159 }
160 160
161 void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { 161 void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
162 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 162 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
163 rb.Push(RESULT_SUCCESS); 163 rb.Push(RESULT_SUCCESS);
164 rb.PushIpcInterface<ILibraryAppletCreator>(); 164 rb.PushIpcInterface<ILibraryAppletCreator>();
165 NGLOG_DEBUG(Service_AM, "called"); 165 LOG_DEBUG(Service_AM, "called");
166 } 166 }
167 167
168 void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) { 168 void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) {
169 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 169 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
170 rb.Push(RESULT_SUCCESS); 170 rb.Push(RESULT_SUCCESS);
171 rb.PushIpcInterface<IHomeMenuFunctions>(); 171 rb.PushIpcInterface<IHomeMenuFunctions>();
172 NGLOG_DEBUG(Service_AM, "called"); 172 LOG_DEBUG(Service_AM, "called");
173 } 173 }
174 174
175 void GetGlobalStateController(Kernel::HLERequestContext& ctx) { 175 void GetGlobalStateController(Kernel::HLERequestContext& ctx) {
176 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 176 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
177 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
178 rb.PushIpcInterface<IGlobalStateController>(); 178 rb.PushIpcInterface<IGlobalStateController>();
179 NGLOG_DEBUG(Service_AM, "called"); 179 LOG_DEBUG(Service_AM, "called");
180 } 180 }
181 181
182 void GetApplicationCreator(Kernel::HLERequestContext& ctx) { 182 void GetApplicationCreator(Kernel::HLERequestContext& ctx) {
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(RESULT_SUCCESS); 184 rb.Push(RESULT_SUCCESS);
185 rb.PushIpcInterface<IApplicationCreator>(); 185 rb.PushIpcInterface<IApplicationCreator>();
186 NGLOG_DEBUG(Service_AM, "called"); 186 LOG_DEBUG(Service_AM, "called");
187 } 187 }
188 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 188 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
189}; 189};
@@ -192,21 +192,21 @@ void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
192 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 192 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
193 rb.Push(RESULT_SUCCESS); 193 rb.Push(RESULT_SUCCESS);
194 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger); 194 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger);
195 NGLOG_DEBUG(Service_AM, "called"); 195 LOG_DEBUG(Service_AM, "called");
196} 196}
197 197
198void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { 198void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
199 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 199 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
200 rb.Push(RESULT_SUCCESS); 200 rb.Push(RESULT_SUCCESS);
201 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); 201 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
202 NGLOG_DEBUG(Service_AM, "called"); 202 LOG_DEBUG(Service_AM, "called");
203} 203}
204 204
205void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { 205void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
206 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 206 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
207 rb.Push(RESULT_SUCCESS); 207 rb.Push(RESULT_SUCCESS);
208 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); 208 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
209 NGLOG_DEBUG(Service_AM, "called"); 209 LOG_DEBUG(Service_AM, "called");
210} 210}
211 211
212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index 587a922fe..278259eda 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -33,56 +33,56 @@ private:
33 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 33 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
34 rb.Push(RESULT_SUCCESS); 34 rb.Push(RESULT_SUCCESS);
35 rb.PushIpcInterface<IAudioController>(); 35 rb.PushIpcInterface<IAudioController>();
36 NGLOG_DEBUG(Service_AM, "called"); 36 LOG_DEBUG(Service_AM, "called");
37 } 37 }
38 38
39 void GetDisplayController(Kernel::HLERequestContext& ctx) { 39 void GetDisplayController(Kernel::HLERequestContext& ctx) {
40 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 40 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
41 rb.Push(RESULT_SUCCESS); 41 rb.Push(RESULT_SUCCESS);
42 rb.PushIpcInterface<IDisplayController>(); 42 rb.PushIpcInterface<IDisplayController>();
43 NGLOG_DEBUG(Service_AM, "called"); 43 LOG_DEBUG(Service_AM, "called");
44 } 44 }
45 45
46 void GetDebugFunctions(Kernel::HLERequestContext& ctx) { 46 void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
47 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 47 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
48 rb.Push(RESULT_SUCCESS); 48 rb.Push(RESULT_SUCCESS);
49 rb.PushIpcInterface<IDebugFunctions>(); 49 rb.PushIpcInterface<IDebugFunctions>();
50 NGLOG_DEBUG(Service_AM, "called"); 50 LOG_DEBUG(Service_AM, "called");
51 } 51 }
52 52
53 void GetWindowController(Kernel::HLERequestContext& ctx) { 53 void GetWindowController(Kernel::HLERequestContext& ctx) {
54 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 54 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
55 rb.Push(RESULT_SUCCESS); 55 rb.Push(RESULT_SUCCESS);
56 rb.PushIpcInterface<IWindowController>(); 56 rb.PushIpcInterface<IWindowController>();
57 NGLOG_DEBUG(Service_AM, "called"); 57 LOG_DEBUG(Service_AM, "called");
58 } 58 }
59 59
60 void GetSelfController(Kernel::HLERequestContext& ctx) { 60 void GetSelfController(Kernel::HLERequestContext& ctx) {
61 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 61 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
62 rb.Push(RESULT_SUCCESS); 62 rb.Push(RESULT_SUCCESS);
63 rb.PushIpcInterface<ISelfController>(nvflinger); 63 rb.PushIpcInterface<ISelfController>(nvflinger);
64 NGLOG_DEBUG(Service_AM, "called"); 64 LOG_DEBUG(Service_AM, "called");
65 } 65 }
66 66
67 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { 67 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
68 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 68 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
69 rb.Push(RESULT_SUCCESS); 69 rb.Push(RESULT_SUCCESS);
70 rb.PushIpcInterface<ICommonStateGetter>(); 70 rb.PushIpcInterface<ICommonStateGetter>();
71 NGLOG_DEBUG(Service_AM, "called"); 71 LOG_DEBUG(Service_AM, "called");
72 } 72 }
73 73
74 void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { 74 void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
75 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 75 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
76 rb.Push(RESULT_SUCCESS); 76 rb.Push(RESULT_SUCCESS);
77 rb.PushIpcInterface<ILibraryAppletCreator>(); 77 rb.PushIpcInterface<ILibraryAppletCreator>();
78 NGLOG_DEBUG(Service_AM, "called"); 78 LOG_DEBUG(Service_AM, "called");
79 } 79 }
80 80
81 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { 81 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
82 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 82 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
83 rb.Push(RESULT_SUCCESS); 83 rb.Push(RESULT_SUCCESS);
84 rb.PushIpcInterface<IApplicationFunctions>(); 84 rb.PushIpcInterface<IApplicationFunctions>();
85 NGLOG_DEBUG(Service_AM, "called"); 85 LOG_DEBUG(Service_AM, "called");
86 } 86 }
87 87
88 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 88 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
@@ -92,7 +92,7 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
92 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 92 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
93 rb.Push(RESULT_SUCCESS); 93 rb.Push(RESULT_SUCCESS);
94 rb.PushIpcInterface<IApplicationProxy>(nvflinger); 94 rb.PushIpcInterface<IApplicationProxy>(nvflinger);
95 NGLOG_DEBUG(Service_AM, "called"); 95 LOG_DEBUG(Service_AM, "called");
96} 96}
97 97
98AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 98AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 5b6dfb48f..6e7438580 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -27,14 +27,14 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
27 IPC::ResponseBuilder rb{ctx, 4}; 27 IPC::ResponseBuilder rb{ctx, 4};
28 rb.Push(RESULT_SUCCESS); 28 rb.Push(RESULT_SUCCESS);
29 rb.Push<u64>(0); 29 rb.Push<u64>(0);
30 NGLOG_WARNING(Service_AOC, "(STUBBED) called"); 30 LOG_WARNING(Service_AOC, "(STUBBED) called");
31} 31}
32 32
33void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { 33void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
34 IPC::ResponseBuilder rb{ctx, 4}; 34 IPC::ResponseBuilder rb{ctx, 4};
35 rb.Push(RESULT_SUCCESS); 35 rb.Push(RESULT_SUCCESS);
36 rb.Push<u64>(0); 36 rb.Push<u64>(0);
37 NGLOG_WARNING(Service_AOC, "(STUBBED) called"); 37 LOG_WARNING(Service_AOC, "(STUBBED) called");
38} 38}
39 39
40void InstallInterfaces(SM::ServiceManager& service_manager) { 40void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index 3a03188ce..751d73f8d 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -29,8 +29,8 @@ private:
29 IPC::ResponseBuilder rb{ctx, 2}; 29 IPC::ResponseBuilder rb{ctx, 2};
30 rb.Push(RESULT_SUCCESS); 30 rb.Push(RESULT_SUCCESS);
31 31
32 NGLOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode), 32 LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode),
33 config); 33 config);
34 } 34 }
35 35
36 void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { 36 void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
@@ -42,7 +42,7 @@ private:
42 rb.Push(RESULT_SUCCESS); 42 rb.Push(RESULT_SUCCESS);
43 rb.Push<u32>(0); // Performance configuration 43 rb.Push<u32>(0); // Performance configuration
44 44
45 NGLOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); 45 LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode));
46 } 46 }
47}; 47};
48 48
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index 92f910b5f..d231e91e1 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/service/audio/audrec_u.h" 8#include "core/hle/service/audio/audrec_u.h"
9#include "core/hle/service/audio/audren_u.h" 9#include "core/hle/service/audio/audren_u.h"
10#include "core/hle/service/audio/codecctl.h" 10#include "core/hle/service/audio/codecctl.h"
11#include "core/hle/service/audio/hwopus.h"
11 12
12namespace Service::Audio { 13namespace Service::Audio {
13 14
@@ -17,6 +18,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
17 std::make_shared<AudRecU>()->InstallAsService(service_manager); 18 std::make_shared<AudRecU>()->InstallAsService(service_manager);
18 std::make_shared<AudRenU>()->InstallAsService(service_manager); 19 std::make_shared<AudRenU>()->InstallAsService(service_manager);
19 std::make_shared<CodecCtl>()->InstallAsService(service_manager); 20 std::make_shared<CodecCtl>()->InstallAsService(service_manager);
21 std::make_shared<HwOpus>()->InstallAsService(service_manager);
20} 22}
21 23
22} // namespace Service::Audio 24} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 402eaa306..1b4b649d8 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -60,14 +60,14 @@ public:
60 60
61private: 61private:
62 void GetAudioOutState(Kernel::HLERequestContext& ctx) { 62 void GetAudioOutState(Kernel::HLERequestContext& ctx) {
63 NGLOG_DEBUG(Service_Audio, "called"); 63 LOG_DEBUG(Service_Audio, "called");
64 IPC::ResponseBuilder rb{ctx, 3}; 64 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(RESULT_SUCCESS); 65 rb.Push(RESULT_SUCCESS);
66 rb.Push(static_cast<u32>(audio_out_state)); 66 rb.Push(static_cast<u32>(audio_out_state));
67 } 67 }
68 68
69 void StartAudioOut(Kernel::HLERequestContext& ctx) { 69 void StartAudioOut(Kernel::HLERequestContext& ctx) {
70 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 70 LOG_WARNING(Service_Audio, "(STUBBED) called");
71 71
72 // Start audio 72 // Start audio
73 audio_out_state = AudioState::Started; 73 audio_out_state = AudioState::Started;
@@ -77,7 +77,7 @@ private:
77 } 77 }
78 78
79 void StopAudioOut(Kernel::HLERequestContext& ctx) { 79 void StopAudioOut(Kernel::HLERequestContext& ctx) {
80 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 80 LOG_WARNING(Service_Audio, "(STUBBED) called");
81 81
82 // Stop audio 82 // Stop audio
83 audio_out_state = AudioState::Stopped; 83 audio_out_state = AudioState::Stopped;
@@ -89,7 +89,7 @@ private:
89 } 89 }
90 90
91 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { 91 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
92 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 92 LOG_WARNING(Service_Audio, "(STUBBED) called");
93 93
94 IPC::ResponseBuilder rb{ctx, 2, 1}; 94 IPC::ResponseBuilder rb{ctx, 2, 1};
95 rb.Push(RESULT_SUCCESS); 95 rb.Push(RESULT_SUCCESS);
@@ -97,7 +97,7 @@ private:
97 } 97 }
98 98
99 void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { 99 void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
100 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 100 LOG_WARNING(Service_Audio, "(STUBBED) called");
101 IPC::RequestParser rp{ctx}; 101 IPC::RequestParser rp{ctx};
102 102
103 const u64 key{rp.Pop<u64>()}; 103 const u64 key{rp.Pop<u64>()};
@@ -108,7 +108,7 @@ private:
108 } 108 }
109 109
110 void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) { 110 void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) {
111 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 111 LOG_WARNING(Service_Audio, "(STUBBED) called");
112 112
113 // TODO(st4rk): This is how libtransistor currently implements the 113 // TODO(st4rk): This is how libtransistor currently implements the
114 // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address 114 // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
@@ -164,7 +164,7 @@ private:
164}; 164};
165 165
166void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { 166void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
167 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 167 LOG_WARNING(Service_Audio, "(STUBBED) called");
168 IPC::RequestParser rp{ctx}; 168 IPC::RequestParser rp{ctx};
169 169
170 const std::string audio_interface = "AudioInterface"; 170 const std::string audio_interface = "AudioInterface";
@@ -180,7 +180,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
180} 180}
181 181
182void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { 182void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
183 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 183 LOG_WARNING(Service_Audio, "(STUBBED) called");
184 184
185 if (!audio_out_interface) { 185 if (!audio_out_interface) {
186 audio_out_interface = std::make_shared<IAudioOut>(); 186 audio_out_interface = std::make_shared<IAudioOut>();
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6e8002bc9..2da936b27 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -17,7 +17,8 @@ constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
17 17
18class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 18class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
19public: 19public:
20 IAudioRenderer() : ServiceFramework("IAudioRenderer") { 20 IAudioRenderer(AudioRendererParameter audren_params)
21 : ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
21 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
22 {0, nullptr, "GetAudioRendererSampleRate"}, 23 {0, nullptr, "GetAudioRendererSampleRate"},
23 {1, nullptr, "GetAudioRendererSampleCount"}, 24 {1, nullptr, "GetAudioRendererSampleCount"},
@@ -46,6 +47,7 @@ public:
46 47
47 // Start the audio event 48 // Start the audio event
48 CoreTiming::ScheduleEvent(audio_ticks, audio_event); 49 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
50 voice_status_list.reserve(worker_params.voice_count);
49 } 51 }
50 ~IAudioRenderer() { 52 ~IAudioRenderer() {
51 CoreTiming::UnscheduleEvent(audio_event, 0); 53 CoreTiming::UnscheduleEvent(audio_event, 0);
@@ -57,30 +59,63 @@ private:
57 } 59 }
58 60
59 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { 61 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
60 NGLOG_DEBUG(Service_Audio, "{}", ctx.Description()); 62 UpdateDataHeader config{};
61 AudioRendererResponseData response_data{}; 63 auto buf = ctx.ReadBuffer();
62 64 std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
63 response_data.section_0_size = 65 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
64 static_cast<u32>(response_data.state_entries.size() * sizeof(AudioRendererStateEntry)); 66
65 response_data.section_1_size = static_cast<u32>(response_data.section_1.size()); 67 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
66 response_data.section_2_size = static_cast<u32>(response_data.section_2.size()); 68 std::memcpy(mem_pool_info.data(),
67 response_data.section_3_size = static_cast<u32>(response_data.section_3.size()); 69 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
68 response_data.section_4_size = static_cast<u32>(response_data.section_4.size()); 70 memory_pool_count * sizeof(MemoryPoolInfo));
69 response_data.section_5_size = static_cast<u32>(response_data.section_5.size()); 71
70 response_data.total_size = sizeof(AudioRendererResponseData); 72 std::vector<VoiceInfo> voice_info(worker_params.voice_count);
71 73 std::memcpy(voice_info.data(),
72 for (unsigned i = 0; i < response_data.state_entries.size(); i++) { 74 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
73 // 4 = Busy and 5 = Ready? 75 config.memory_pools_size + config.voice_resource_size,
74 response_data.state_entries[i].state = 5; 76 worker_params.voice_count * sizeof(VoiceInfo));
77
78 UpdateDataHeader response_data{worker_params};
79
80 ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
81
82 std::vector<u8> output(response_data.total_size);
83 std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
84 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
85 for (unsigned i = 0; i < memory_pool.size(); i++) {
86 if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
87 memory_pool[i].state = MemoryPoolStates::Attached;
88 else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
89 memory_pool[i].state = MemoryPoolStates::Detached;
90 else
91 memory_pool[i].state = mem_pool_info[i].pool_state;
75 } 92 }
93 std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
94 response_data.memory_pools_size);
95
96 for (unsigned i = 0; i < voice_info.size(); i++) {
97 if (voice_info[i].is_new) {
98 voice_status_list[i].played_sample_count = 0;
99 voice_status_list[i].wave_buffer_consumed = 0;
100 } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
101 for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
102 voice_status_list[i].played_sample_count +=
103 (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
104 voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
105 2;
106 voice_status_list[i].wave_buffer_consumed++;
107 }
108 }
109 }
110 std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
111 voice_status_list.data(), response_data.voices_size);
76 112
77 ctx.WriteBuffer(&response_data, response_data.total_size); 113 ctx.WriteBuffer(output);
78 114
79 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
80
81 rb.Push(RESULT_SUCCESS); 116 rb.Push(RESULT_SUCCESS);
82 117
83 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 118 LOG_WARNING(Service_Audio, "(STUBBED) called");
84 } 119 }
85 120
86 void StartAudioRenderer(Kernel::HLERequestContext& ctx) { 121 void StartAudioRenderer(Kernel::HLERequestContext& ctx) {
@@ -88,7 +123,7 @@ private:
88 123
89 rb.Push(RESULT_SUCCESS); 124 rb.Push(RESULT_SUCCESS);
90 125
91 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 126 LOG_WARNING(Service_Audio, "(STUBBED) called");
92 } 127 }
93 128
94 void StopAudioRenderer(Kernel::HLERequestContext& ctx) { 129 void StopAudioRenderer(Kernel::HLERequestContext& ctx) {
@@ -96,7 +131,7 @@ private:
96 131
97 rb.Push(RESULT_SUCCESS); 132 rb.Push(RESULT_SUCCESS);
98 133
99 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 134 LOG_WARNING(Service_Audio, "(STUBBED) called");
100 } 135 }
101 136
102 void QuerySystemEvent(Kernel::HLERequestContext& ctx) { 137 void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
@@ -106,51 +141,132 @@ private:
106 rb.Push(RESULT_SUCCESS); 141 rb.Push(RESULT_SUCCESS);
107 rb.PushCopyObjects(system_event); 142 rb.PushCopyObjects(system_event);
108 143
109 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 144 LOG_WARNING(Service_Audio, "(STUBBED) called");
110 } 145 }
111 146
112 struct AudioRendererStateEntry { 147 enum class MemoryPoolStates : u32 { // Should be LE
113 u32_le state; 148 Invalid = 0x0,
149 Unknown = 0x1,
150 RequestDetach = 0x2,
151 Detached = 0x3,
152 RequestAttach = 0x4,
153 Attached = 0x5,
154 Released = 0x6,
155 };
156
157 enum class PlayStates : u8 {
158 Started = 0,
159 Stopped = 1,
160 };
161
162 struct MemoryPoolEntry {
163 MemoryPoolStates state;
114 u32_le unknown_4; 164 u32_le unknown_4;
115 u32_le unknown_8; 165 u32_le unknown_8;
116 u32_le unknown_c; 166 u32_le unknown_c;
117 }; 167 };
118 static_assert(sizeof(AudioRendererStateEntry) == 0x10, 168 static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
119 "AudioRendererStateEntry has wrong size"); 169
120 170 struct MemoryPoolInfo {
121 struct AudioRendererResponseData { 171 u64_le pool_address;
122 u32_le unknown_0; 172 u64_le pool_size;
123 u32_le section_5_size; 173 MemoryPoolStates pool_state;
124 u32_le section_0_size; 174 INSERT_PADDING_WORDS(3); // Unknown
125 u32_le section_1_size; 175 };
126 u32_le unknown_10; 176 static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
127 u32_le section_2_size; 177
128 u32_le unknown_18; 178 struct UpdateDataHeader {
129 u32_le section_3_size; 179 UpdateDataHeader() {}
130 u32_le section_4_size; 180
131 u32_le unknown_24; 181 UpdateDataHeader(const AudioRendererParameter& config) {
132 u32_le unknown_28; 182 revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
133 u32_le unknown_2c; 183 behavior_size = 0xb0;
134 u32_le unknown_30; 184 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
135 u32_le unknown_34; 185 voices_size = config.voice_count * 0x10;
136 u32_le unknown_38; 186 effects_size = config.effect_count * 0x10;
187 sinks_size = config.sink_count * 0x20;
188 performance_manager_size = 0x10;
189 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
190 voices_size + effects_size + sinks_size + performance_manager_size;
191 }
192
193 u32_le revision;
194 u32_le behavior_size;
195 u32_le memory_pools_size;
196 u32_le voices_size;
197 u32_le voice_resource_size;
198 u32_le effects_size;
199 u32_le mixes_size;
200 u32_le sinks_size;
201 u32_le performance_manager_size;
202 INSERT_PADDING_WORDS(6);
137 u32_le total_size; 203 u32_le total_size;
204 };
205 static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
138 206
139 std::array<AudioRendererStateEntry, 0x18e> state_entries; 207 struct BiquadFilter {
208 u8 enable;
209 INSERT_PADDING_BYTES(1);
210 s16_le numerator[3];
211 s16_le denominator[2];
212 };
213 static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
214
215 struct WaveBuffer {
216 u64_le buffer_addr;
217 u64_le buffer_sz;
218 s32_le start_sample_offset;
219 s32_le end_sample_offset;
220 u8 loop;
221 u8 end_of_stream;
222 u8 sent_to_server;
223 INSERT_PADDING_BYTES(5);
224 u64 context_addr;
225 u64 context_sz;
226 INSERT_PADDING_BYTES(8);
227 };
228 static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
229
230 struct VoiceInfo {
231 u32_le id;
232 u32_le node_id;
233 u8 is_new;
234 u8 is_in_use;
235 u8 play_state;
236 u8 sample_format;
237 u32_le sample_rate;
238 u32_le priority;
239 u32_le sorting_order;
240 u32_le channel_count;
241 float_le pitch;
242 float_le volume;
243 BiquadFilter biquad_filter[2];
244 u32_le wave_buffer_count;
245 u16_le wave_buffer_head;
246 INSERT_PADDING_BYTES(6);
247 u64_le additional_params_addr;
248 u64_le additional_params_sz;
249 u32_le mix_id;
250 u32_le splitter_info_id;
251 WaveBuffer wave_buffer[4];
252 u32_le voice_channel_resource_ids[6];
253 INSERT_PADDING_BYTES(24);
254 };
255 static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
140 256
141 std::array<u8, 0x600> section_1; 257 struct VoiceOutStatus {
142 std::array<u8, 0xe0> section_2; 258 u64_le played_sample_count;
143 std::array<u8, 0x20> section_3; 259 u32_le wave_buffer_consumed;
144 std::array<u8, 0x10> section_4; 260 INSERT_PADDING_WORDS(1);
145 std::array<u8, 0xb0> section_5;
146 }; 261 };
147 static_assert(sizeof(AudioRendererResponseData) == 0x20e0, 262 static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
148 "AudioRendererResponseData has wrong size");
149 263
150 /// This is used to trigger the audio event callback. 264 /// This is used to trigger the audio event callback.
151 CoreTiming::EventType* audio_event; 265 CoreTiming::EventType* audio_event;
152 266
153 Kernel::SharedPtr<Kernel::Event> system_event; 267 Kernel::SharedPtr<Kernel::Event> system_event;
268 AudioRendererParameter worker_params;
269 std::vector<VoiceOutStatus> voice_status_list;
154}; 270};
155 271
156class IAudioDevice final : public ServiceFramework<IAudioDevice> { 272class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -179,7 +295,7 @@ public:
179 295
180private: 296private:
181 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { 297 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
182 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 298 LOG_WARNING(Service_Audio, "(STUBBED) called");
183 IPC::RequestParser rp{ctx}; 299 IPC::RequestParser rp{ctx};
184 300
185 const std::string audio_interface = "AudioInterface"; 301 const std::string audio_interface = "AudioInterface";
@@ -191,7 +307,7 @@ private:
191 } 307 }
192 308
193 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { 309 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
194 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 310 LOG_WARNING(Service_Audio, "(STUBBED) called");
195 311
196 IPC::RequestParser rp{ctx}; 312 IPC::RequestParser rp{ctx};
197 f32 volume = static_cast<f32>(rp.Pop<u32>()); 313 f32 volume = static_cast<f32>(rp.Pop<u32>());
@@ -204,7 +320,7 @@ private:
204 } 320 }
205 321
206 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { 322 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
207 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 323 LOG_WARNING(Service_Audio, "(STUBBED) called");
208 IPC::RequestParser rp{ctx}; 324 IPC::RequestParser rp{ctx};
209 325
210 const std::string audio_interface = "AudioDevice"; 326 const std::string audio_interface = "AudioDevice";
@@ -216,7 +332,7 @@ private:
216 } 332 }
217 333
218 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { 334 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
219 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 335 LOG_WARNING(Service_Audio, "(STUBBED) called");
220 336
221 buffer_event->Signal(); 337 buffer_event->Signal();
222 338
@@ -226,7 +342,7 @@ private:
226 } 342 }
227 343
228 void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { 344 void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
229 NGLOG_WARNING(Service_Audio, "(STUBBED) called"); 345 LOG_WARNING(Service_Audio, "(STUBBED) called");
230 IPC::ResponseBuilder rb{ctx, 3}; 346 IPC::ResponseBuilder rb{ctx, 3};
231 rb.Push(RESULT_SUCCESS); 347 rb.Push(RESULT_SUCCESS);
232 rb.Push<u32>(1); 348 rb.Push<u32>(1);
@@ -248,31 +364,33 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
248} 364}
249 365
250void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { 366void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
367 IPC::RequestParser rp{ctx};
368 auto params = rp.PopRaw<AudioRendererParameter>();
251 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 369 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
252 370
253 rb.Push(RESULT_SUCCESS); 371 rb.Push(RESULT_SUCCESS);
254 rb.PushIpcInterface<Audio::IAudioRenderer>(); 372 rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params));
255 373
256 NGLOG_DEBUG(Service_Audio, "called"); 374 LOG_DEBUG(Service_Audio, "called");
257} 375}
258 376
259void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { 377void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
260 IPC::RequestParser rp{ctx}; 378 IPC::RequestParser rp{ctx};
261 auto params = rp.PopRaw<WorkerBufferParameters>(); 379 auto params = rp.PopRaw<AudioRendererParameter>();
262 380
263 u64 buffer_sz = Common::AlignUp(4 * params.unknown8, 0x40); 381 u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
264 buffer_sz += params.unknownC * 1024; 382 buffer_sz += params.unknown_c * 1024;
265 buffer_sz += 0x940 * (params.unknownC + 1); 383 buffer_sz += 0x940 * (params.unknown_c + 1);
266 buffer_sz += 0x3F0 * params.voice_count; 384 buffer_sz += 0x3F0 * params.voice_count;
267 buffer_sz += Common::AlignUp(8 * (params.unknownC + 1), 0x10); 385 buffer_sz += Common::AlignUp(8 * (params.unknown_c + 1), 0x10);
268 buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); 386 buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
269 buffer_sz += 387 buffer_sz +=
270 Common::AlignUp((0x3C0 * (params.sink_count + params.unknownC) + 4 * params.sample_count) * 388 Common::AlignUp((0x3C0 * (params.sink_count + params.unknown_c) + 4 * params.sample_count) *
271 (params.unknown8 + 6), 389 (params.unknown_8 + 6),
272 0x40); 390 0x40);
273 391
274 if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) { 392 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
275 u32 count = params.unknownC + 1; 393 u32 count = params.unknown_c + 1;
276 u64 node_count = Common::AlignUp(count, 0x40); 394 u64 node_count = Common::AlignUp(count, 0x40);
277 u64 node_state_buffer_sz = 395 u64 node_state_buffer_sz =
278 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8); 396 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
@@ -287,20 +405,20 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
287 } 405 }
288 406
289 buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50; 407 buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
290 if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) { 408 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
291 buffer_sz += 0xE0 * params.unknown2c; 409 buffer_sz += 0xE0 * params.unknown_2c;
292 buffer_sz += 0x20 * params.splitter_count; 410 buffer_sz += 0x20 * params.splitter_count;
293 buffer_sz += Common::AlignUp(4 * params.unknown2c, 0x10); 411 buffer_sz += Common::AlignUp(4 * params.unknown_2c, 0x10);
294 } 412 }
295 buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count; 413 buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
296 u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count + 414 u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
297 ((params.voice_count * 256) | 0x40); 415 ((params.voice_count * 256) | 0x40);
298 416
299 if (params.unknown1c >= 1) { 417 if (params.unknown_1c >= 1) {
300 output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count + 418 output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
301 16 * params.voice_count + 16) + 419 16 * params.voice_count + 16) +
302 0x658) * 420 0x658) *
303 (params.unknown1c + 1) + 421 (params.unknown_1c + 1) +
304 0xc0, 422 0xc0,
305 0x40) + 423 0x40) +
306 output_sz; 424 output_sz;
@@ -312,7 +430,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
312 rb.Push(RESULT_SUCCESS); 430 rb.Push(RESULT_SUCCESS);
313 rb.Push<u64>(output_sz); 431 rb.Push<u64>(output_sz);
314 432
315 NGLOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz); 433 LOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz);
316} 434}
317 435
318void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { 436void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
@@ -321,14 +439,14 @@ void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
321 rb.Push(RESULT_SUCCESS); 439 rb.Push(RESULT_SUCCESS);
322 rb.PushIpcInterface<Audio::IAudioDevice>(); 440 rb.PushIpcInterface<Audio::IAudioDevice>();
323 441
324 NGLOG_DEBUG(Service_Audio, "called"); 442 LOG_DEBUG(Service_Audio, "called");
325} 443}
326 444
327bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { 445bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
328 u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap 446 u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
329 switch (feature) { 447 switch (feature) {
330 case AudioFeatures::Splitter: 448 case AudioFeatures::Splitter:
331 return version_num >= 2; 449 return version_num >= 2u;
332 default: 450 default:
333 return false; 451 return false;
334 } 452 }
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index fe53de4ce..b9b81db4f 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -12,6 +12,24 @@ class HLERequestContext;
12 12
13namespace Service::Audio { 13namespace Service::Audio {
14 14
15struct AudioRendererParameter {
16 u32_le sample_rate;
17 u32_le sample_count;
18 u32_le unknown_8;
19 u32_le unknown_c;
20 u32_le voice_count;
21 u32_le sink_count;
22 u32_le effect_count;
23 u32_le unknown_1c;
24 u8 unknown_20;
25 INSERT_PADDING_BYTES(3);
26 u32_le splitter_count;
27 u32_le unknown_2c;
28 INSERT_PADDING_WORDS(1);
29 u32_le revision;
30};
31static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
32
15class AudRenU final : public ServiceFramework<AudRenU> { 33class AudRenU final : public ServiceFramework<AudRenU> {
16public: 34public:
17 explicit AudRenU(); 35 explicit AudRenU();
@@ -22,25 +40,6 @@ private:
22 void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); 40 void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
23 void GetAudioDevice(Kernel::HLERequestContext& ctx); 41 void GetAudioDevice(Kernel::HLERequestContext& ctx);
24 42
25 struct WorkerBufferParameters {
26 u32_le sample_rate;
27 u32_le sample_count;
28 u32_le unknown8;
29 u32_le unknownC;
30 u32_le voice_count;
31 u32_le sink_count;
32 u32_le effect_count;
33 u32_le unknown1c;
34 u8 unknown20;
35 u8 padding1[3];
36 u32_le splitter_count;
37 u32_le unknown2c;
38 u8 padding2[4];
39 u32_le magic;
40 };
41 static_assert(sizeof(WorkerBufferParameters) == 52,
42 "WorkerBufferParameters is an invalid size");
43
44 enum class AudioFeatures : u32 { 43 enum class AudioFeatures : u32 {
45 Splitter, 44 Splitter,
46 }; 45 };
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
new file mode 100644
index 000000000..844df382c
--- /dev/null
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -0,0 +1,29 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/service/audio/hwopus.h"
9
10namespace Service::Audio {
11
12void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
13 LOG_WARNING(Service_Audio, "(STUBBED) called");
14 IPC::ResponseBuilder rb{ctx, 3};
15 rb.Push(RESULT_SUCCESS);
16 rb.Push<u32>(0x4000);
17}
18
19HwOpus::HwOpus() : ServiceFramework("hwopus") {
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"},
22 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
23 {2, nullptr, "InitializeMultiStream"},
24 {3, nullptr, "GetWorkBufferSizeMultiStream"},
25 };
26 RegisterHandlers(functions);
27}
28
29} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
new file mode 100644
index 000000000..090b8c825
--- /dev/null
+++ b/src/core/hle/service/audio/hwopus.h
@@ -0,0 +1,20 @@
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/service.h"
8
9namespace Service::Audio {
10
11class HwOpus final : public ServiceFramework<HwOpus> {
12public:
13 explicit HwOpus();
14 ~HwOpus() = default;
15
16private:
17 void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
18};
19
20} // namespace Service::Audio
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 52be9db22..35e024c3d 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -36,7 +36,7 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
36 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 36 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
37 rb.Push(RESULT_SUCCESS); 37 rb.Push(RESULT_SUCCESS);
38 rb.PushIpcInterface<IBcatService>(); 38 rb.PushIpcInterface<IBcatService>();
39 NGLOG_DEBUG(Service_BCAT, "called"); 39 LOG_DEBUG(Service_BCAT, "called");
40} 40}
41 41
42Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 42Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 2d4282209..299b9474f 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -16,13 +16,13 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
16void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { 16void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
17 IPC::RequestParser rp(ctx); 17 IPC::RequestParser rp(ctx);
18 u32 error_code = rp.Pop<u32>(); 18 u32 error_code = rp.Pop<u32>();
19 NGLOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code); 19 LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code);
20 IPC::ResponseBuilder rb{ctx, 2}; 20 IPC::ResponseBuilder rb{ctx, 2};
21 rb.Push(RESULT_SUCCESS); 21 rb.Push(RESULT_SUCCESS);
22} 22}
23 23
24void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { 24void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
25 NGLOG_WARNING(Service_Fatal, "(STUBBED) called"); 25 LOG_WARNING(Service_Fatal, "(STUBBED) called");
26 IPC::ResponseBuilder rb{ctx, 2}; 26 IPC::ResponseBuilder rb{ctx, 2};
27 rb.Push(RESULT_SUCCESS); 27 rb.Push(RESULT_SUCCESS);
28} 28}
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 68d1c90a5..f58b518b6 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -25,14 +25,14 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact
25 ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); 25 ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
26 26
27 auto& filesystem = result.first->second; 27 auto& filesystem = result.first->second;
28 NGLOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", 28 LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
29 filesystem->GetName(), static_cast<u32>(type)); 29 static_cast<u32>(type));
30 return RESULT_SUCCESS; 30 return RESULT_SUCCESS;
31} 31}
32 32
33ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, 33ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
34 FileSys::Path& path) { 34 FileSys::Path& path) {
35 NGLOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); 35 LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
36 36
37 auto itr = filesystem_map.find(type); 37 auto itr = filesystem_map.find(type);
38 if (itr == filesystem_map.end()) { 38 if (itr == filesystem_map.end()) {
@@ -44,7 +44,7 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
44} 44}
45 45
46ResultCode FormatFileSystem(Type type) { 46ResultCode FormatFileSystem(Type type) {
47 NGLOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type)); 47 LOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type));
48 48
49 auto itr = filesystem_map.find(type); 49 auto itr = filesystem_map.find(type);
50 if (itr == filesystem_map.end()) { 50 if (itr == filesystem_map.end()) {
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index eb5748cf8..82efe7f7d 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -37,7 +37,7 @@ private:
37 const s64 offset = rp.Pop<s64>(); 37 const s64 offset = rp.Pop<s64>();
38 const s64 length = rp.Pop<s64>(); 38 const s64 length = rp.Pop<s64>();
39 39
40 NGLOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); 40 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
41 41
42 // Error checking 42 // Error checking
43 if (length < 0) { 43 if (length < 0) {
@@ -89,7 +89,7 @@ private:
89 const s64 offset = rp.Pop<s64>(); 89 const s64 offset = rp.Pop<s64>();
90 const s64 length = rp.Pop<s64>(); 90 const s64 length = rp.Pop<s64>();
91 91
92 NGLOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); 92 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
93 93
94 // Error checking 94 // Error checking
95 if (length < 0) { 95 if (length < 0) {
@@ -126,7 +126,7 @@ private:
126 const s64 offset = rp.Pop<s64>(); 126 const s64 offset = rp.Pop<s64>();
127 const s64 length = rp.Pop<s64>(); 127 const s64 length = rp.Pop<s64>();
128 128
129 NGLOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); 129 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
130 130
131 // Error checking 131 // Error checking
132 if (length < 0) { 132 if (length < 0) {
@@ -154,7 +154,7 @@ private:
154 } 154 }
155 155
156 void Flush(Kernel::HLERequestContext& ctx) { 156 void Flush(Kernel::HLERequestContext& ctx) {
157 NGLOG_DEBUG(Service_FS, "called"); 157 LOG_DEBUG(Service_FS, "called");
158 backend->Flush(); 158 backend->Flush();
159 159
160 IPC::ResponseBuilder rb{ctx, 2}; 160 IPC::ResponseBuilder rb{ctx, 2};
@@ -165,7 +165,7 @@ private:
165 IPC::RequestParser rp{ctx}; 165 IPC::RequestParser rp{ctx};
166 const u64 size = rp.Pop<u64>(); 166 const u64 size = rp.Pop<u64>();
167 backend->SetSize(size); 167 backend->SetSize(size);
168 NGLOG_DEBUG(Service_FS, "called, size={}", size); 168 LOG_DEBUG(Service_FS, "called, size={}", size);
169 169
170 IPC::ResponseBuilder rb{ctx, 2}; 170 IPC::ResponseBuilder rb{ctx, 2};
171 rb.Push(RESULT_SUCCESS); 171 rb.Push(RESULT_SUCCESS);
@@ -173,7 +173,7 @@ private:
173 173
174 void GetSize(Kernel::HLERequestContext& ctx) { 174 void GetSize(Kernel::HLERequestContext& ctx) {
175 const u64 size = backend->GetSize(); 175 const u64 size = backend->GetSize();
176 NGLOG_DEBUG(Service_FS, "called, size={}", size); 176 LOG_DEBUG(Service_FS, "called, size={}", size);
177 177
178 IPC::ResponseBuilder rb{ctx, 4}; 178 IPC::ResponseBuilder rb{ctx, 4};
179 rb.Push(RESULT_SUCCESS); 179 rb.Push(RESULT_SUCCESS);
@@ -199,7 +199,7 @@ private:
199 IPC::RequestParser rp{ctx}; 199 IPC::RequestParser rp{ctx};
200 const u64 unk = rp.Pop<u64>(); 200 const u64 unk = rp.Pop<u64>();
201 201
202 NGLOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk); 202 LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk);
203 203
204 // Calculate how many entries we can fit in the output buffer 204 // Calculate how many entries we can fit in the output buffer
205 u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); 205 u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
@@ -221,7 +221,7 @@ private:
221 } 221 }
222 222
223 void GetEntryCount(Kernel::HLERequestContext& ctx) { 223 void GetEntryCount(Kernel::HLERequestContext& ctx) {
224 NGLOG_DEBUG(Service_FS, "called"); 224 LOG_DEBUG(Service_FS, "called");
225 225
226 u64 count = backend->GetEntryCount(); 226 u64 count = backend->GetEntryCount();
227 227
@@ -265,7 +265,7 @@ public:
265 u64 mode = rp.Pop<u64>(); 265 u64 mode = rp.Pop<u64>();
266 u32 size = rp.Pop<u32>(); 266 u32 size = rp.Pop<u32>();
267 267
268 NGLOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); 268 LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
269 269
270 IPC::ResponseBuilder rb{ctx, 2}; 270 IPC::ResponseBuilder rb{ctx, 2};
271 rb.Push(backend->CreateFile(name, size)); 271 rb.Push(backend->CreateFile(name, size));
@@ -277,7 +277,7 @@ public:
277 auto file_buffer = ctx.ReadBuffer(); 277 auto file_buffer = ctx.ReadBuffer();
278 std::string name = Common::StringFromBuffer(file_buffer); 278 std::string name = Common::StringFromBuffer(file_buffer);
279 279
280 NGLOG_DEBUG(Service_FS, "called file {}", name); 280 LOG_DEBUG(Service_FS, "called file {}", name);
281 281
282 IPC::ResponseBuilder rb{ctx, 2}; 282 IPC::ResponseBuilder rb{ctx, 2};
283 rb.Push(backend->DeleteFile(name)); 283 rb.Push(backend->DeleteFile(name));
@@ -289,7 +289,7 @@ public:
289 auto file_buffer = ctx.ReadBuffer(); 289 auto file_buffer = ctx.ReadBuffer();
290 std::string name = Common::StringFromBuffer(file_buffer); 290 std::string name = Common::StringFromBuffer(file_buffer);
291 291
292 NGLOG_DEBUG(Service_FS, "called directory {}", name); 292 LOG_DEBUG(Service_FS, "called directory {}", name);
293 293
294 IPC::ResponseBuilder rb{ctx, 2}; 294 IPC::ResponseBuilder rb{ctx, 2};
295 rb.Push(backend->CreateDirectory(name)); 295 rb.Push(backend->CreateDirectory(name));
@@ -307,7 +307,7 @@ public:
307 Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); 307 Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
308 std::string dst_name = Common::StringFromBuffer(buffer); 308 std::string dst_name = Common::StringFromBuffer(buffer);
309 309
310 NGLOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); 310 LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
311 311
312 IPC::ResponseBuilder rb{ctx, 2}; 312 IPC::ResponseBuilder rb{ctx, 2};
313 rb.Push(backend->RenameFile(src_name, dst_name)); 313 rb.Push(backend->RenameFile(src_name, dst_name));
@@ -321,7 +321,7 @@ public:
321 321
322 auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); 322 auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
323 323
324 NGLOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); 324 LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
325 325
326 auto result = backend->OpenFile(name, mode); 326 auto result = backend->OpenFile(name, mode);
327 if (result.Failed()) { 327 if (result.Failed()) {
@@ -346,7 +346,7 @@ public:
346 // TODO(Subv): Implement this filter. 346 // TODO(Subv): Implement this filter.
347 u32 filter_flags = rp.Pop<u32>(); 347 u32 filter_flags = rp.Pop<u32>();
348 348
349 NGLOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); 349 LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
350 350
351 auto result = backend->OpenDirectory(name); 351 auto result = backend->OpenDirectory(name);
352 if (result.Failed()) { 352 if (result.Failed()) {
@@ -368,7 +368,7 @@ public:
368 auto file_buffer = ctx.ReadBuffer(); 368 auto file_buffer = ctx.ReadBuffer();
369 std::string name = Common::StringFromBuffer(file_buffer); 369 std::string name = Common::StringFromBuffer(file_buffer);
370 370
371 NGLOG_DEBUG(Service_FS, "called file {}", name); 371 LOG_DEBUG(Service_FS, "called file {}", name);
372 372
373 auto result = backend->GetEntryType(name); 373 auto result = backend->GetEntryType(name);
374 if (result.Failed()) { 374 if (result.Failed()) {
@@ -383,7 +383,7 @@ public:
383 } 383 }
384 384
385 void Commit(Kernel::HLERequestContext& ctx) { 385 void Commit(Kernel::HLERequestContext& ctx) {
386 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 386 LOG_WARNING(Service_FS, "(STUBBED) called");
387 387
388 IPC::ResponseBuilder rb{ctx, 2}; 388 IPC::ResponseBuilder rb{ctx, 2};
389 rb.Push(RESULT_SUCCESS); 389 rb.Push(RESULT_SUCCESS);
@@ -499,14 +499,14 @@ void FSP_SRV::TryLoadRomFS() {
499} 499}
500 500
501void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { 501void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
502 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 502 LOG_WARNING(Service_FS, "(STUBBED) called");
503 503
504 IPC::ResponseBuilder rb{ctx, 2}; 504 IPC::ResponseBuilder rb{ctx, 2};
505 rb.Push(RESULT_SUCCESS); 505 rb.Push(RESULT_SUCCESS);
506} 506}
507 507
508void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { 508void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
509 NGLOG_DEBUG(Service_FS, "called"); 509 LOG_DEBUG(Service_FS, "called");
510 510
511 FileSys::Path unused; 511 FileSys::Path unused;
512 auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap(); 512 auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
@@ -523,14 +523,14 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
523 auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); 523 auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
524 u128 uid = rp.PopRaw<u128>(); 524 u128 uid = rp.PopRaw<u128>();
525 525
526 NGLOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); 526 LOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
527 527
528 IPC::ResponseBuilder rb{ctx, 2}; 528 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(RESULT_SUCCESS); 529 rb.Push(RESULT_SUCCESS);
530} 530}
531 531
532void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { 532void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
533 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 533 LOG_WARNING(Service_FS, "(STUBBED) called");
534 534
535 // TODO(Subv): Read the input parameters and mount the requested savedata instead of always 535 // TODO(Subv): Read the input parameters and mount the requested savedata instead of always
536 // mounting the current process' savedata. 536 // mounting the current process' savedata.
@@ -549,7 +549,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
549} 549}
550 550
551void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 551void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
552 NGLOG_WARNING(Service_FS, "(STUBBED) called"); 552 LOG_WARNING(Service_FS, "(STUBBED) called");
553 553
554 IPC::ResponseBuilder rb{ctx, 3}; 554 IPC::ResponseBuilder rb{ctx, 3};
555 rb.Push(RESULT_SUCCESS); 555 rb.Push(RESULT_SUCCESS);
@@ -557,12 +557,12 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
557} 557}
558 558
559void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { 559void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
560 NGLOG_DEBUG(Service_FS, "called"); 560 LOG_DEBUG(Service_FS, "called");
561 561
562 TryLoadRomFS(); 562 TryLoadRomFS();
563 if (!romfs) { 563 if (!romfs) {
564 // TODO (bunnei): Find the right error code to use here 564 // TODO (bunnei): Find the right error code to use here
565 NGLOG_CRITICAL(Service_FS, "no file system interface available!"); 565 LOG_CRITICAL(Service_FS, "no file system interface available!");
566 IPC::ResponseBuilder rb{ctx, 2}; 566 IPC::ResponseBuilder rb{ctx, 2};
567 rb.Push(ResultCode(-1)); 567 rb.Push(ResultCode(-1));
568 return; 568 return;
@@ -571,7 +571,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
571 // Attempt to open a StorageBackend interface to the RomFS 571 // Attempt to open a StorageBackend interface to the RomFS
572 auto storage = romfs->OpenFile({}, {}); 572 auto storage = romfs->OpenFile({}, {});
573 if (storage.Failed()) { 573 if (storage.Failed()) {
574 NGLOG_CRITICAL(Service_FS, "no storage interface available!"); 574 LOG_CRITICAL(Service_FS, "no storage interface available!");
575 IPC::ResponseBuilder rb{ctx, 2}; 575 IPC::ResponseBuilder rb{ctx, 2};
576 rb.Push(storage.Code()); 576 rb.Push(storage.Code());
577 return; 577 return;
@@ -583,7 +583,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
583} 583}
584 584
585void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { 585void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
586 NGLOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); 586 LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess");
587 OpenDataStorageByCurrentProcess(ctx); 587 OpenDataStorageByCurrentProcess(ctx);
588} 588}
589 589
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 94d9fbf25..c98a46e05 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -13,7 +13,7 @@ namespace Service::Friend {
13void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { 13void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
14 IPC::ResponseBuilder rb{ctx, 2}; 14 IPC::ResponseBuilder rb{ctx, 2};
15 rb.Push(RESULT_SUCCESS); 15 rb.Push(RESULT_SUCCESS);
16 NGLOG_WARNING(Service_Friend, "(STUBBED) called"); 16 LOG_WARNING(Service_Friend, "(STUBBED) called");
17} 17}
18 18
19Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 19Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 00c5308ba..b0f4a384e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -53,7 +53,7 @@ private:
53 IPC::ResponseBuilder rb{ctx, 2, 1}; 53 IPC::ResponseBuilder rb{ctx, 2, 1};
54 rb.Push(RESULT_SUCCESS); 54 rb.Push(RESULT_SUCCESS);
55 rb.PushCopyObjects(shared_mem); 55 rb.PushCopyObjects(shared_mem);
56 NGLOG_DEBUG(Service_HID, "called"); 56 LOG_DEBUG(Service_HID, "called");
57 } 57 }
58 58
59 void LoadInputDevices() { 59 void LoadInputDevices() {
@@ -75,7 +75,7 @@ private:
75 75
76 // Set up controllers as neon red+blue Joy-Con attached to console 76 // Set up controllers as neon red+blue Joy-Con attached to console
77 ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header; 77 ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
78 controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair; 78 controller_header.type = ControllerType_Handheld;
79 controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent; 79 controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
80 controller_header.right_color_body = JOYCON_BODY_NEON_RED; 80 controller_header.right_color_body = JOYCON_BODY_NEON_RED;
81 controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED; 81 controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
@@ -90,19 +90,22 @@ private:
90 90
91 // HID shared memory stores the state of the past 17 samples in a circlular buffer, 91 // HID shared memory stores the state of the past 17 samples in a circlular buffer,
92 // each with a timestamp in number of samples since boot. 92 // each with a timestamp in number of samples since boot.
93 const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
94
93 layout.header.timestamp_ticks = CoreTiming::GetTicks(); 95 layout.header.timestamp_ticks = CoreTiming::GetTicks();
94 layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES; 96 layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
95 97
96 ControllerInputEntry& entry = layout.entries[layout.header.latest_entry]; 98 ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
97 entry.connection_state = ConnectionState_Connected | ConnectionState_Wired; 99 entry.timestamp = last_entry.timestamp + 1;
98 entry.timestamp++;
99 // TODO(shinyquagsire23): Is this always identical to timestamp? 100 // TODO(shinyquagsire23): Is this always identical to timestamp?
100 entry.timestamp_2++; 101 entry.timestamp_2 = entry.timestamp;
101 102
102 // TODO(shinyquagsire23): More than just handheld input 103 // TODO(shinyquagsire23): More than just handheld input
103 if (controller != Controller_Handheld) 104 if (controller != Controller_Handheld)
104 continue; 105 continue;
105 106
107 entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
108
106 // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future? 109 // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
107 // For now everything is just the default handheld layout, but split Joy-Con will 110 // For now everything is just the default handheld layout, but split Joy-Con will
108 // rotate the face buttons and directions for certain layouts. 111 // rotate the face buttons and directions for certain layouts.
@@ -262,7 +265,7 @@ private:
262 void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { 265 void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) {
263 IPC::ResponseBuilder rb{ctx, 2}; 266 IPC::ResponseBuilder rb{ctx, 2};
264 rb.Push(RESULT_SUCCESS); 267 rb.Push(RESULT_SUCCESS);
265 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 268 LOG_WARNING(Service_HID, "(STUBBED) called");
266 } 269 }
267}; 270};
268 271
@@ -394,144 +397,144 @@ private:
394 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 397 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
395 rb.Push(RESULT_SUCCESS); 398 rb.Push(RESULT_SUCCESS);
396 rb.PushIpcInterface<IAppletResource>(applet_resource); 399 rb.PushIpcInterface<IAppletResource>(applet_resource);
397 NGLOG_DEBUG(Service_HID, "called"); 400 LOG_DEBUG(Service_HID, "called");
398 } 401 }
399 402
400 void ActivateDebugPad(Kernel::HLERequestContext& ctx) { 403 void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
401 IPC::ResponseBuilder rb{ctx, 2}; 404 IPC::ResponseBuilder rb{ctx, 2};
402 rb.Push(RESULT_SUCCESS); 405 rb.Push(RESULT_SUCCESS);
403 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 406 LOG_WARNING(Service_HID, "(STUBBED) called");
404 } 407 }
405 408
406 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { 409 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
407 IPC::ResponseBuilder rb{ctx, 2}; 410 IPC::ResponseBuilder rb{ctx, 2};
408 rb.Push(RESULT_SUCCESS); 411 rb.Push(RESULT_SUCCESS);
409 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 412 LOG_WARNING(Service_HID, "(STUBBED) called");
410 } 413 }
411 414
412 void ActivateMouse(Kernel::HLERequestContext& ctx) { 415 void ActivateMouse(Kernel::HLERequestContext& ctx) {
413 IPC::ResponseBuilder rb{ctx, 2}; 416 IPC::ResponseBuilder rb{ctx, 2};
414 rb.Push(RESULT_SUCCESS); 417 rb.Push(RESULT_SUCCESS);
415 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 418 LOG_WARNING(Service_HID, "(STUBBED) called");
416 } 419 }
417 420
418 void ActivateKeyboard(Kernel::HLERequestContext& ctx) { 421 void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
419 IPC::ResponseBuilder rb{ctx, 2}; 422 IPC::ResponseBuilder rb{ctx, 2};
420 rb.Push(RESULT_SUCCESS); 423 rb.Push(RESULT_SUCCESS);
421 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 424 LOG_WARNING(Service_HID, "(STUBBED) called");
422 } 425 }
423 426
424 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 427 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
425 IPC::ResponseBuilder rb{ctx, 2}; 428 IPC::ResponseBuilder rb{ctx, 2};
426 rb.Push(RESULT_SUCCESS); 429 rb.Push(RESULT_SUCCESS);
427 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 430 LOG_WARNING(Service_HID, "(STUBBED) called");
428 } 431 }
429 432
430 void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 433 void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
431 IPC::ResponseBuilder rb{ctx, 2}; 434 IPC::ResponseBuilder rb{ctx, 2};
432 rb.Push(RESULT_SUCCESS); 435 rb.Push(RESULT_SUCCESS);
433 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 436 LOG_WARNING(Service_HID, "(STUBBED) called");
434 } 437 }
435 438
436 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 439 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
437 IPC::ResponseBuilder rb{ctx, 2}; 440 IPC::ResponseBuilder rb{ctx, 2};
438 rb.Push(RESULT_SUCCESS); 441 rb.Push(RESULT_SUCCESS);
439 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 442 LOG_WARNING(Service_HID, "(STUBBED) called");
440 } 443 }
441 444
442 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 445 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
443 IPC::ResponseBuilder rb{ctx, 3}; 446 IPC::ResponseBuilder rb{ctx, 3};
444 rb.Push(RESULT_SUCCESS); 447 rb.Push(RESULT_SUCCESS);
445 rb.Push<u32>(0); 448 rb.Push<u32>(0);
446 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 449 LOG_WARNING(Service_HID, "(STUBBED) called");
447 } 450 }
448 451
449 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 452 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
450 IPC::ResponseBuilder rb{ctx, 2}; 453 IPC::ResponseBuilder rb{ctx, 2};
451 rb.Push(RESULT_SUCCESS); 454 rb.Push(RESULT_SUCCESS);
452 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 455 LOG_WARNING(Service_HID, "(STUBBED) called");
453 } 456 }
454 457
455 void ActivateNpad(Kernel::HLERequestContext& ctx) { 458 void ActivateNpad(Kernel::HLERequestContext& ctx) {
456 IPC::ResponseBuilder rb{ctx, 2}; 459 IPC::ResponseBuilder rb{ctx, 2};
457 rb.Push(RESULT_SUCCESS); 460 rb.Push(RESULT_SUCCESS);
458 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 461 LOG_WARNING(Service_HID, "(STUBBED) called");
459 } 462 }
460 463
461 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 464 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
462 IPC::ResponseBuilder rb{ctx, 2, 1}; 465 IPC::ResponseBuilder rb{ctx, 2, 1};
463 rb.Push(RESULT_SUCCESS); 466 rb.Push(RESULT_SUCCESS);
464 rb.PushCopyObjects(event); 467 rb.PushCopyObjects(event);
465 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 468 LOG_WARNING(Service_HID, "(STUBBED) called");
466 } 469 }
467 470
468 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 471 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
469 IPC::ResponseBuilder rb{ctx, 2}; 472 IPC::ResponseBuilder rb{ctx, 2};
470 rb.Push(RESULT_SUCCESS); 473 rb.Push(RESULT_SUCCESS);
471 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 474 LOG_WARNING(Service_HID, "(STUBBED) called");
472 } 475 }
473 476
474 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 477 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
475 IPC::ResponseBuilder rb{ctx, 2}; 478 IPC::ResponseBuilder rb{ctx, 2};
476 rb.Push(RESULT_SUCCESS); 479 rb.Push(RESULT_SUCCESS);
477 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 480 LOG_WARNING(Service_HID, "(STUBBED) called");
478 } 481 }
479 482
480 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 483 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
481 IPC::ResponseBuilder rb{ctx, 3}; 484 IPC::ResponseBuilder rb{ctx, 3};
482 rb.Push(RESULT_SUCCESS); 485 rb.Push(RESULT_SUCCESS);
483 rb.Push(joy_hold_type); 486 rb.Push(joy_hold_type);
484 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 487 LOG_WARNING(Service_HID, "(STUBBED) called");
485 } 488 }
486 489
487 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 490 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
488 IPC::ResponseBuilder rb{ctx, 2}; 491 IPC::ResponseBuilder rb{ctx, 2};
489 rb.Push(RESULT_SUCCESS); 492 rb.Push(RESULT_SUCCESS);
490 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 493 LOG_WARNING(Service_HID, "(STUBBED) called");
491 } 494 }
492 495
493 void SendVibrationValue(Kernel::HLERequestContext& ctx) { 496 void SendVibrationValue(Kernel::HLERequestContext& ctx) {
494 IPC::ResponseBuilder rb{ctx, 2}; 497 IPC::ResponseBuilder rb{ctx, 2};
495 rb.Push(RESULT_SUCCESS); 498 rb.Push(RESULT_SUCCESS);
496 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 499 LOG_WARNING(Service_HID, "(STUBBED) called");
497 } 500 }
498 501
499 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 502 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
500 IPC::ResponseBuilder rb{ctx, 2}; 503 IPC::ResponseBuilder rb{ctx, 2};
501 rb.Push(RESULT_SUCCESS); 504 rb.Push(RESULT_SUCCESS);
502 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 505 LOG_WARNING(Service_HID, "(STUBBED) called");
503 } 506 }
504 507
505 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 508 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
506 IPC::ResponseBuilder rb{ctx, 2}; 509 IPC::ResponseBuilder rb{ctx, 2};
507 rb.Push(RESULT_SUCCESS); 510 rb.Push(RESULT_SUCCESS);
508 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 511 LOG_WARNING(Service_HID, "(STUBBED) called");
509 } 512 }
510 513
511 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { 514 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
512 IPC::ResponseBuilder rb{ctx, 2}; 515 IPC::ResponseBuilder rb{ctx, 2};
513 rb.Push(RESULT_SUCCESS); 516 rb.Push(RESULT_SUCCESS);
514 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 517 LOG_WARNING(Service_HID, "(STUBBED) called");
515 } 518 }
516 519
517 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 520 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
518 IPC::ResponseBuilder rb{ctx, 4}; 521 IPC::ResponseBuilder rb{ctx, 4};
519 rb.Push(RESULT_SUCCESS); 522 rb.Push(RESULT_SUCCESS);
520 rb.Push<u64>(0); 523 rb.Push<u64>(0);
521 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 524 LOG_WARNING(Service_HID, "(STUBBED) called");
522 } 525 }
523 526
524 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { 527 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
525 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 528 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
526 rb.Push(RESULT_SUCCESS); 529 rb.Push(RESULT_SUCCESS);
527 rb.PushIpcInterface<IActiveVibrationDeviceList>(); 530 rb.PushIpcInterface<IActiveVibrationDeviceList>();
528 NGLOG_DEBUG(Service_HID, "called"); 531 LOG_DEBUG(Service_HID, "called");
529 } 532 }
530 533
531 void SendVibrationValues(Kernel::HLERequestContext& ctx) { 534 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
532 IPC::ResponseBuilder rb{ctx, 2}; 535 IPC::ResponseBuilder rb{ctx, 2};
533 rb.Push(RESULT_SUCCESS); 536 rb.Push(RESULT_SUCCESS);
534 NGLOG_WARNING(Service_HID, "(STUBBED) called"); 537 LOG_WARNING(Service_HID, "(STUBBED) called");
535 } 538 }
536}; 539};
537 540
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 15eee8f01..b499308d6 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -12,7 +12,7 @@ namespace Service::HID {
12// Begin enums and output structs 12// Begin enums and output structs
13 13
14constexpr u32 HID_NUM_ENTRIES = 17; 14constexpr u32 HID_NUM_ENTRIES = 17;
15constexpr u32 HID_NUM_LAYOUTS = 2; 15constexpr u32 HID_NUM_LAYOUTS = 7;
16constexpr s32 HID_JOYSTICK_MAX = 0x8000; 16constexpr s32 HID_JOYSTICK_MAX = 0x8000;
17constexpr s32 HID_JOYSTICK_MIN = -0x8000; 17constexpr s32 HID_JOYSTICK_MIN = -0x8000;
18 18
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 46194643e..e85a8bdb9 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -141,19 +141,19 @@ private:
141 if (header.IsTailLog()) { 141 if (header.IsTailLog()) {
142 switch (header.severity) { 142 switch (header.severity) {
143 case MessageHeader::Severity::Trace: 143 case MessageHeader::Severity::Trace:
144 NGLOG_TRACE(Debug_Emulated, "{}", log_stream.str()); 144 LOG_TRACE(Debug_Emulated, "{}", log_stream.str());
145 break; 145 break;
146 case MessageHeader::Severity::Info: 146 case MessageHeader::Severity::Info:
147 NGLOG_INFO(Debug_Emulated, "{}", log_stream.str()); 147 LOG_INFO(Debug_Emulated, "{}", log_stream.str());
148 break; 148 break;
149 case MessageHeader::Severity::Warning: 149 case MessageHeader::Severity::Warning:
150 NGLOG_WARNING(Debug_Emulated, "{}", log_stream.str()); 150 LOG_WARNING(Debug_Emulated, "{}", log_stream.str());
151 break; 151 break;
152 case MessageHeader::Severity::Error: 152 case MessageHeader::Severity::Error:
153 NGLOG_ERROR(Debug_Emulated, "{}", log_stream.str()); 153 LOG_ERROR(Debug_Emulated, "{}", log_stream.str());
154 break; 154 break;
155 case MessageHeader::Severity::Critical: 155 case MessageHeader::Severity::Critical:
156 NGLOG_CRITICAL(Debug_Emulated, "{}", log_stream.str()); 156 LOG_CRITICAL(Debug_Emulated, "{}", log_stream.str());
157 break; 157 break;
158 } 158 }
159 } 159 }
@@ -178,7 +178,7 @@ void LM::Initialize(Kernel::HLERequestContext& ctx) {
178 rb.Push(RESULT_SUCCESS); 178 rb.Push(RESULT_SUCCESS);
179 rb.PushIpcInterface<Logger>(); 179 rb.PushIpcInterface<Logger>();
180 180
181 NGLOG_DEBUG(Service_LM, "called"); 181 LOG_DEBUG(Service_LM, "called");
182} 182}
183 183
184LM::LM() : ServiceFramework("lm") { 184LM::LM() : ServiceFramework("lm") {
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index b3a85b818..08f45b78a 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -14,7 +14,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
14} 14}
15 15
16void MM_U::Initialize(Kernel::HLERequestContext& ctx) { 16void MM_U::Initialize(Kernel::HLERequestContext& ctx) {
17 NGLOG_WARNING(Service_MM, "(STUBBED) called"); 17 LOG_WARNING(Service_MM, "(STUBBED) called");
18 IPC::ResponseBuilder rb{ctx, 2}; 18 IPC::ResponseBuilder rb{ctx, 2};
19 rb.Push(RESULT_SUCCESS); 19 rb.Push(RESULT_SUCCESS);
20} 20}
@@ -25,13 +25,13 @@ void MM_U::SetAndWait(Kernel::HLERequestContext& ctx) {
25 max = rp.Pop<u32>(); 25 max = rp.Pop<u32>();
26 current = min; 26 current = min;
27 27
28 NGLOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); 28 LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
29 IPC::ResponseBuilder rb{ctx, 2}; 29 IPC::ResponseBuilder rb{ctx, 2};
30 rb.Push(RESULT_SUCCESS); 30 rb.Push(RESULT_SUCCESS);
31} 31}
32 32
33void MM_U::Get(Kernel::HLERequestContext& ctx) { 33void MM_U::Get(Kernel::HLERequestContext& ctx) {
34 NGLOG_WARNING(Service_MM, "(STUBBED) called"); 34 LOG_WARNING(Service_MM, "(STUBBED) called");
35 IPC::ResponseBuilder rb{ctx, 3}; 35 IPC::ResponseBuilder rb{ctx, 3};
36 rb.Push(RESULT_SUCCESS); 36 rb.Push(RESULT_SUCCESS);
37 rb.Push(current); 37 rb.Push(current);
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 2a9f84037..56b05e9e8 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -64,7 +64,7 @@ private:
64 }; 64 };
65 65
66 void Initialize(Kernel::HLERequestContext& ctx) { 66 void Initialize(Kernel::HLERequestContext& ctx) {
67 NGLOG_WARNING(Service_NFP, "(STUBBED) called"); 67 LOG_WARNING(Service_NFP, "(STUBBED) called");
68 68
69 state = State::Initialized; 69 state = State::Initialized;
70 70
@@ -78,7 +78,7 @@ private:
78 78
79 ctx.WriteBuffer(&device_handle, sizeof(device_handle)); 79 ctx.WriteBuffer(&device_handle, sizeof(device_handle));
80 80
81 NGLOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); 81 LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size);
82 82
83 IPC::ResponseBuilder rb{ctx, 3}; 83 IPC::ResponseBuilder rb{ctx, 3};
84 rb.Push(RESULT_SUCCESS); 84 rb.Push(RESULT_SUCCESS);
@@ -88,7 +88,7 @@ private:
88 void AttachActivateEvent(Kernel::HLERequestContext& ctx) { 88 void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
89 IPC::RequestParser rp{ctx}; 89 IPC::RequestParser rp{ctx};
90 const u64 dev_handle = rp.Pop<u64>(); 90 const u64 dev_handle = rp.Pop<u64>();
91 NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 91 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
92 92
93 IPC::ResponseBuilder rb{ctx, 2, 1}; 93 IPC::ResponseBuilder rb{ctx, 2, 1};
94 rb.Push(RESULT_SUCCESS); 94 rb.Push(RESULT_SUCCESS);
@@ -98,7 +98,7 @@ private:
98 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { 98 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
99 IPC::RequestParser rp{ctx}; 99 IPC::RequestParser rp{ctx};
100 const u64 dev_handle = rp.Pop<u64>(); 100 const u64 dev_handle = rp.Pop<u64>();
101 NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 101 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
102 102
103 IPC::ResponseBuilder rb{ctx, 2, 1}; 103 IPC::ResponseBuilder rb{ctx, 2, 1};
104 rb.Push(RESULT_SUCCESS); 104 rb.Push(RESULT_SUCCESS);
@@ -106,14 +106,14 @@ private:
106 } 106 }
107 107
108 void GetState(Kernel::HLERequestContext& ctx) { 108 void GetState(Kernel::HLERequestContext& ctx) {
109 NGLOG_WARNING(Service_NFP, "(STUBBED) called"); 109 LOG_WARNING(Service_NFP, "(STUBBED) called");
110 IPC::ResponseBuilder rb{ctx, 3}; 110 IPC::ResponseBuilder rb{ctx, 3};
111 rb.Push(RESULT_SUCCESS); 111 rb.Push(RESULT_SUCCESS);
112 rb.Push<u32>(static_cast<u32>(state)); 112 rb.Push<u32>(static_cast<u32>(state));
113 } 113 }
114 114
115 void GetDeviceState(Kernel::HLERequestContext& ctx) { 115 void GetDeviceState(Kernel::HLERequestContext& ctx) {
116 NGLOG_WARNING(Service_NFP, "(STUBBED) called"); 116 LOG_WARNING(Service_NFP, "(STUBBED) called");
117 IPC::ResponseBuilder rb{ctx, 3}; 117 IPC::ResponseBuilder rb{ctx, 3};
118 rb.Push(RESULT_SUCCESS); 118 rb.Push(RESULT_SUCCESS);
119 rb.Push<u32>(static_cast<u32>(device_state)); 119 rb.Push<u32>(static_cast<u32>(device_state));
@@ -122,7 +122,7 @@ private:
122 void GetNpadId(Kernel::HLERequestContext& ctx) { 122 void GetNpadId(Kernel::HLERequestContext& ctx) {
123 IPC::RequestParser rp{ctx}; 123 IPC::RequestParser rp{ctx};
124 const u64 dev_handle = rp.Pop<u64>(); 124 const u64 dev_handle = rp.Pop<u64>();
125 NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 125 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
126 IPC::ResponseBuilder rb{ctx, 3}; 126 IPC::ResponseBuilder rb{ctx, 3};
127 rb.Push(RESULT_SUCCESS); 127 rb.Push(RESULT_SUCCESS);
128 rb.Push<u32>(npad_id); 128 rb.Push<u32>(npad_id);
@@ -131,7 +131,7 @@ private:
131 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { 131 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
132 IPC::RequestParser rp{ctx}; 132 IPC::RequestParser rp{ctx};
133 const u64 dev_handle = rp.Pop<u64>(); 133 const u64 dev_handle = rp.Pop<u64>();
134 NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 134 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
135 135
136 IPC::ResponseBuilder rb{ctx, 2, 1}; 136 IPC::ResponseBuilder rb{ctx, 2, 1};
137 rb.Push(RESULT_SUCCESS); 137 rb.Push(RESULT_SUCCESS);
@@ -148,7 +148,7 @@ private:
148}; 148};
149 149
150void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 150void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
151 NGLOG_DEBUG(Service_NFP, "called"); 151 LOG_DEBUG(Service_NFP, "called");
152 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 152 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
153 rb.Push(RESULT_SUCCESS); 153 rb.Push(RESULT_SUCCESS);
154 rb.PushIpcInterface<IUser>(); 154 rb.PushIpcInterface<IUser>();
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 62489c7fe..54a151c26 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -62,33 +62,33 @@ public:
62 62
63private: 63private:
64 void GetRequestState(Kernel::HLERequestContext& ctx) { 64 void GetRequestState(Kernel::HLERequestContext& ctx) {
65 NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); 65 LOG_WARNING(Service_NIFM, "(STUBBED) called");
66 IPC::ResponseBuilder rb{ctx, 3}; 66 IPC::ResponseBuilder rb{ctx, 3};
67 rb.Push(RESULT_SUCCESS); 67 rb.Push(RESULT_SUCCESS);
68 rb.Push<u32>(0); 68 rb.Push<u32>(0);
69 } 69 }
70 70
71 void GetResult(Kernel::HLERequestContext& ctx) { 71 void GetResult(Kernel::HLERequestContext& ctx) {
72 NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); 72 LOG_WARNING(Service_NIFM, "(STUBBED) called");
73 IPC::ResponseBuilder rb{ctx, 2}; 73 IPC::ResponseBuilder rb{ctx, 2};
74 rb.Push(RESULT_SUCCESS); 74 rb.Push(RESULT_SUCCESS);
75 } 75 }
76 76
77 void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { 77 void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
78 NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); 78 LOG_WARNING(Service_NIFM, "(STUBBED) called");
79 IPC::ResponseBuilder rb{ctx, 2, 2}; 79 IPC::ResponseBuilder rb{ctx, 2, 2};
80 rb.Push(RESULT_SUCCESS); 80 rb.Push(RESULT_SUCCESS);
81 rb.PushCopyObjects(event1, event2); 81 rb.PushCopyObjects(event1, event2);
82 } 82 }
83 83
84 void Cancel(Kernel::HLERequestContext& ctx) { 84 void Cancel(Kernel::HLERequestContext& ctx) {
85 NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); 85 LOG_WARNING(Service_NIFM, "(STUBBED) called");
86 IPC::ResponseBuilder rb{ctx, 2}; 86 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(RESULT_SUCCESS); 87 rb.Push(RESULT_SUCCESS);
88 } 88 }
89 89
90 void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) { 90 void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) {
91 NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); 91 LOG_WARNING(Service_NIFM, "(STUBBED) called");
92 IPC::ResponseBuilder rb{ctx, 2}; 92 IPC::ResponseBuilder rb{ctx, 2};
93 rb.Push(RESULT_SUCCESS); 93 rb.Push(RESULT_SUCCESS);
94 } 94 }
@@ -114,7 +114,7 @@ public:
114 114
115private: 115private:
116 void GetClientId(Kernel::HLERequestContext& ctx) { 116 void GetClientId(Kernel::HLERequestContext& ctx) {
117 NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); 117 LOG_WARNING(Service_NIFM, "(STUBBED) called");
118 IPC::ResponseBuilder rb{ctx, 4}; 118 IPC::ResponseBuilder rb{ctx, 4};
119 rb.Push(RESULT_SUCCESS); 119 rb.Push(RESULT_SUCCESS);
120 rb.Push<u64>(0); 120 rb.Push<u64>(0);
@@ -125,7 +125,7 @@ private:
125 rb.Push(RESULT_SUCCESS); 125 rb.Push(RESULT_SUCCESS);
126 rb.PushIpcInterface<IScanRequest>(); 126 rb.PushIpcInterface<IScanRequest>();
127 127
128 NGLOG_DEBUG(Service_NIFM, "called"); 128 LOG_DEBUG(Service_NIFM, "called");
129 } 129 }
130 void CreateRequest(Kernel::HLERequestContext& ctx) { 130 void CreateRequest(Kernel::HLERequestContext& ctx) {
131 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 131 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -133,10 +133,10 @@ private:
133 rb.Push(RESULT_SUCCESS); 133 rb.Push(RESULT_SUCCESS);
134 rb.PushIpcInterface<IRequest>(); 134 rb.PushIpcInterface<IRequest>();
135 135
136 NGLOG_DEBUG(Service_NIFM, "called"); 136 LOG_DEBUG(Service_NIFM, "called");
137 } 137 }
138 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { 138 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
139 NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); 139 LOG_WARNING(Service_NIFM, "(STUBBED) called");
140 IPC::ResponseBuilder rb{ctx, 2}; 140 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(RESULT_SUCCESS); 141 rb.Push(RESULT_SUCCESS);
142 } 142 }
@@ -146,7 +146,7 @@ private:
146 rb.Push(RESULT_SUCCESS); 146 rb.Push(RESULT_SUCCESS);
147 rb.PushIpcInterface<INetworkProfile>(); 147 rb.PushIpcInterface<INetworkProfile>();
148 148
149 NGLOG_DEBUG(Service_NIFM, "called"); 149 LOG_DEBUG(Service_NIFM, "called");
150 } 150 }
151}; 151};
152 152
@@ -196,14 +196,14 @@ void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx)
196 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 196 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
197 rb.Push(RESULT_SUCCESS); 197 rb.Push(RESULT_SUCCESS);
198 rb.PushIpcInterface<IGeneralService>(); 198 rb.PushIpcInterface<IGeneralService>();
199 NGLOG_DEBUG(Service_NIFM, "called"); 199 LOG_DEBUG(Service_NIFM, "called");
200} 200}
201 201
202void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) { 202void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) {
203 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 203 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
204 rb.Push(RESULT_SUCCESS); 204 rb.Push(RESULT_SUCCESS);
205 rb.PushIpcInterface<IGeneralService>(); 205 rb.PushIpcInterface<IGeneralService>();
206 NGLOG_DEBUG(Service_NIFM, "called"); 206 LOG_DEBUG(Service_NIFM, "called");
207} 207}
208 208
209Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 209Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 636af9a1e..d6a12ede5 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -52,7 +52,7 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
52 ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); 52 ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE);
53 file.ReadBytes(shared_font->data(), shared_font->size()); 53 file.ReadBytes(shared_font->data(), shared_font->size());
54 } else { 54 } else {
55 NGLOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); 55 LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath);
56 } 56 }
57} 57}
58 58
@@ -60,7 +60,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
60 IPC::RequestParser rp{ctx}; 60 IPC::RequestParser rp{ctx};
61 const u32 shared_font_type{rp.Pop<u32>()}; 61 const u32 shared_font_type{rp.Pop<u32>()};
62 62
63 NGLOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); 63 LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type);
64 IPC::ResponseBuilder rb{ctx, 2}; 64 IPC::ResponseBuilder rb{ctx, 2};
65 rb.Push(RESULT_SUCCESS); 65 rb.Push(RESULT_SUCCESS);
66} 66}
@@ -69,7 +69,7 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
69 IPC::RequestParser rp{ctx}; 69 IPC::RequestParser rp{ctx};
70 const u32 font_id{rp.Pop<u32>()}; 70 const u32 font_id{rp.Pop<u32>()};
71 71
72 NGLOG_DEBUG(Service_NS, "called, font_id={}", font_id); 72 LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
73 IPC::ResponseBuilder rb{ctx, 3}; 73 IPC::ResponseBuilder rb{ctx, 3};
74 rb.Push(RESULT_SUCCESS); 74 rb.Push(RESULT_SUCCESS);
75 rb.Push<u32>(static_cast<u32>(LoadState::Done)); 75 rb.Push<u32>(static_cast<u32>(LoadState::Done));
@@ -79,7 +79,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
79 IPC::RequestParser rp{ctx}; 79 IPC::RequestParser rp{ctx};
80 const u32 font_id{rp.Pop<u32>()}; 80 const u32 font_id{rp.Pop<u32>()};
81 81
82 NGLOG_DEBUG(Service_NS, "called, font_id={}", font_id); 82 LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
83 IPC::ResponseBuilder rb{ctx, 3}; 83 IPC::ResponseBuilder rb{ctx, 3};
84 rb.Push(RESULT_SUCCESS); 84 rb.Push(RESULT_SUCCESS);
85 rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size); 85 rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size);
@@ -89,7 +89,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
89 IPC::RequestParser rp{ctx}; 89 IPC::RequestParser rp{ctx};
90 const u32 font_id{rp.Pop<u32>()}; 90 const u32 font_id{rp.Pop<u32>()};
91 91
92 NGLOG_DEBUG(Service_NS, "called, font_id={}", font_id); 92 LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
93 IPC::ResponseBuilder rb{ctx, 3}; 93 IPC::ResponseBuilder rb{ctx, 3};
94 rb.Push(RESULT_SUCCESS); 94 rb.Push(RESULT_SUCCESS);
95 rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset); 95 rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset);
@@ -110,7 +110,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
110 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, 110 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
111 "PL_U:shared_font_mem"); 111 "PL_U:shared_font_mem");
112 112
113 NGLOG_DEBUG(Service_NS, "called"); 113 LOG_DEBUG(Service_NS, "called");
114 IPC::ResponseBuilder rb{ctx, 2, 1}; 114 IPC::ResponseBuilder rb{ctx, 2, 1};
115 rb.Push(RESULT_SUCCESS); 115 rb.Push(RESULT_SUCCESS);
116 rb.PushCopyObjects(shared_font_mem); 116 rb.PushCopyObjects(shared_font_mem);
@@ -119,7 +119,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
119void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { 119void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
120 IPC::RequestParser rp{ctx}; 120 IPC::RequestParser rp{ctx};
121 const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for 121 const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
122 NGLOG_DEBUG(Service_NS, "called, language_code=%lx", language_code); 122 LOG_DEBUG(Service_NS, "called, language_code=%lx", language_code);
123 IPC::ResponseBuilder rb{ctx, 4}; 123 IPC::ResponseBuilder rb{ctx, 4};
124 std::vector<u32> font_codes; 124 std::vector<u32> font_codes;
125 std::vector<u32> font_offsets; 125 std::vector<u32> font_offsets;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 103e66d0c..c39d5a164 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -20,9 +20,9 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector
20void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 20void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
21 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) { 21 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
22 VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 22 VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
23 NGLOG_WARNING(Service, 23 LOG_WARNING(Service,
24 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 24 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
25 addr, offset, width, height, stride, format); 25 addr, offset, width, height, stride, format);
26 26
27 using PixelFormat = Tegra::FramebufferConfig::PixelFormat; 27 using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
28 const Tegra::FramebufferConfig framebuffer{ 28 const Tegra::FramebufferConfig framebuffer{
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index c1eea861d..57b128b40 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -8,12 +8,14 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
10#include "core/hle/service/nvdrv/devices/nvmap.h" 10#include "core/hle/service/nvdrv/devices/nvmap.h"
11#include "video_core/renderer_base.h"
12#include "video_core/video_core.h"
11 13
12namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
13 15
14u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 16u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
15 NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
16 command.raw, input.size(), output.size()); 18 command.raw, input.size(), output.size());
17 19
18 switch (static_cast<IoctlCommand>(command.raw)) { 20 switch (static_cast<IoctlCommand>(command.raw)) {
19 case IoctlCommand::IocInitalizeExCommand: 21 case IoctlCommand::IocInitalizeExCommand:
@@ -40,15 +42,15 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vecto
40u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { 42u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
41 IoctlInitalizeEx params{}; 43 IoctlInitalizeEx params{};
42 std::memcpy(&params, input.data(), input.size()); 44 std::memcpy(&params, input.data(), input.size());
43 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); 45 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
44 return 0; 46 return 0;
45} 47}
46 48
47u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { 49u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
48 IoctlAllocSpace params{}; 50 IoctlAllocSpace params{};
49 std::memcpy(&params, input.data(), input.size()); 51 std::memcpy(&params, input.data(), input.size());
50 NGLOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, 52 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
51 params.page_size, params.flags); 53 params.page_size, params.flags);
52 54
53 auto& gpu = Core::System::GetInstance().GPU(); 55 auto& gpu = Core::System::GetInstance().GPU();
54 const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; 56 const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
@@ -65,7 +67,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
65u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 67u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
66 size_t num_entries = input.size() / sizeof(IoctlRemapEntry); 68 size_t num_entries = input.size() / sizeof(IoctlRemapEntry);
67 69
68 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); 70 LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries);
69 71
70 std::vector<IoctlRemapEntry> entries(num_entries); 72 std::vector<IoctlRemapEntry> entries(num_entries);
71 std::memcpy(entries.data(), input.data(), input.size()); 73 std::memcpy(entries.data(), input.data(), input.size());
@@ -73,8 +75,8 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
73 auto& gpu = Core::System::GetInstance().GPU(); 75 auto& gpu = Core::System::GetInstance().GPU();
74 76
75 for (const auto& entry : entries) { 77 for (const auto& entry : entries) {
76 NGLOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 78 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
77 entry.offset, entry.nvmap_handle, entry.pages); 79 entry.offset, entry.nvmap_handle, entry.pages);
78 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; 80 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
79 81
80 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 82 auto object = nvmap_dev->GetObject(entry.nvmap_handle);
@@ -96,11 +98,11 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
96 IoctlMapBufferEx params{}; 98 IoctlMapBufferEx params{};
97 std::memcpy(&params, input.data(), input.size()); 99 std::memcpy(&params, input.data(), input.size());
98 100
99 NGLOG_DEBUG(Service_NVDRV, 101 LOG_DEBUG(Service_NVDRV,
100 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" 102 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
101 ", offset={}", 103 ", offset={}",
102 params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, 104 params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
103 params.offset); 105 params.offset);
104 106
105 if (!params.nvmap_handle) { 107 if (!params.nvmap_handle) {
106 return 0; 108 return 0;
@@ -146,7 +148,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
146 IoctlUnmapBuffer params{}; 148 IoctlUnmapBuffer params{};
147 std::memcpy(&params, input.data(), input.size()); 149 std::memcpy(&params, input.data(), input.size());
148 150
149 NGLOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 151 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
150 152
151 auto& gpu = Core::System::GetInstance().GPU(); 153 auto& gpu = Core::System::GetInstance().GPU();
152 154
@@ -154,6 +156,9 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
154 156
155 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); 157 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
156 158
159 // Remove this memory region from the rasterizer cache.
160 VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size);
161
157 params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size); 162 params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
158 163
159 buffer_mappings.erase(itr->second.offset); 164 buffer_mappings.erase(itr->second.offset);
@@ -165,7 +170,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
165u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 170u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
166 IoctlBindChannel params{}; 171 IoctlBindChannel params{};
167 std::memcpy(&params, input.data(), input.size()); 172 std::memcpy(&params, input.data(), input.size());
168 NGLOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 173 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
169 channel = params.fd; 174 channel = params.fd;
170 return 0; 175 return 0;
171} 176}
@@ -173,8 +178,8 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
173u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 178u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
174 IoctlGetVaRegions params{}; 179 IoctlGetVaRegions params{};
175 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
176 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 181 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
177 params.buf_size); 182 params.buf_size);
178 183
179 params.buf_size = 0x30; 184 params.buf_size = 0x30;
180 params.regions[0].offset = 0x04000000; 185 params.regions[0].offset = 0x04000000;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 7872d1e09..671b092e1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -9,8 +9,8 @@
9namespace Service::Nvidia::Devices { 9namespace Service::Nvidia::Devices {
10 10
11u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 11u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
12 NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 12 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
13 command.raw, input.size(), output.size()); 13 command.raw, input.size(), output.size());
14 14
15 switch (static_cast<IoctlCommand>(command.raw)) { 15 switch (static_cast<IoctlCommand>(command.raw)) {
16 case IoctlCommand::IocGetConfigCommand: 16 case IoctlCommand::IocGetConfigCommand:
@@ -29,33 +29,18 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
29u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 29u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
30 IocGetConfigParams params{}; 30 IocGetConfigParams params{};
31 std::memcpy(&params, input.data(), sizeof(params)); 31 std::memcpy(&params, input.data(), sizeof(params));
32 NGLOG_DEBUG(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 32 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
33 params.param_str.data()); 33 params.param_str.data());
34 34 return 0x30006; // Returns error on production mode
35 if (!strcmp(params.domain_str.data(), "nv")) {
36 if (!strcmp(params.param_str.data(), "NV_MEMORY_PROFILER")) {
37 params.config_str[0] = '0';
38 } else if (!strcmp(params.param_str.data(), "NVN_THROUGH_OPENGL")) {
39 params.config_str[0] = '0';
40 } else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) {
41 params.config_str[0] = '0';
42 } else {
43 params.config_str[0] = '\0';
44 }
45 } else {
46 UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware
47 }
48 std::memcpy(output.data(), &params, sizeof(params));
49 return 0;
50} 35}
51 36
52u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 37u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
53 bool is_async) { 38 bool is_async) {
54 IocCtrlEventWaitParams params{}; 39 IocCtrlEventWaitParams params{};
55 std::memcpy(&params, input.data(), sizeof(params)); 40 std::memcpy(&params, input.data(), sizeof(params));
56 NGLOG_WARNING(Service_NVDRV, 41 LOG_WARNING(Service_NVDRV,
57 "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}", 42 "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}",
58 params.syncpt_id, params.threshold, params.timeout, is_async); 43 params.syncpt_id, params.threshold, params.timeout, is_async);
59 44
60 // TODO(Subv): Implement actual syncpt waiting. 45 // TODO(Subv): Implement actual syncpt waiting.
61 params.value = 0; 46 params.value = 0;
@@ -64,7 +49,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
64} 49}
65 50
66u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 51u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
67 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 52 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
68 // TODO(bunnei): Implement this. 53 // TODO(bunnei): Implement this.
69 return 0; 54 return 0;
70} 55}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 0abc0de83..44e062f50 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -10,8 +10,8 @@
10namespace Service::Nvidia::Devices { 10namespace Service::Nvidia::Devices {
11 11
12u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 12u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
13 NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 13 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
14 command.raw, input.size(), output.size()); 14 command.raw, input.size(), output.size());
15 15
16 switch (static_cast<IoctlCommand>(command.raw)) { 16 switch (static_cast<IoctlCommand>(command.raw)) {
17 case IoctlCommand::IocGetCharacteristicsCommand: 17 case IoctlCommand::IocGetCharacteristicsCommand:
@@ -36,7 +36,7 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
36} 36}
37 37
38u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { 38u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) {
39 NGLOG_DEBUG(Service_NVDRV, "called"); 39 LOG_DEBUG(Service_NVDRV, "called");
40 IoctlCharacteristics params{}; 40 IoctlCharacteristics params{};
41 std::memcpy(&params, input.data(), input.size()); 41 std::memcpy(&params, input.data(), input.size());
42 params.gc.arch = 0x120; 42 params.gc.arch = 0x120;
@@ -83,8 +83,8 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
83u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { 83u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
84 IoctlGpuGetTpcMasksArgs params{}; 84 IoctlGpuGetTpcMasksArgs params{};
85 std::memcpy(&params, input.data(), input.size()); 85 std::memcpy(&params, input.data(), input.size());
86 NGLOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size, 86 LOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size,
87 params.mask_buf_addr); 87 params.mask_buf_addr);
88 // TODO(ogniK): Confirm value on hardware 88 // TODO(ogniK): Confirm value on hardware
89 if (params.mask_buf_size) 89 if (params.mask_buf_size)
90 params.tpc_mask_size = 4 * 1; // 4 * num_gpc 90 params.tpc_mask_size = 4 * 1; // 4 * num_gpc
@@ -95,7 +95,7 @@ u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>&
95} 95}
96 96
97u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { 97u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
98 NGLOG_DEBUG(Service_NVDRV, "called"); 98 LOG_DEBUG(Service_NVDRV, "called");
99 IoctlActiveSlotMask params{}; 99 IoctlActiveSlotMask params{};
100 std::memcpy(&params, input.data(), input.size()); 100 std::memcpy(&params, input.data(), input.size());
101 params.slot = 0x07; 101 params.slot = 0x07;
@@ -105,7 +105,7 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
105} 105}
106 106
107u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { 107u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
108 NGLOG_DEBUG(Service_NVDRV, "called"); 108 LOG_DEBUG(Service_NVDRV, "called");
109 IoctlZcullGetCtxSize params{}; 109 IoctlZcullGetCtxSize params{};
110 std::memcpy(&params, input.data(), input.size()); 110 std::memcpy(&params, input.data(), input.size());
111 params.size = 0x1; 111 params.size = 0x1;
@@ -114,7 +114,7 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
114} 114}
115 115
116u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { 116u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
117 NGLOG_DEBUG(Service_NVDRV, "called"); 117 LOG_DEBUG(Service_NVDRV, "called");
118 IoctlNvgpuGpuZcullGetInfoArgs params{}; 118 IoctlNvgpuGpuZcullGetInfoArgs params{};
119 std::memcpy(&params, input.data(), input.size()); 119 std::memcpy(&params, input.data(), input.size());
120 params.width_align_pixels = 0x20; 120 params.width_align_pixels = 0x20;
@@ -132,7 +132,7 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
132} 132}
133 133
134u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { 134u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
135 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 135 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
136 IoctlZbcSetTable params{}; 136 IoctlZbcSetTable params{};
137 std::memcpy(&params, input.data(), input.size()); 137 std::memcpy(&params, input.data(), input.size());
138 // TODO(ogniK): What does this even actually do? 138 // TODO(ogniK): What does this even actually do?
@@ -141,7 +141,7 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>&
141} 141}
142 142
143u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { 143u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
144 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 144 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
145 IoctlZbcQueryTable params{}; 145 IoctlZbcQueryTable params{};
146 std::memcpy(&params, input.data(), input.size()); 146 std::memcpy(&params, input.data(), input.size());
147 // TODO : To implement properly 147 // TODO : To implement properly
@@ -150,7 +150,7 @@ u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>
150} 150}
151 151
152u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { 152u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
153 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 153 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
154 IoctlFlushL2 params{}; 154 IoctlFlushL2 params{};
155 std::memcpy(&params, input.data(), input.size()); 155 std::memcpy(&params, input.data(), input.size());
156 // TODO : To implement properly 156 // TODO : To implement properly
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 79aab87f9..8de870596 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -12,8 +12,8 @@
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 14u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
15 NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 15 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
16 command.raw, input.size(), output.size()); 16 command.raw, input.size(), output.size());
17 17
18 switch (static_cast<IoctlCommand>(command.raw)) { 18 switch (static_cast<IoctlCommand>(command.raw)) {
19 case IoctlCommand::IocSetNVMAPfdCommand: 19 case IoctlCommand::IocSetNVMAPfdCommand:
@@ -51,13 +51,13 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
51u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 51u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
52 IoctlSetNvmapFD params{}; 52 IoctlSetNvmapFD params{};
53 std::memcpy(&params, input.data(), input.size()); 53 std::memcpy(&params, input.data(), input.size());
54 NGLOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 54 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
55 nvmap_fd = params.nvmap_fd; 55 nvmap_fd = params.nvmap_fd;
56 return 0; 56 return 0;
57} 57}
58 58
59u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 59u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
60 NGLOG_DEBUG(Service_NVDRV, "called"); 60 LOG_DEBUG(Service_NVDRV, "called");
61 IoctlClientData params{}; 61 IoctlClientData params{};
62 std::memcpy(&params, input.data(), input.size()); 62 std::memcpy(&params, input.data(), input.size());
63 user_data = params.data; 63 user_data = params.data;
@@ -65,7 +65,7 @@ u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& out
65} 65}
66 66
67u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 67u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
68 NGLOG_DEBUG(Service_NVDRV, "called"); 68 LOG_DEBUG(Service_NVDRV, "called");
69 IoctlClientData params{}; 69 IoctlClientData params{};
70 std::memcpy(&params, input.data(), input.size()); 70 std::memcpy(&params, input.data(), input.size());
71 params.data = user_data; 71 params.data = user_data;
@@ -75,8 +75,8 @@ u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& out
75 75
76u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { 76u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
77 std::memcpy(&zcull_params, input.data(), input.size()); 77 std::memcpy(&zcull_params, input.data(), input.size());
78 NGLOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 78 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
79 zcull_params.mode); 79 zcull_params.mode);
80 std::memcpy(output.data(), &zcull_params, output.size()); 80 std::memcpy(output.data(), &zcull_params, output.size());
81 return 0; 81 return 0;
82} 82}
@@ -84,26 +84,26 @@ u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output)
84u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { 84u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
85 IoctlSetErrorNotifier params{}; 85 IoctlSetErrorNotifier params{};
86 std::memcpy(&params, input.data(), input.size()); 86 std::memcpy(&params, input.data(), input.size());
87 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", 87 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
88 params.offset, params.size, params.mem); 88 params.size, params.mem);
89 std::memcpy(output.data(), &params, output.size()); 89 std::memcpy(output.data(), &params, output.size());
90 return 0; 90 return 0;
91} 91}
92 92
93u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { 93u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
94 std::memcpy(&channel_priority, input.data(), input.size()); 94 std::memcpy(&channel_priority, input.data(), input.size());
95 NGLOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 95 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
96 return 0; 96 return 0;
97} 97}
98 98
99u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { 99u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
100 IoctlAllocGpfifoEx2 params{}; 100 IoctlAllocGpfifoEx2 params{};
101 std::memcpy(&params, input.data(), input.size()); 101 std::memcpy(&params, input.data(), input.size());
102 NGLOG_WARNING(Service_NVDRV, 102 LOG_WARNING(Service_NVDRV,
103 "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " 103 "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, "
104 "unk1={:X}, unk2={:X}, unk3={:X}", 104 "unk1={:X}, unk2={:X}, unk3={:X}",
105 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, 105 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
106 params.unk3); 106 params.unk3);
107 params.fence_out.id = 0; 107 params.fence_out.id = 0;
108 params.fence_out.value = 0; 108 params.fence_out.value = 0;
109 std::memcpy(output.data(), &params, output.size()); 109 std::memcpy(output.data(), &params, output.size());
@@ -113,20 +113,21 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
113u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { 113u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
114 IoctlAllocObjCtx params{}; 114 IoctlAllocObjCtx params{};
115 std::memcpy(&params, input.data(), input.size()); 115 std::memcpy(&params, input.data(), input.size());
116 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 116 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
117 params.flags); 117 params.flags);
118 params.obj_id = 0x0; 118 params.obj_id = 0x0;
119 std::memcpy(output.data(), &params, output.size()); 119 std::memcpy(output.data(), &params, output.size());
120 return 0; 120 return 0;
121} 121}
122 122
123u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 123u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
124 if (input.size() < sizeof(IoctlSubmitGpfifo)) 124 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
125 UNIMPLEMENTED(); 125 UNIMPLEMENTED();
126 }
126 IoctlSubmitGpfifo params{}; 127 IoctlSubmitGpfifo params{};
127 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 128 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
128 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 129 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
129 params.gpfifo, params.num_entries, params.flags); 130 params.gpfifo, params.num_entries, params.flags);
130 131
131 auto entries = std::vector<IoctlGpfifoEntry>(); 132 auto entries = std::vector<IoctlGpfifoEntry>();
132 entries.resize(params.num_entries); 133 entries.resize(params.num_entries);
@@ -145,7 +146,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
145u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 146u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
146 IoctlGetWaitbase params{}; 147 IoctlGetWaitbase params{};
147 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 148 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
148 NGLOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 149 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
149 params.value = 0; // Seems to be hard coded at 0 150 params.value = 0; // Seems to be hard coded at 0
150 std::memcpy(output.data(), &params, output.size()); 151 std::memcpy(output.data(), &params, output.size());
151 return 0; 152 return 0;
@@ -154,7 +155,7 @@ u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& outpu
154u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { 155u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
155 IoctlChannelSetTimeout params{}; 156 IoctlChannelSetTimeout params{};
156 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout)); 157 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
157 NGLOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 158 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
158 return 0; 159 return 0;
159} 160}
160 161
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 0b6c22898..b51c73ee8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -9,8 +9,8 @@
9namespace Service::Nvidia::Devices { 9namespace Service::Nvidia::Devices {
10 10
11u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 11u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
12 NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 12 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
13 command.raw, input.size(), output.size()); 13 command.raw, input.size(), output.size());
14 14
15 switch (static_cast<IoctlCommand>(command.raw)) { 15 switch (static_cast<IoctlCommand>(command.raw)) {
16 case IoctlCommand::IocSetNVMAPfdCommand: 16 case IoctlCommand::IocSetNVMAPfdCommand:
@@ -24,7 +24,7 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector
24u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 24u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
25 IoctlSetNvmapFD params{}; 25 IoctlSetNvmapFD params{};
26 std::memcpy(&params, input.data(), input.size()); 26 std::memcpy(&params, input.data(), input.size());
27 NGLOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 27 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
28 nvmap_fd = params.nvmap_fd; 28 nvmap_fd = params.nvmap_fd;
29 return 0; 29 return 0;
30} 30}
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 23fe98190..724eeb139 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -52,7 +52,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
52 u32 handle = next_handle++; 52 u32 handle = next_handle++;
53 handles[handle] = std::move(object); 53 handles[handle] = std::move(object);
54 54
55 NGLOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); 55 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
56 56
57 params.handle = handle; 57 params.handle = handle;
58 58
@@ -73,7 +73,7 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
73 object->addr = params.addr; 73 object->addr = params.addr;
74 object->status = Object::Status::Allocated; 74 object->status = Object::Status::Allocated;
75 75
76 NGLOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); 76 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
77 77
78 std::memcpy(output.data(), &params, sizeof(params)); 78 std::memcpy(output.data(), &params, sizeof(params));
79 return 0; 79 return 0;
@@ -83,7 +83,7 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
83 IocGetIdParams params; 83 IocGetIdParams params;
84 std::memcpy(&params, input.data(), sizeof(params)); 84 std::memcpy(&params, input.data(), sizeof(params));
85 85
86 NGLOG_WARNING(Service_NVDRV, "called"); 86 LOG_WARNING(Service_NVDRV, "called");
87 87
88 auto object = GetObject(params.handle); 88 auto object = GetObject(params.handle);
89 ASSERT(object); 89 ASSERT(object);
@@ -98,7 +98,7 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
98 IocFromIdParams params; 98 IocFromIdParams params;
99 std::memcpy(&params, input.data(), sizeof(params)); 99 std::memcpy(&params, input.data(), sizeof(params));
100 100
101 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 101 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
102 102
103 auto itr = std::find_if(handles.begin(), handles.end(), 103 auto itr = std::find_if(handles.begin(), handles.end(),
104 [&](const auto& entry) { return entry.second->id == params.id; }); 104 [&](const auto& entry) { return entry.second->id == params.id; });
@@ -119,7 +119,7 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
119 IocParamParams params; 119 IocParamParams params;
120 std::memcpy(&params, input.data(), sizeof(params)); 120 std::memcpy(&params, input.data(), sizeof(params));
121 121
122 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); 122 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
123 123
124 auto object = GetObject(params.handle); 124 auto object = GetObject(params.handle);
125 ASSERT(object); 125 ASSERT(object);
@@ -148,6 +148,7 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
148} 148}
149 149
150u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { 150u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
151 // TODO(Subv): These flags are unconfirmed.
151 enum FreeFlags { 152 enum FreeFlags {
152 Freed = 0, 153 Freed = 0,
153 NotFreedYet = 1, 154 NotFreedYet = 1,
@@ -156,20 +157,26 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
156 IocFreeParams params; 157 IocFreeParams params;
157 std::memcpy(&params, input.data(), sizeof(params)); 158 std::memcpy(&params, input.data(), sizeof(params));
158 159
159 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 160 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
160 161
161 auto itr = handles.find(params.handle); 162 auto itr = handles.find(params.handle);
162 ASSERT(itr != handles.end()); 163 ASSERT(itr != handles.end());
163 164
165 ASSERT(itr->second->refcount > 0);
166
164 itr->second->refcount--; 167 itr->second->refcount--;
165 168
166 params.refcount = itr->second->refcount;
167 params.size = itr->second->size; 169 params.size = itr->second->size;
168 170
169 if (itr->second->refcount == 0) 171 if (itr->second->refcount == 0) {
170 params.flags = Freed; 172 params.flags = Freed;
171 else 173 // The address of the nvmap is written to the output if we're finally freeing it, otherwise
174 // 0 is written.
175 params.address = itr->second->addr;
176 } else {
172 params.flags = NotFreedYet; 177 params.flags = NotFreedYet;
178 params.address = 0;
179 }
173 180
174 handles.erase(params.handle); 181 handles.erase(params.handle);
175 182
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 39fafaa7c..f2eec6409 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -94,7 +94,7 @@ private:
94 struct IocFreeParams { 94 struct IocFreeParams {
95 u32_le handle; 95 u32_le handle;
96 INSERT_PADDING_BYTES(4); 96 INSERT_PADDING_BYTES(4);
97 u64_le refcount; 97 u64_le address;
98 u32_le size; 98 u32_le size;
99 u32_le flags; 99 u32_le flags;
100 }; 100 };
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 45d2862ef..b10efd5c9 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -12,7 +12,7 @@
12namespace Service::Nvidia { 12namespace Service::Nvidia {
13 13
14void NVDRV::Open(Kernel::HLERequestContext& ctx) { 14void NVDRV::Open(Kernel::HLERequestContext& ctx) {
15 NGLOG_DEBUG(Service_NVDRV, "called"); 15 LOG_DEBUG(Service_NVDRV, "called");
16 16
17 const auto& buffer = ctx.ReadBuffer(); 17 const auto& buffer = ctx.ReadBuffer();
18 std::string device_name(buffer.begin(), buffer.end()); 18 std::string device_name(buffer.begin(), buffer.end());
@@ -25,7 +25,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
25} 25}
26 26
27void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { 27void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
28 NGLOG_DEBUG(Service_NVDRV, "called"); 28 LOG_DEBUG(Service_NVDRV, "called");
29 29
30 IPC::RequestParser rp{ctx}; 30 IPC::RequestParser rp{ctx};
31 u32 fd = rp.Pop<u32>(); 31 u32 fd = rp.Pop<u32>();
@@ -41,7 +41,7 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
41} 41}
42 42
43void NVDRV::Close(Kernel::HLERequestContext& ctx) { 43void NVDRV::Close(Kernel::HLERequestContext& ctx) {
44 NGLOG_DEBUG(Service_NVDRV, "called"); 44 LOG_DEBUG(Service_NVDRV, "called");
45 45
46 IPC::RequestParser rp{ctx}; 46 IPC::RequestParser rp{ctx};
47 u32 fd = rp.Pop<u32>(); 47 u32 fd = rp.Pop<u32>();
@@ -53,7 +53,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
53} 53}
54 54
55void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { 55void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
56 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 56 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
57 IPC::ResponseBuilder rb{ctx, 3}; 57 IPC::ResponseBuilder rb{ctx, 3};
58 rb.Push(RESULT_SUCCESS); 58 rb.Push(RESULT_SUCCESS);
59 rb.Push<u32>(0); 59 rb.Push<u32>(0);
@@ -63,7 +63,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
63 IPC::RequestParser rp{ctx}; 63 IPC::RequestParser rp{ctx};
64 u32 fd = rp.Pop<u32>(); 64 u32 fd = rp.Pop<u32>();
65 u32 event_id = rp.Pop<u32>(); 65 u32 event_id = rp.Pop<u32>();
66 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); 66 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
67 67
68 IPC::ResponseBuilder rb{ctx, 3, 1}; 68 IPC::ResponseBuilder rb{ctx, 3, 1};
69 rb.Push(RESULT_SUCCESS); 69 rb.Push(RESULT_SUCCESS);
@@ -75,14 +75,14 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
75 IPC::RequestParser rp{ctx}; 75 IPC::RequestParser rp{ctx};
76 pid = rp.Pop<u64>(); 76 pid = rp.Pop<u64>();
77 77
78 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid); 78 LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
79 IPC::ResponseBuilder rb{ctx, 3}; 79 IPC::ResponseBuilder rb{ctx, 3};
80 rb.Push(RESULT_SUCCESS); 80 rb.Push(RESULT_SUCCESS);
81 rb.Push<u32>(0); 81 rb.Push<u32>(0);
82} 82}
83 83
84void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) { 84void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
85 NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); 85 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
86 IPC::ResponseBuilder rb{ctx, 2}; 86 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(RESULT_SUCCESS); 87 rb.Push(RESULT_SUCCESS);
88} 88}
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 49e88b394..f7f2fe1b2 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -23,7 +23,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
23 buffer.igbp_buffer = igbp_buffer; 23 buffer.igbp_buffer = igbp_buffer;
24 buffer.status = Buffer::Status::Free; 24 buffer.status = Buffer::Status::Free;
25 25
26 NGLOG_WARNING(Service, "Adding graphics buffer {}", slot); 26 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
27 27
28 queue.emplace_back(buffer); 28 queue.emplace_back(buffer);
29 29
@@ -94,7 +94,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
94} 94}
95 95
96u32 BufferQueue::Query(QueryType type) { 96u32 BufferQueue::Query(QueryType type) {
97 NGLOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type)); 97 LOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type));
98 switch (type) { 98 switch (type) {
99 case QueryType::NativeWindowFormat: 99 case QueryType::NativeWindowFormat:
100 // TODO(Subv): Use an enum for this 100 // TODO(Subv): Use an enum for this
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 5c50ed601..ef3c2cc98 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -48,7 +48,7 @@ NVFlinger::~NVFlinger() {
48} 48}
49 49
50u64 NVFlinger::OpenDisplay(const std::string& name) { 50u64 NVFlinger::OpenDisplay(const std::string& name) {
51 NGLOG_WARNING(Service, "Opening display {}", name); 51 LOG_WARNING(Service, "Opening display {}", name);
52 52
53 // TODO(Subv): Currently we only support the Default display. 53 // TODO(Subv): Currently we only support the Default display.
54 ASSERT(name == "Default"); 54 ASSERT(name == "Default");
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index dd20d5ae7..fcf1f3da3 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -112,7 +112,7 @@ public:
112 112
113private: 113private:
114 void Initialize(Kernel::HLERequestContext& ctx) { 114 void Initialize(Kernel::HLERequestContext& ctx) {
115 NGLOG_WARNING(Service_PCTL, "(STUBBED) called"); 115 LOG_WARNING(Service_PCTL, "(STUBBED) called");
116 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 116 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
117 rb.Push(RESULT_SUCCESS); 117 rb.Push(RESULT_SUCCESS);
118 } 118 }
@@ -122,14 +122,14 @@ void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
122 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 122 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
123 rb.Push(RESULT_SUCCESS); 123 rb.Push(RESULT_SUCCESS);
124 rb.PushIpcInterface<IParentalControlService>(); 124 rb.PushIpcInterface<IParentalControlService>();
125 NGLOG_DEBUG(Service_PCTL, "called"); 125 LOG_DEBUG(Service_PCTL, "called");
126} 126}
127 127
128void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { 128void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) {
129 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 129 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
130 rb.Push(RESULT_SUCCESS); 130 rb.Push(RESULT_SUCCESS);
131 rb.PushIpcInterface<IParentalControlService>(); 131 rb.PushIpcInterface<IParentalControlService>();
132 NGLOG_DEBUG(Service_PCTL, "called"); 132 LOG_DEBUG(Service_PCTL, "called");
133} 133}
134 134
135Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 135Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index eaf30ee6b..3c43b8d8c 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -27,7 +27,7 @@ PlayReport::PlayReport(const char* name) : ServiceFramework(name) {
27 27
28void PlayReport::SaveReportWithUser(Kernel::HLERequestContext& ctx) { 28void PlayReport::SaveReportWithUser(Kernel::HLERequestContext& ctx) {
29 // TODO(ogniK): Do we want to add play report? 29 // TODO(ogniK): Do we want to add play report?
30 NGLOG_WARNING(Service_PREPO, "(STUBBED) called"); 30 LOG_WARNING(Service_PREPO, "(STUBBED) called");
31 31
32 IPC::ResponseBuilder rb{ctx, 2}; 32 IPC::ResponseBuilder rb{ctx, 2};
33 rb.Push(RESULT_SUCCESS); 33 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index bdd9eb5a5..0d036bfaa 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -122,7 +122,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
122 } 122 }
123 buf.push_back('}'); 123 buf.push_back('}');
124 124
125 NGLOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf)); 125 LOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf));
126 UNIMPLEMENTED(); 126 UNIMPLEMENTED();
127} 127}
128 128
@@ -133,7 +133,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
133 return ReportUnimplementedFunction(ctx, info); 133 return ReportUnimplementedFunction(ctx, info);
134 } 134 }
135 135
136 NGLOG_TRACE( 136 LOG_TRACE(
137 Service, "{}", 137 Service, "{}",
138 MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str()); 138 MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str());
139 handler_invoker(this, info->handler_callback, ctx); 139 handler_invoker(this, info->handler_callback, ctx);
@@ -206,12 +206,12 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
206 VI::InstallInterfaces(*sm, nv_flinger); 206 VI::InstallInterfaces(*sm, nv_flinger);
207 Set::InstallInterfaces(*sm); 207 Set::InstallInterfaces(*sm);
208 208
209 NGLOG_DEBUG(Service, "initialized OK"); 209 LOG_DEBUG(Service, "initialized OK");
210} 210}
211 211
212/// Shutdown ServiceManager 212/// Shutdown ServiceManager
213void Shutdown() { 213void Shutdown() {
214 g_kernel_named_ports.clear(); 214 g_kernel_named_ports.clear();
215 NGLOG_DEBUG(Service, "shutdown OK"); 215 LOG_DEBUG(Service, "shutdown OK");
216} 216}
217} // namespace Service 217} // namespace Service
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index f0572bed6..bd295cdf6 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -12,9 +12,6 @@
12namespace Service::Set { 12namespace Service::Set {
13 13
14void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { 14void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
15 IPC::RequestParser rp{ctx};
16 u32 id = rp.Pop<u32>();
17
18 static constexpr std::array<LanguageCode, 17> available_language_codes = {{ 15 static constexpr std::array<LanguageCode, 17> available_language_codes = {{
19 LanguageCode::JA, 16 LanguageCode::JA,
20 LanguageCode::EN_US, 17 LanguageCode::EN_US,
@@ -40,7 +37,7 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
40 rb.Push(RESULT_SUCCESS); 37 rb.Push(RESULT_SUCCESS);
41 rb.Push(static_cast<u64>(available_language_codes.size())); 38 rb.Push(static_cast<u64>(available_language_codes.size()));
42 39
43 NGLOG_DEBUG(Service_SET, "called"); 40 LOG_DEBUG(Service_SET, "called");
44} 41}
45 42
46SET::SET() : ServiceFramework("set") { 43SET::SET() : ServiceFramework("set") {
@@ -50,7 +47,7 @@ SET::SET() : ServiceFramework("set") {
50 {2, nullptr, "MakeLanguageCode"}, 47 {2, nullptr, "MakeLanguageCode"},
51 {3, nullptr, "GetAvailableLanguageCodeCount"}, 48 {3, nullptr, "GetAvailableLanguageCodeCount"},
52 {4, nullptr, "GetRegionCode"}, 49 {4, nullptr, "GetRegionCode"},
53 {5, nullptr, "GetAvailableLanguageCodes2"}, 50 {5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"},
54 {6, nullptr, "GetAvailableLanguageCodeCount2"}, 51 {6, nullptr, "GetAvailableLanguageCodeCount2"},
55 {7, nullptr, "GetKeyCodeMap"}, 52 {7, nullptr, "GetKeyCodeMap"},
56 {8, nullptr, "GetQuestFlag"}, 53 {8, nullptr, "GetQuestFlag"},
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 762a664c5..fa85277fe 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -16,7 +16,7 @@ void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) {
16 rb.Push(RESULT_SUCCESS); 16 rb.Push(RESULT_SUCCESS);
17 rb.Push<u32>(0); 17 rb.Push<u32>(0);
18 18
19 NGLOG_WARNING(Service_SET, "(STUBBED) called"); 19 LOG_WARNING(Service_SET, "(STUBBED) called");
20} 20}
21 21
22SET_SYS::SET_SYS() : ServiceFramework("set:sys") { 22SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index fe5097cdc..518a0cc46 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -17,7 +17,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
17 rb.Push(RESULT_SUCCESS); 17 rb.Push(RESULT_SUCCESS);
18 rb.Push<u32>(1); // Converted sessions start with 1 request handler 18 rb.Push<u32>(1); // Converted sessions start with 1 request handler
19 19
20 NGLOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId()); 20 LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
21} 21}
22 22
23void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { 23void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
@@ -29,11 +29,11 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
29 Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; 29 Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client};
30 rb.PushMoveObjects(session); 30 rb.PushMoveObjects(session);
31 31
32 NGLOG_DEBUG(Service, "called, session={}", session->GetObjectId()); 32 LOG_DEBUG(Service, "called, session={}", session->GetObjectId());
33} 33}
34 34
35void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { 35void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
36 NGLOG_WARNING(Service, "(STUBBED) called, using DuplicateSession"); 36 LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession");
37 37
38 DuplicateSession(ctx); 38 DuplicateSession(ctx);
39} 39}
@@ -43,7 +43,7 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
43 rb.Push(RESULT_SUCCESS); 43 rb.Push(RESULT_SUCCESS);
44 rb.Push<u32>(0x500); 44 rb.Push<u32>(0x500);
45 45
46 NGLOG_WARNING(Service, "(STUBBED) called"); 46 LOG_WARNING(Service, "(STUBBED) called");
47} 47}
48 48
49Controller::Controller() : ServiceFramework("IpcController") { 49Controller::Controller() : ServiceFramework("IpcController") {
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index bded8421f..f22a2a79f 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -86,7 +86,7 @@ SM::~SM() = default;
86void SM::Initialize(Kernel::HLERequestContext& ctx) { 86void SM::Initialize(Kernel::HLERequestContext& ctx) {
87 IPC::ResponseBuilder rb{ctx, 2}; 87 IPC::ResponseBuilder rb{ctx, 2};
88 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
89 NGLOG_DEBUG(Service_SM, "called"); 89 LOG_DEBUG(Service_SM, "called");
90} 90}
91 91
92void SM::GetService(Kernel::HLERequestContext& ctx) { 92void SM::GetService(Kernel::HLERequestContext& ctx) {
@@ -102,8 +102,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
102 if (client_port.Failed()) { 102 if (client_port.Failed()) {
103 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 103 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
104 rb.Push(client_port.Code()); 104 rb.Push(client_port.Code());
105 NGLOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, 105 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw);
106 client_port.Code().raw);
107 if (name.length() == 0) 106 if (name.length() == 0)
108 return; // LibNX Fix 107 return; // LibNX Fix
109 UNIMPLEMENTED(); 108 UNIMPLEMENTED();
@@ -113,7 +112,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
113 auto session = client_port.Unwrap()->Connect(); 112 auto session = client_port.Unwrap()->Connect();
114 ASSERT(session.Succeeded()); 113 ASSERT(session.Succeeded());
115 if (session.Succeeded()) { 114 if (session.Succeeded()) {
116 NGLOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); 115 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId());
117 IPC::ResponseBuilder rb = 116 IPC::ResponseBuilder rb =
118 rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles); 117 rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles);
119 rb.Push(session.Code()); 118 rb.Push(session.Code());
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index ab909fdaa..32648bdd9 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -8,7 +8,7 @@
8namespace Service::Sockets { 8namespace Service::Sockets {
9 9
10void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { 10void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
11 NGLOG_WARNING(Service, "(STUBBED) called"); 11 LOG_WARNING(Service, "(STUBBED) called");
12 12
13 IPC::ResponseBuilder rb{ctx, 3}; 13 IPC::ResponseBuilder rb{ctx, 3};
14 14
@@ -17,7 +17,7 @@ void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
17} 17}
18 18
19void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { 19void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
20 NGLOG_WARNING(Service, "(STUBBED) called"); 20 LOG_WARNING(Service, "(STUBBED) called");
21 21
22 IPC::ResponseBuilder rb{ctx, 3}; 22 IPC::ResponseBuilder rb{ctx, 3};
23 23
@@ -32,8 +32,7 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) {
32 u32 type = rp.Pop<u32>(); 32 u32 type = rp.Pop<u32>();
33 u32 protocol = rp.Pop<u32>(); 33 u32 protocol = rp.Pop<u32>();
34 34
35 NGLOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, 35 LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol);
36 protocol);
37 36
38 u32 fd = next_fd++; 37 u32 fd = next_fd++;
39 38
@@ -45,7 +44,7 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) {
45} 44}
46 45
47void BSD::Connect(Kernel::HLERequestContext& ctx) { 46void BSD::Connect(Kernel::HLERequestContext& ctx) {
48 NGLOG_WARNING(Service, "(STUBBED) called"); 47 LOG_WARNING(Service, "(STUBBED) called");
49 48
50 IPC::ResponseBuilder rb{ctx, 4}; 49 IPC::ResponseBuilder rb{ctx, 4};
51 50
@@ -55,7 +54,7 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
55} 54}
56 55
57void BSD::SendTo(Kernel::HLERequestContext& ctx) { 56void BSD::SendTo(Kernel::HLERequestContext& ctx) {
58 NGLOG_WARNING(Service, "(STUBBED) called"); 57 LOG_WARNING(Service, "(STUBBED) called");
59 58
60 IPC::ResponseBuilder rb{ctx, 4}; 59 IPC::ResponseBuilder rb{ctx, 4};
61 60
@@ -65,7 +64,7 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) {
65} 64}
66 65
67void BSD::Close(Kernel::HLERequestContext& ctx) { 66void BSD::Close(Kernel::HLERequestContext& ctx) {
68 NGLOG_WARNING(Service, "(STUBBED) called"); 67 LOG_WARNING(Service, "(STUBBED) called");
69 68
70 IPC::ResponseBuilder rb{ctx, 4}; 69 IPC::ResponseBuilder rb{ctx, 4};
71 70
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index f377e59f2..d235c4cfd 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -10,7 +10,7 @@ namespace Service::Sockets {
10void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { 10void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
11 IPC::RequestParser rp{ctx}; 11 IPC::RequestParser rp{ctx};
12 12
13 NGLOG_WARNING(Service, "(STUBBED) called"); 13 LOG_WARNING(Service, "(STUBBED) called");
14 14
15 IPC::ResponseBuilder rb{ctx, 2}; 15 IPC::ResponseBuilder rb{ctx, 2};
16 16
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 76ba97156..3f5a342a7 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -28,7 +28,7 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
28 28
29 IPC::ResponseBuilder rb{ctx, 2}; 29 IPC::ResponseBuilder rb{ctx, 2};
30 rb.Push(RESULT_SUCCESS); 30 rb.Push(RESULT_SUCCESS);
31 NGLOG_DEBUG(Service_SPL, "called"); 31 LOG_DEBUG(Service_SPL, "called");
32} 32}
33 33
34void InstallInterfaces(SM::ServiceManager& service_manager) { 34void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index b3dad8b06..40aea6090 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -65,7 +65,7 @@ public:
65 65
66private: 66private:
67 void SetOption(Kernel::HLERequestContext& ctx) { 67 void SetOption(Kernel::HLERequestContext& ctx) {
68 NGLOG_WARNING(Service_SSL, "(STUBBED) called"); 68 LOG_WARNING(Service_SSL, "(STUBBED) called");
69 IPC::RequestParser rp{ctx}; 69 IPC::RequestParser rp{ctx};
70 70
71 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 71 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
@@ -73,7 +73,7 @@ private:
73 } 73 }
74 74
75 void CreateConnection(Kernel::HLERequestContext& ctx) { 75 void CreateConnection(Kernel::HLERequestContext& ctx) {
76 NGLOG_WARNING(Service_SSL, "(STUBBED) called"); 76 LOG_WARNING(Service_SSL, "(STUBBED) called");
77 77
78 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 78 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
79 rb.Push(RESULT_SUCCESS); 79 rb.Push(RESULT_SUCCESS);
@@ -82,7 +82,7 @@ private:
82}; 82};
83 83
84void SSL::CreateContext(Kernel::HLERequestContext& ctx) { 84void SSL::CreateContext(Kernel::HLERequestContext& ctx) {
85 NGLOG_WARNING(Service_SSL, "(STUBBED) called"); 85 LOG_WARNING(Service_SSL, "(STUBBED) called");
86 86
87 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 87 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
88 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
@@ -103,7 +103,7 @@ SSL::SSL() : ServiceFramework("ssl") {
103} 103}
104 104
105void SSL::SetInterfaceVersion(Kernel::HLERequestContext& ctx) { 105void SSL::SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
106 NGLOG_WARNING(Service_SSL, "(STUBBED) called"); 106 LOG_WARNING(Service_SSL, "(STUBBED) called");
107 IPC::RequestParser rp{ctx}; 107 IPC::RequestParser rp{ctx};
108 u32 unk1 = rp.Pop<u32>(); // Probably minor/major? 108 u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
109 u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does 109 u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 654012189..507ae95f4 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -33,14 +33,14 @@ private:
33 const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>( 33 const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
34 std::chrono::system_clock::now().time_since_epoch()) 34 std::chrono::system_clock::now().time_since_epoch())
35 .count()}; 35 .count()};
36 NGLOG_DEBUG(Service_Time, "called"); 36 LOG_DEBUG(Service_Time, "called");
37 IPC::ResponseBuilder rb{ctx, 4}; 37 IPC::ResponseBuilder rb{ctx, 4};
38 rb.Push(RESULT_SUCCESS); 38 rb.Push(RESULT_SUCCESS);
39 rb.Push<u64>(time_since_epoch); 39 rb.Push<u64>(time_since_epoch);
40 } 40 }
41 41
42 void GetSystemClockContext(Kernel::HLERequestContext& ctx) { 42 void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
43 NGLOG_WARNING(Service_Time, "(STUBBED) called"); 43 LOG_WARNING(Service_Time, "(STUBBED) called");
44 SystemClockContext system_clock_ontext{}; 44 SystemClockContext system_clock_ontext{};
45 IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; 45 IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
46 rb.Push(RESULT_SUCCESS); 46 rb.Push(RESULT_SUCCESS);
@@ -59,7 +59,7 @@ public:
59 59
60private: 60private:
61 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { 61 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
62 NGLOG_DEBUG(Service_Time, "called"); 62 LOG_DEBUG(Service_Time, "called");
63 SteadyClockTimePoint steady_clock_time_point{ 63 SteadyClockTimePoint steady_clock_time_point{
64 CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000}; 64 CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000};
65 IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; 65 IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
@@ -91,21 +91,21 @@ private:
91 TimeZoneRule my_time_zone_rule{}; 91 TimeZoneRule my_time_zone_rule{};
92 92
93 void GetDeviceLocationName(Kernel::HLERequestContext& ctx) { 93 void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
94 NGLOG_DEBUG(Service_Time, "called"); 94 LOG_DEBUG(Service_Time, "called");
95 IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2}; 95 IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
96 rb.Push(RESULT_SUCCESS); 96 rb.Push(RESULT_SUCCESS);
97 rb.PushRaw(location_name); 97 rb.PushRaw(location_name);
98 } 98 }
99 99
100 void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) { 100 void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) {
101 NGLOG_WARNING(Service_Time, "(STUBBED) called"); 101 LOG_WARNING(Service_Time, "(STUBBED) called");
102 IPC::ResponseBuilder rb{ctx, 3}; 102 IPC::ResponseBuilder rb{ctx, 3};
103 rb.Push(RESULT_SUCCESS); 103 rb.Push(RESULT_SUCCESS);
104 rb.Push<u32>(0); 104 rb.Push<u32>(0);
105 } 105 }
106 106
107 void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) { 107 void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
108 NGLOG_WARNING(Service_Time, "(STUBBED) called"); 108 LOG_WARNING(Service_Time, "(STUBBED) called");
109 109
110 ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule)); 110 ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule));
111 111
@@ -117,7 +117,7 @@ private:
117 IPC::RequestParser rp{ctx}; 117 IPC::RequestParser rp{ctx};
118 const u64 posix_time = rp.Pop<u64>(); 118 const u64 posix_time = rp.Pop<u64>();
119 119
120 NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); 120 LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
121 121
122 TimeZoneRule time_zone_rule{}; 122 TimeZoneRule time_zone_rule{};
123 auto buffer = ctx.ReadBuffer(); 123 auto buffer = ctx.ReadBuffer();
@@ -138,7 +138,7 @@ private:
138 IPC::RequestParser rp{ctx}; 138 IPC::RequestParser rp{ctx};
139 const u64 posix_time = rp.Pop<u64>(); 139 const u64 posix_time = rp.Pop<u64>();
140 140
141 NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); 141 LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
142 142
143 CalendarTime calendar_time{2018, 1, 1, 0, 0, 0}; 143 CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
144 CalendarAdditionalInfo additional_info{}; 144 CalendarAdditionalInfo additional_info{};
@@ -176,35 +176,35 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
176 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 176 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
177 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
178 rb.PushIpcInterface<ISystemClock>(); 178 rb.PushIpcInterface<ISystemClock>();
179 NGLOG_DEBUG(Service_Time, "called"); 179 LOG_DEBUG(Service_Time, "called");
180} 180}
181 181
182void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { 182void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(RESULT_SUCCESS); 184 rb.Push(RESULT_SUCCESS);
185 rb.PushIpcInterface<ISystemClock>(); 185 rb.PushIpcInterface<ISystemClock>();
186 NGLOG_DEBUG(Service_Time, "called"); 186 LOG_DEBUG(Service_Time, "called");
187} 187}
188 188
189void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { 189void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
190 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 190 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
191 rb.Push(RESULT_SUCCESS); 191 rb.Push(RESULT_SUCCESS);
192 rb.PushIpcInterface<ISteadyClock>(); 192 rb.PushIpcInterface<ISteadyClock>();
193 NGLOG_DEBUG(Service_Time, "called"); 193 LOG_DEBUG(Service_Time, "called");
194} 194}
195 195
196void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { 196void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
197 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 197 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
198 rb.Push(RESULT_SUCCESS); 198 rb.Push(RESULT_SUCCESS);
199 rb.PushIpcInterface<ITimeZoneService>(); 199 rb.PushIpcInterface<ITimeZoneService>();
200 NGLOG_DEBUG(Service_Time, "called"); 200 LOG_DEBUG(Service_Time, "called");
201} 201}
202 202
203void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { 203void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
204 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 204 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
205 rb.Push(RESULT_SUCCESS); 205 rb.Push(RESULT_SUCCESS);
206 rb.PushIpcInterface<ISystemClock>(); 206 rb.PushIpcInterface<ISystemClock>();
207 NGLOG_DEBUG(Service_Time, "called"); 207 LOG_DEBUG(Service_Time, "called");
208} 208}
209 209
210Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 210Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index e86556671..f3765b555 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -470,7 +470,7 @@ private:
470 u32 flags = rp.Pop<u32>(); 470 u32 flags = rp.Pop<u32>();
471 auto buffer_queue = nv_flinger->GetBufferQueue(id); 471 auto buffer_queue = nv_flinger->GetBufferQueue(id);
472 472
473 NGLOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction)); 473 LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction));
474 474
475 if (transaction == TransactionId::Connect) { 475 if (transaction == TransactionId::Connect) {
476 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 476 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
@@ -532,7 +532,7 @@ private:
532 IGBPQueryResponseParcel response{value}; 532 IGBPQueryResponseParcel response{value};
533 ctx.WriteBuffer(response.Serialize()); 533 ctx.WriteBuffer(response.Serialize());
534 } else if (transaction == TransactionId::CancelBuffer) { 534 } else if (transaction == TransactionId::CancelBuffer) {
535 NGLOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); 535 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
536 } else { 536 } else {
537 ASSERT_MSG(false, "Unimplemented"); 537 ASSERT_MSG(false, "Unimplemented");
538 } 538 }
@@ -547,8 +547,8 @@ private:
547 s32 addval = rp.PopRaw<s32>(); 547 s32 addval = rp.PopRaw<s32>();
548 u32 type = rp.Pop<u32>(); 548 u32 type = rp.Pop<u32>();
549 549
550 NGLOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval, 550 LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval,
551 type); 551 type);
552 IPC::ResponseBuilder rb{ctx, 2}; 552 IPC::ResponseBuilder rb{ctx, 2};
553 rb.Push(RESULT_SUCCESS); 553 rb.Push(RESULT_SUCCESS);
554 } 554 }
@@ -562,7 +562,7 @@ private:
562 562
563 // TODO(Subv): Find out what this actually is. 563 // TODO(Subv): Find out what this actually is.
564 564
565 NGLOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 565 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
566 IPC::ResponseBuilder rb{ctx, 2, 1}; 566 IPC::ResponseBuilder rb{ctx, 2, 1};
567 rb.Push(RESULT_SUCCESS); 567 rb.Push(RESULT_SUCCESS);
568 rb.PushCopyObjects(buffer_queue->GetNativeHandle()); 568 rb.PushCopyObjects(buffer_queue->GetNativeHandle());
@@ -625,7 +625,7 @@ public:
625 625
626private: 626private:
627 void SetLayerZ(Kernel::HLERequestContext& ctx) { 627 void SetLayerZ(Kernel::HLERequestContext& ctx) {
628 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 628 LOG_WARNING(Service_VI, "(STUBBED) called");
629 IPC::RequestParser rp{ctx}; 629 IPC::RequestParser rp{ctx};
630 u64 layer_id = rp.Pop<u64>(); 630 u64 layer_id = rp.Pop<u64>();
631 u64 z_value = rp.Pop<u64>(); 631 u64 z_value = rp.Pop<u64>();
@@ -640,8 +640,8 @@ private:
640 bool visibility = rp.Pop<bool>(); 640 bool visibility = rp.Pop<bool>();
641 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 641 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
642 rb.Push(RESULT_SUCCESS); 642 rb.Push(RESULT_SUCCESS);
643 NGLOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, 643 LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
644 visibility); 644 visibility);
645 } 645 }
646}; 646};
647 647
@@ -723,7 +723,7 @@ public:
723 723
724private: 724private:
725 void CloseDisplay(Kernel::HLERequestContext& ctx) { 725 void CloseDisplay(Kernel::HLERequestContext& ctx) {
726 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 726 LOG_WARNING(Service_VI, "(STUBBED) called");
727 IPC::RequestParser rp{ctx}; 727 IPC::RequestParser rp{ctx};
728 u64 display = rp.Pop<u64>(); 728 u64 display = rp.Pop<u64>();
729 729
@@ -732,7 +732,7 @@ private:
732 } 732 }
733 733
734 void CreateManagedLayer(Kernel::HLERequestContext& ctx) { 734 void CreateManagedLayer(Kernel::HLERequestContext& ctx) {
735 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 735 LOG_WARNING(Service_VI, "(STUBBED) called");
736 IPC::RequestParser rp{ctx}; 736 IPC::RequestParser rp{ctx};
737 u32 unknown = rp.Pop<u32>(); 737 u32 unknown = rp.Pop<u32>();
738 rp.Skip(1, false); 738 rp.Skip(1, false);
@@ -747,7 +747,7 @@ private:
747 } 747 }
748 748
749 void AddToLayerStack(Kernel::HLERequestContext& ctx) { 749 void AddToLayerStack(Kernel::HLERequestContext& ctx) {
750 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 750 LOG_WARNING(Service_VI, "(STUBBED) called");
751 IPC::RequestParser rp{ctx}; 751 IPC::RequestParser rp{ctx};
752 u32 stack = rp.Pop<u32>(); 752 u32 stack = rp.Pop<u32>();
753 u64 layer_id = rp.Pop<u64>(); 753 u64 layer_id = rp.Pop<u64>();
@@ -762,8 +762,8 @@ private:
762 bool visibility = rp.Pop<bool>(); 762 bool visibility = rp.Pop<bool>();
763 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 763 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
764 rb.Push(RESULT_SUCCESS); 764 rb.Push(RESULT_SUCCESS);
765 NGLOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, 765 LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
766 visibility); 766 visibility);
767 } 767 }
768 768
769 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 769 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
@@ -776,7 +776,7 @@ public:
776 776
777private: 777private:
778 void GetRelayService(Kernel::HLERequestContext& ctx) { 778 void GetRelayService(Kernel::HLERequestContext& ctx) {
779 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 779 LOG_WARNING(Service_VI, "(STUBBED) called");
780 780
781 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 781 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
782 rb.Push(RESULT_SUCCESS); 782 rb.Push(RESULT_SUCCESS);
@@ -784,7 +784,7 @@ private:
784 } 784 }
785 785
786 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { 786 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
787 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 787 LOG_WARNING(Service_VI, "(STUBBED) called");
788 788
789 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 789 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
790 rb.Push(RESULT_SUCCESS); 790 rb.Push(RESULT_SUCCESS);
@@ -792,7 +792,7 @@ private:
792 } 792 }
793 793
794 void GetManagerDisplayService(Kernel::HLERequestContext& ctx) { 794 void GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
795 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 795 LOG_WARNING(Service_VI, "(STUBBED) called");
796 796
797 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 797 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
798 rb.Push(RESULT_SUCCESS); 798 rb.Push(RESULT_SUCCESS);
@@ -800,7 +800,7 @@ private:
800 } 800 }
801 801
802 void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) { 802 void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) {
803 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 803 LOG_WARNING(Service_VI, "(STUBBED) called");
804 804
805 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 805 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
806 rb.Push(RESULT_SUCCESS); 806 rb.Push(RESULT_SUCCESS);
@@ -808,7 +808,7 @@ private:
808 } 808 }
809 809
810 void OpenDisplay(Kernel::HLERequestContext& ctx) { 810 void OpenDisplay(Kernel::HLERequestContext& ctx) {
811 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 811 LOG_WARNING(Service_VI, "(STUBBED) called");
812 IPC::RequestParser rp{ctx}; 812 IPC::RequestParser rp{ctx};
813 auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); 813 auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
814 auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); 814 auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
@@ -823,7 +823,7 @@ private:
823 } 823 }
824 824
825 void CloseDisplay(Kernel::HLERequestContext& ctx) { 825 void CloseDisplay(Kernel::HLERequestContext& ctx) {
826 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 826 LOG_WARNING(Service_VI, "(STUBBED) called");
827 IPC::RequestParser rp{ctx}; 827 IPC::RequestParser rp{ctx};
828 u64 display_id = rp.Pop<u64>(); 828 u64 display_id = rp.Pop<u64>();
829 829
@@ -832,7 +832,7 @@ private:
832 } 832 }
833 833
834 void GetDisplayResolution(Kernel::HLERequestContext& ctx) { 834 void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
835 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 835 LOG_WARNING(Service_VI, "(STUBBED) called");
836 IPC::RequestParser rp{ctx}; 836 IPC::RequestParser rp{ctx};
837 u64 display_id = rp.Pop<u64>(); 837 u64 display_id = rp.Pop<u64>();
838 838
@@ -849,7 +849,7 @@ private:
849 } 849 }
850 850
851 void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { 851 void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
852 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 852 LOG_WARNING(Service_VI, "(STUBBED) called");
853 IPC::RequestParser rp{ctx}; 853 IPC::RequestParser rp{ctx};
854 u32 scaling_mode = rp.Pop<u32>(); 854 u32 scaling_mode = rp.Pop<u32>();
855 u64 unknown = rp.Pop<u64>(); 855 u64 unknown = rp.Pop<u64>();
@@ -865,11 +865,11 @@ private:
865 IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); 865 IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
866 rb.Push(RESULT_SUCCESS); 866 rb.Push(RESULT_SUCCESS);
867 rb.Push<u64>(1); 867 rb.Push<u64>(1);
868 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 868 LOG_WARNING(Service_VI, "(STUBBED) called");
869 } 869 }
870 870
871 void OpenLayer(Kernel::HLERequestContext& ctx) { 871 void OpenLayer(Kernel::HLERequestContext& ctx) {
872 NGLOG_DEBUG(Service_VI, "called"); 872 LOG_DEBUG(Service_VI, "called");
873 IPC::RequestParser rp{ctx}; 873 IPC::RequestParser rp{ctx};
874 auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); 874 auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
875 auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); 875 auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
@@ -889,7 +889,7 @@ private:
889 } 889 }
890 890
891 void CreateStrayLayer(Kernel::HLERequestContext& ctx) { 891 void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
892 NGLOG_DEBUG(Service_VI, "called"); 892 LOG_DEBUG(Service_VI, "called");
893 893
894 IPC::RequestParser rp{ctx}; 894 IPC::RequestParser rp{ctx};
895 u32 flags = rp.Pop<u32>(); 895 u32 flags = rp.Pop<u32>();
@@ -909,7 +909,7 @@ private:
909 } 909 }
910 910
911 void DestroyStrayLayer(Kernel::HLERequestContext& ctx) { 911 void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
912 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 912 LOG_WARNING(Service_VI, "(STUBBED) called");
913 913
914 IPC::RequestParser rp{ctx}; 914 IPC::RequestParser rp{ctx};
915 u64 layer_id = rp.Pop<u64>(); 915 u64 layer_id = rp.Pop<u64>();
@@ -919,7 +919,7 @@ private:
919 } 919 }
920 920
921 void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) { 921 void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
922 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 922 LOG_WARNING(Service_VI, "(STUBBED) called");
923 IPC::RequestParser rp{ctx}; 923 IPC::RequestParser rp{ctx};
924 u64 display_id = rp.Pop<u64>(); 924 u64 display_id = rp.Pop<u64>();
925 925
@@ -968,7 +968,7 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
968 : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} 968 : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
969 969
970void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { 970void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
971 NGLOG_WARNING(Service_VI, "(STUBBED) called"); 971 LOG_WARNING(Service_VI, "(STUBBED) called");
972 972
973 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 973 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
974 rb.Push(RESULT_SUCCESS); 974 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 8fc91dc9c..2f48068c1 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -33,7 +33,7 @@ inline void Read(T& var, const u32 addr) {
33 LCD::Read(var, addr); 33 LCD::Read(var, addr);
34 break; 34 break;
35 default: 35 default:
36 NGLOG_ERROR(HW_Memory, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); 36 LOG_ERROR(HW_Memory, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr);
37 break; 37 break;
38 } 38 }
39} 39}
@@ -62,7 +62,7 @@ inline void Write(u32 addr, const T data) {
62 LCD::Write(addr, data); 62 LCD::Write(addr, data);
63 break; 63 break;
64 default: 64 default:
65 NGLOG_ERROR(HW_Memory, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); 65 LOG_ERROR(HW_Memory, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr);
66 break; 66 break;
67 } 67 }
68} 68}
@@ -85,12 +85,12 @@ void Update() {}
85/// Initialize hardware 85/// Initialize hardware
86void Init() { 86void Init() {
87 LCD::Init(); 87 LCD::Init();
88 NGLOG_DEBUG(HW, "Initialized OK"); 88 LOG_DEBUG(HW, "Initialized OK");
89} 89}
90 90
91/// Shutdown hardware 91/// Shutdown hardware
92void Shutdown() { 92void Shutdown() {
93 LCD::Shutdown(); 93 LCD::Shutdown();
94 NGLOG_DEBUG(HW, "Shutdown OK"); 94 LOG_DEBUG(HW, "Shutdown OK");
95} 95}
96} // namespace HW 96} // namespace HW
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp
index e8525efde..0b62174d5 100644
--- a/src/core/hw/lcd.cpp
+++ b/src/core/hw/lcd.cpp
@@ -20,7 +20,7 @@ inline void Read(T& var, const u32 raw_addr) {
20 20
21 // Reads other than u32 are untested, so I'd rather have them abort than silently fail 21 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
22 if (index >= 0x400 || !std::is_same<T, u32>::value) { 22 if (index >= 0x400 || !std::is_same<T, u32>::value) {
23 NGLOG_ERROR(HW_LCD, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); 23 LOG_ERROR(HW_LCD, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr);
24 return; 24 return;
25 } 25 }
26 26
@@ -34,7 +34,7 @@ inline void Write(u32 addr, const T data) {
34 34
35 // Writes other than u32 are untested, so I'd rather have them abort than silently fail 35 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
36 if (index >= 0x400 || !std::is_same<T, u32>::value) { 36 if (index >= 0x400 || !std::is_same<T, u32>::value) {
37 NGLOG_ERROR(HW_LCD, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); 37 LOG_ERROR(HW_LCD, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr);
38 return; 38 return;
39 } 39 }
40 40
@@ -56,12 +56,12 @@ template void Write<u8>(u32 addr, const u8 data);
56/// Initialize hardware 56/// Initialize hardware
57void Init() { 57void Init() {
58 memset(&g_regs, 0, sizeof(g_regs)); 58 memset(&g_regs, 0, sizeof(g_regs));
59 NGLOG_DEBUG(HW_LCD, "Initialized OK"); 59 LOG_DEBUG(HW_LCD, "Initialized OK");
60} 60}
61 61
62/// Shutdown hardware 62/// Shutdown hardware
63void Shutdown() { 63void Shutdown() {
64 NGLOG_DEBUG(HW_LCD, "Shutdown OK"); 64 LOG_DEBUG(HW_LCD, "Shutdown OK");
65} 65}
66 66
67} // namespace LCD 67} // namespace LCD
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index b01b2caf6..eb7feb617 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -132,7 +132,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
132 const VAddr load_addr = next_load_addr; 132 const VAddr load_addr = next_load_addr;
133 next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); 133 next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
134 if (next_load_addr) { 134 if (next_load_addr) {
135 NGLOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 135 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
136 } else { 136 } else {
137 next_load_addr = load_addr; 137 next_load_addr = load_addr;
138 } 138 }
@@ -163,7 +163,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(
163 std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { 163 std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {
164 164
165 if (filepath_romfs.empty()) { 165 if (filepath_romfs.empty()) {
166 NGLOG_DEBUG(Loader, "No RomFS available"); 166 LOG_DEBUG(Loader, "No RomFS available");
167 return ResultStatus::ErrorNotUsed; 167 return ResultStatus::ErrorNotUsed;
168 } 168 }
169 169
@@ -176,8 +176,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(
176 offset = 0; 176 offset = 0;
177 size = romfs_file->GetSize(); 177 size = romfs_file->GetSize();
178 178
179 NGLOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); 179 LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
180 NGLOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); 180 LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
181 181
182 // Reset read pointer 182 // Reset read pointer
183 file.Seek(0, SEEK_SET); 183 file.Seek(0, SEEK_SET);
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index e42d3a870..b69e5c6ef 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -273,18 +273,18 @@ const char* ElfReader::GetSectionName(int section) const {
273} 273}
274 274
275SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { 275SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
276 NGLOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 276 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
277 277
278 // Should we relocate? 278 // Should we relocate?
279 relocate = (header->e_type != ET_EXEC); 279 relocate = (header->e_type != ET_EXEC);
280 280
281 if (relocate) { 281 if (relocate) {
282 NGLOG_DEBUG(Loader, "Relocatable module"); 282 LOG_DEBUG(Loader, "Relocatable module");
283 entryPoint += vaddr; 283 entryPoint += vaddr;
284 } else { 284 } else {
285 NGLOG_DEBUG(Loader, "Prerelocated executable"); 285 LOG_DEBUG(Loader, "Prerelocated executable");
286 } 286 }
287 NGLOG_DEBUG(Loader, "{} segments:", header->e_phnum); 287 LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
288 288
289 // First pass : Get the bits into RAM 289 // First pass : Get the bits into RAM
290 u32 base_addr = relocate ? vaddr : 0; 290 u32 base_addr = relocate ? vaddr : 0;
@@ -304,8 +304,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
304 304
305 for (unsigned int i = 0; i < header->e_phnum; ++i) { 305 for (unsigned int i = 0; i < header->e_phnum; ++i) {
306 Elf32_Phdr* p = &segments[i]; 306 Elf32_Phdr* p = &segments[i];
307 NGLOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, 307 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
308 p->p_vaddr, p->p_filesz, p->p_memsz); 308 p->p_vaddr, p->p_filesz, p->p_memsz);
309 309
310 if (p->p_type == PT_LOAD) { 310 if (p->p_type == PT_LOAD) {
311 CodeSet::Segment* codeset_segment; 311 CodeSet::Segment* codeset_segment;
@@ -317,16 +317,16 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
317 } else if (permission_flags == (PF_R | PF_W)) { 317 } else if (permission_flags == (PF_R | PF_W)) {
318 codeset_segment = &codeset->data; 318 codeset_segment = &codeset->data;
319 } else { 319 } else {
320 NGLOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 320 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
321 p->p_flags); 321 p->p_flags);
322 continue; 322 continue;
323 } 323 }
324 324
325 if (codeset_segment->size != 0) { 325 if (codeset_segment->size != 0) {
326 NGLOG_ERROR(Loader, 326 LOG_ERROR(Loader,
327 "ELF has more than one segment of the same type. Skipping extra " 327 "ELF has more than one segment of the same type. Skipping extra "
328 "segment (id {})", 328 "segment (id {})",
329 i); 329 i);
330 continue; 330 continue;
331 } 331 }
332 332
@@ -345,7 +345,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
345 codeset->entrypoint = base_addr + header->e_entry; 345 codeset->entrypoint = base_addr + header->e_entry;
346 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 346 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
347 347
348 NGLOG_DEBUG(Loader, "Done loading."); 348 LOG_DEBUG(Loader, "Done loading.");
349 349
350 return codeset; 350 return codeset;
351} 351}
diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp
index c7be5f265..769516b6f 100644
--- a/src/core/loader/linker.cpp
+++ b/src/core/loader/linker.cpp
@@ -84,7 +84,7 @@ void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<
84 } 84 }
85 break; 85 break;
86 default: 86 default:
87 NGLOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type)); 87 LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
88 break; 88 break;
89 } 89 }
90 } 90 }
@@ -141,7 +141,7 @@ void Linker::ResolveImports() {
141 if (search != exports.end()) { 141 if (search != exports.end()) {
142 Memory::Write64(import.second.ea, search->second + import.second.addend); 142 Memory::Write64(import.second.ea, search->second + import.second.addend);
143 } else { 143 } else {
144 NGLOG_ERROR(Loader, "Unresolved import: {}", import.first); 144 LOG_ERROR(Loader, "Unresolved import: {}", import.first);
145 } 145 }
146 } 146 }
147} 147}
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 6a4fd38cb..8831d8e83 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -9,6 +9,7 @@
9#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
10#include "core/loader/deconstructed_rom_directory.h" 10#include "core/loader/deconstructed_rom_directory.h"
11#include "core/loader/elf.h" 11#include "core/loader/elf.h"
12#include "core/loader/nca.h"
12#include "core/loader/nro.h" 13#include "core/loader/nro.h"
13#include "core/loader/nso.h" 14#include "core/loader/nso.h"
14 15
@@ -32,6 +33,7 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) {
32 CHECK_TYPE(ELF) 33 CHECK_TYPE(ELF)
33 CHECK_TYPE(NSO) 34 CHECK_TYPE(NSO)
34 CHECK_TYPE(NRO) 35 CHECK_TYPE(NRO)
36 CHECK_TYPE(NCA)
35 37
36#undef CHECK_TYPE 38#undef CHECK_TYPE
37 39
@@ -41,7 +43,7 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) {
41FileType IdentifyFile(const std::string& file_name) { 43FileType IdentifyFile(const std::string& file_name) {
42 FileUtil::IOFile file(file_name, "rb"); 44 FileUtil::IOFile file(file_name, "rb");
43 if (!file.IsOpen()) { 45 if (!file.IsOpen()) {
44 NGLOG_ERROR(Loader, "Failed to load file {}", file_name); 46 LOG_ERROR(Loader, "Failed to load file {}", file_name);
45 return FileType::Unknown; 47 return FileType::Unknown;
46 } 48 }
47 49
@@ -57,6 +59,8 @@ FileType GuessFromExtension(const std::string& extension_) {
57 return FileType::NRO; 59 return FileType::NRO;
58 else if (extension == ".nso") 60 else if (extension == ".nso")
59 return FileType::NSO; 61 return FileType::NSO;
62 else if (extension == ".nca")
63 return FileType::NCA;
60 64
61 return FileType::Unknown; 65 return FileType::Unknown;
62} 66}
@@ -69,6 +73,8 @@ const char* GetFileTypeString(FileType type) {
69 return "NRO"; 73 return "NRO";
70 case FileType::NSO: 74 case FileType::NSO:
71 return "NSO"; 75 return "NSO";
76 case FileType::NCA:
77 return "NCA";
72 case FileType::DeconstructedRomDirectory: 78 case FileType::DeconstructedRomDirectory:
73 return "Directory"; 79 return "Directory";
74 case FileType::Error: 80 case FileType::Error:
@@ -104,6 +110,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp
104 case FileType::NRO: 110 case FileType::NRO:
105 return std::make_unique<AppLoader_NRO>(std::move(file), filepath); 111 return std::make_unique<AppLoader_NRO>(std::move(file), filepath);
106 112
113 // NX NCA file format.
114 case FileType::NCA:
115 return std::make_unique<AppLoader_NCA>(std::move(file), filepath);
116
107 // NX deconstructed ROM directory. 117 // NX deconstructed ROM directory.
108 case FileType::DeconstructedRomDirectory: 118 case FileType::DeconstructedRomDirectory:
109 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath); 119 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath);
@@ -116,7 +126,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp
116std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { 126std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
117 FileUtil::IOFile file(filename, "rb"); 127 FileUtil::IOFile file(filename, "rb");
118 if (!file.IsOpen()) { 128 if (!file.IsOpen()) {
119 NGLOG_ERROR(Loader, "Failed to load file {}", filename); 129 LOG_ERROR(Loader, "Failed to load file {}", filename);
120 return nullptr; 130 return nullptr;
121 } 131 }
122 132
@@ -127,12 +137,12 @@ std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
127 FileType filename_type = GuessFromExtension(filename_extension); 137 FileType filename_type = GuessFromExtension(filename_extension);
128 138
129 if (type != filename_type) { 139 if (type != filename_type) {
130 NGLOG_WARNING(Loader, "File {} has a different type than its extension.", filename); 140 LOG_WARNING(Loader, "File {} has a different type than its extension.", filename);
131 if (FileType::Unknown == type) 141 if (FileType::Unknown == type)
132 type = filename_type; 142 type = filename_type;
133 } 143 }
134 144
135 NGLOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type)); 145 LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type));
136 146
137 return GetFileLoader(std::move(file), type, filename_filename, filename); 147 return GetFileLoader(std::move(file), type, filename_filename, filename);
138} 148}
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index b1aabb1cb..b76f7b13d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -29,6 +29,7 @@ enum class FileType {
29 ELF, 29 ELF,
30 NSO, 30 NSO,
31 NRO, 31 NRO,
32 NCA,
32 DeconstructedRomDirectory, 33 DeconstructedRomDirectory,
33}; 34};
34 35
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
new file mode 100644
index 000000000..da064f8e3
--- /dev/null
+++ b/src/core/loader/nca.cpp
@@ -0,0 +1,303 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <vector>
6
7#include "common/common_funcs.h"
8#include "common/file_util.h"
9#include "common/logging/log.h"
10#include "common/swap.h"
11#include "core/core.h"
12#include "core/file_sys/program_metadata.h"
13#include "core/file_sys/romfs_factory.h"
14#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/resource_limit.h"
16#include "core/hle/service/filesystem/filesystem.h"
17#include "core/loader/nca.h"
18#include "core/loader/nso.h"
19#include "core/memory.h"
20
21namespace Loader {
22
23// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
24constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
25
26constexpr u64 SECTION_HEADER_SIZE = 0x200;
27constexpr u64 SECTION_HEADER_OFFSET = 0x400;
28
29enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
30
31enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
32
33struct NcaSectionTableEntry {
34 u32_le media_offset;
35 u32_le media_end_offset;
36 INSERT_PADDING_BYTES(0x8);
37};
38static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size.");
39
40struct NcaHeader {
41 std::array<u8, 0x100> rsa_signature_1;
42 std::array<u8, 0x100> rsa_signature_2;
43 u32_le magic;
44 u8 is_system;
45 NcaContentType content_type;
46 u8 crypto_type;
47 u8 key_index;
48 u64_le size;
49 u64_le title_id;
50 INSERT_PADDING_BYTES(0x4);
51 u32_le sdk_version;
52 u8 crypto_type_2;
53 INSERT_PADDING_BYTES(15);
54 std::array<u8, 0x10> rights_id;
55 std::array<NcaSectionTableEntry, 0x4> section_tables;
56 std::array<std::array<u8, 0x20>, 0x4> hash_tables;
57 std::array<std::array<u8, 0x10>, 0x4> key_area;
58 INSERT_PADDING_BYTES(0xC0);
59};
60static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size.");
61
62struct NcaSectionHeaderBlock {
63 INSERT_PADDING_BYTES(3);
64 NcaSectionFilesystemType filesystem_type;
65 u8 crypto_type;
66 INSERT_PADDING_BYTES(3);
67};
68static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size.");
69
70struct Pfs0Superblock {
71 NcaSectionHeaderBlock header_block;
72 std::array<u8, 0x20> hash;
73 u32_le size;
74 INSERT_PADDING_BYTES(4);
75 u64_le hash_table_offset;
76 u64_le hash_table_size;
77 u64_le pfs0_header_offset;
78 u64_le pfs0_size;
79 INSERT_PADDING_BYTES(432);
80};
81static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size.");
82
83static bool IsValidNca(const NcaHeader& header) {
84 return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
85 header.magic == Common::MakeMagic('N', 'C', 'A', '3');
86}
87
88// TODO(DarkLordZach): Add support for encrypted.
89class Nca final {
90 std::vector<FileSys::PartitionFilesystem> pfs;
91 std::vector<u64> pfs_offset;
92
93 u64 romfs_offset = 0;
94 u64 romfs_size = 0;
95
96 boost::optional<u8> exefs_id = boost::none;
97
98 FileUtil::IOFile file;
99 std::string path;
100
101 u64 GetExeFsFileOffset(const std::string& file_name) const;
102 u64 GetExeFsFileSize(const std::string& file_name) const;
103
104public:
105 ResultStatus Load(FileUtil::IOFile&& file, std::string path);
106
107 FileSys::PartitionFilesystem GetPfs(u8 id) const;
108
109 u64 GetRomFsOffset() const;
110 u64 GetRomFsSize() const;
111
112 std::vector<u8> GetExeFsFile(const std::string& file_name);
113};
114
115static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) {
116 // According to switchbrew, an exefs must only contain these two files:
117 return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0;
118}
119
120ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) {
121 file = std::move(in_file);
122 path = in_path;
123 file.Seek(0, SEEK_SET);
124 std::array<u8, sizeof(NcaHeader)> header_array{};
125 if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader)))
126 LOG_CRITICAL(Loader, "File reader errored out during header read.");
127
128 NcaHeader header{};
129 std::memcpy(&header, header_array.data(), sizeof(NcaHeader));
130 if (!IsValidNca(header))
131 return ResultStatus::ErrorInvalidFormat;
132
133 int number_sections =
134 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
135 [](NcaSectionTableEntry entry) { return entry.media_offset > 0; });
136
137 for (int i = 0; i < number_sections; ++i) {
138 // Seek to beginning of this section.
139 file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
140 std::array<u8, sizeof(NcaSectionHeaderBlock)> array{};
141 if (sizeof(NcaSectionHeaderBlock) !=
142 file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock)))
143 LOG_CRITICAL(Loader, "File reader errored out during header read.");
144
145 NcaSectionHeaderBlock block{};
146 std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock));
147
148 if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) {
149 romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
150 romfs_size =
151 header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset;
152 } else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) {
153 Pfs0Superblock sb{};
154 // Seek back to beginning of this section.
155 file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
156 if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock)))
157 LOG_CRITICAL(Loader, "File reader errored out during header read.");
158
159 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
160 MEDIA_OFFSET_MULTIPLIER) +
161 sb.pfs0_header_offset;
162 FileSys::PartitionFilesystem npfs{};
163 ResultStatus status = npfs.Load(path, offset);
164
165 if (status == ResultStatus::Success) {
166 pfs.emplace_back(std::move(npfs));
167 pfs_offset.emplace_back(offset);
168 }
169 }
170 }
171
172 for (size_t i = 0; i < pfs.size(); ++i) {
173 if (IsPfsExeFs(pfs[i]))
174 exefs_id = i;
175 }
176
177 return ResultStatus::Success;
178}
179
180FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const {
181 return pfs[id];
182}
183
184u64 Nca::GetExeFsFileOffset(const std::string& file_name) const {
185 if (exefs_id == boost::none)
186 return 0;
187 return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id];
188}
189
190u64 Nca::GetExeFsFileSize(const std::string& file_name) const {
191 if (exefs_id == boost::none)
192 return 0;
193 return pfs[*exefs_id].GetFileSize(file_name);
194}
195
196u64 Nca::GetRomFsOffset() const {
197 return romfs_offset;
198}
199
200u64 Nca::GetRomFsSize() const {
201 return romfs_size;
202}
203
204std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) {
205 std::vector<u8> out(GetExeFsFileSize(file_name));
206 file.Seek(GetExeFsFileOffset(file_name), SEEK_SET);
207 file.ReadBytes(out.data(), GetExeFsFileSize(file_name));
208 return out;
209}
210
211AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath)
212 : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
213
214FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) {
215 file.Seek(0, SEEK_SET);
216 std::array<u8, 0x400> header_enc_array{};
217 if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400))
218 return FileType::Error;
219
220 // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
221 NcaHeader header{};
222 std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader));
223
224 if (IsValidNca(header) && header.content_type == NcaContentType::Program)
225 return FileType::NCA;
226
227 return FileType::Error;
228}
229
230ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
231 if (is_loaded) {
232 return ResultStatus::ErrorAlreadyLoaded;
233 }
234 if (!file.IsOpen()) {
235 return ResultStatus::Error;
236 }
237
238 nca = std::make_unique<Nca>();
239 ResultStatus result = nca->Load(std::move(file), filepath);
240 if (result != ResultStatus::Success) {
241 return result;
242 }
243
244 result = metadata.Load(nca->GetExeFsFile("main.npdm"));
245 if (result != ResultStatus::Success) {
246 return result;
247 }
248 metadata.Print();
249
250 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
251 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
252 return ResultStatus::ErrorUnsupportedArch;
253 }
254
255 VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
256 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
257 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
258 const VAddr load_addr = next_load_addr;
259 next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr);
260 if (next_load_addr) {
261 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
262 } else {
263 next_load_addr = load_addr;
264 }
265 }
266
267 process->program_id = metadata.GetTitleID();
268 process->svc_access_mask.set();
269 process->address_mappings = default_address_mappings;
270 process->resource_limit =
271 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
272 process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
273 metadata.GetMainThreadStackSize());
274
275 if (nca->GetRomFsSize() > 0)
276 Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
277 Service::FileSystem::Type::RomFS);
278
279 is_loaded = true;
280 return ResultStatus::Success;
281}
282
283ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
284 u64& size) {
285 if (nca->GetRomFsSize() == 0) {
286 LOG_DEBUG(Loader, "No RomFS available");
287 return ResultStatus::ErrorNotUsed;
288 }
289
290 romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
291
292 offset = nca->GetRomFsOffset();
293 size = nca->GetRomFsSize();
294
295 LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
296 LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
297
298 return ResultStatus::Success;
299}
300
301AppLoader_NCA::~AppLoader_NCA() = default;
302
303} // namespace Loader
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
new file mode 100644
index 000000000..3b6c451d0
--- /dev/null
+++ b/src/core/loader/nca.h
@@ -0,0 +1,49 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include "common/common_types.h"
9#include "core/file_sys/partition_filesystem.h"
10#include "core/file_sys/program_metadata.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/loader/loader.h"
13
14namespace Loader {
15
16class Nca;
17
18/// Loads an NCA file
19class AppLoader_NCA final : public AppLoader {
20public:
21 AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath);
22
23 /**
24 * Returns the type of the file
25 * @param file FileUtil::IOFile open file
26 * @param filepath Path of the file that we are opening.
27 * @return FileType found, or FileType::Error if this loader doesn't know it
28 */
29 static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
30
31 FileType GetFileType() override {
32 return IdentifyType(file, filepath);
33 }
34
35 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
36
37 ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
38 u64& size) override;
39
40 ~AppLoader_NCA();
41
42private:
43 std::string filepath;
44 FileSys::ProgramMetadata metadata;
45
46 std::unique_ptr<Nca> nca;
47};
48
49} // namespace Loader
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 01be9e217..7f84e4b1b 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -66,22 +66,13 @@ FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&)
66 return FileType::Error; 66 return FileType::Error;
67} 67}
68 68
69static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header, 69static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
70 int compressed_size) { 70 const NsoSegmentHeader& header) {
71 std::vector<u8> compressed_data;
72 compressed_data.resize(compressed_size);
73
74 file.Seek(header.offset, SEEK_SET);
75 if (compressed_size != file.ReadBytes(compressed_data.data(), compressed_size)) {
76 NGLOG_CRITICAL(Loader, "Failed to read {} NSO LZ4 compressed bytes", compressed_size);
77 return {};
78 }
79
80 std::vector<u8> uncompressed_data; 71 std::vector<u8> uncompressed_data;
81 uncompressed_data.resize(header.size); 72 uncompressed_data.resize(header.size);
82 const int bytes_uncompressed = LZ4_decompress_safe( 73 const int bytes_uncompressed = LZ4_decompress_safe(
83 reinterpret_cast<const char*>(compressed_data.data()), 74 reinterpret_cast<const char*>(compressed_data.data()),
84 reinterpret_cast<char*>(uncompressed_data.data()), compressed_size, header.size); 75 reinterpret_cast<char*>(uncompressed_data.data()), compressed_data.size(), header.size);
85 76
86 ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(), 77 ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(),
87 "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size()); 78 "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
@@ -89,10 +80,76 @@ static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeade
89 return uncompressed_data; 80 return uncompressed_data;
90} 81}
91 82
83static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header,
84 size_t compressed_size) {
85 std::vector<u8> compressed_data;
86 compressed_data.resize(compressed_size);
87
88 file.Seek(header.offset, SEEK_SET);
89 if (compressed_size != file.ReadBytes(compressed_data.data(), compressed_size)) {
90 LOG_CRITICAL(Loader, "Failed to read {} NSO LZ4 compressed bytes", compressed_size);
91 return {};
92 }
93
94 return DecompressSegment(compressed_data, header);
95}
96
92static constexpr u32 PageAlignSize(u32 size) { 97static constexpr u32 PageAlignSize(u32 size) {
93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 98 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
94} 99}
95 100
101VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data,
102 VAddr load_base) {
103 if (file_data.size() < sizeof(NsoHeader))
104 return {};
105
106 NsoHeader nso_header;
107 std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader));
108
109 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
110 return {};
111
112 // Build program image
113 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
114 std::vector<u8> program_image;
115 for (int i = 0; i < nso_header.segments.size(); ++i) {
116 std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]);
117 for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j)
118 compressed_data[j] = file_data[nso_header.segments[i].offset + j];
119 std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
120 program_image.resize(nso_header.segments[i].location);
121 program_image.insert(program_image.end(), data.begin(), data.end());
122 codeset->segments[i].addr = nso_header.segments[i].location;
123 codeset->segments[i].offset = nso_header.segments[i].location;
124 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
125 }
126
127 // MOD header pointer is at .text offset + 4
128 u32 module_offset;
129 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
130
131 // Read MOD header
132 ModHeader mod_header{};
133 // Default .bss to size in segment header if MOD0 section doesn't exist
134 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
135 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
136 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
137 if (has_mod_header) {
138 // Resize program image to include .bss section and page align each section
139 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
140 }
141 codeset->data.size += bss_size;
142 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
143 program_image.resize(image_size);
144
145 // Load codeset for current process
146 codeset->name = name;
147 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
148 Core::CurrentProcess()->LoadModule(codeset, load_base);
149
150 return load_base + image_size;
151}
152
96VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { 153VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
97 FileUtil::IOFile file(path, "rb"); 154 FileUtil::IOFile file(path, "rb");
98 if (!file.IsOpen()) { 155 if (!file.IsOpen()) {
@@ -158,7 +215,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
158 215
159 // Load module 216 // Load module
160 LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR); 217 LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR);
161 NGLOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR); 218 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR);
162 219
163 process->svc_access_mask.set(); 220 process->svc_access_mask.set();
164 process->address_mappings = default_address_mappings; 221 process->address_mappings = default_address_mappings;
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 1ae30a824..386f4d39a 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -29,6 +29,9 @@ public:
29 return IdentifyType(file, filepath); 29 return IdentifyType(file, filepath);
30 } 30 }
31 31
32 static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data,
33 VAddr load_base);
34
32 static VAddr LoadModule(const std::string& path, VAddr load_base); 35 static VAddr LoadModule(const std::string& path, VAddr load_base);
33 36
34 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 37 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 3b81acd63..190ccc25c 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -43,8 +43,8 @@ PageTable* GetCurrentPageTable() {
43} 43}
44 44
45static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { 45static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
46 NGLOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, 46 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
47 (base + size) * PAGE_SIZE); 47 (base + size) * PAGE_SIZE);
48 48
49 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, 49 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
50 FlushMode::FlushAndInvalidate); 50 FlushMode::FlushAndInvalidate);
@@ -173,7 +173,7 @@ T Read(const VAddr vaddr) {
173 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 173 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
174 switch (type) { 174 switch (type) {
175 case PageType::Unmapped: 175 case PageType::Unmapped:
176 NGLOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 176 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
177 return 0; 177 return 0;
178 case PageType::Memory: 178 case PageType::Memory:
179 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 179 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
@@ -205,8 +205,8 @@ void Write(const VAddr vaddr, const T data) {
205 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 205 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
206 switch (type) { 206 switch (type) {
207 case PageType::Unmapped: 207 case PageType::Unmapped:
208 NGLOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 208 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
209 static_cast<u32>(data), vaddr); 209 static_cast<u32>(data), vaddr);
210 return; 210 return;
211 case PageType::Memory: 211 case PageType::Memory:
212 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 212 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
@@ -241,6 +241,10 @@ bool IsValidVirtualAddress(const VAddr vaddr) {
241 return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr); 241 return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr);
242} 242}
243 243
244bool IsKernelVirtualAddress(const VAddr vaddr) {
245 return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
246}
247
244bool IsValidPhysicalAddress(const PAddr paddr) { 248bool IsValidPhysicalAddress(const PAddr paddr) {
245 return GetPhysicalPointer(paddr) != nullptr; 249 return GetPhysicalPointer(paddr) != nullptr;
246} 250}
@@ -255,7 +259,7 @@ u8* GetPointer(const VAddr vaddr) {
255 return GetPointerFromVMA(vaddr); 259 return GetPointerFromVMA(vaddr);
256 } 260 }
257 261
258 NGLOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); 262 LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr);
259 return nullptr; 263 return nullptr;
260} 264}
261 265
@@ -292,12 +296,12 @@ u8* GetPhysicalPointer(PAddr address) {
292 }); 296 });
293 297
294 if (area == std::end(memory_areas)) { 298 if (area == std::end(memory_areas)) {
295 NGLOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ 0x{:016X}", address); 299 LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ 0x{:016X}", address);
296 return nullptr; 300 return nullptr;
297 } 301 }
298 302
299 if (area->paddr_base == IO_AREA_PADDR) { 303 if (area->paddr_base == IO_AREA_PADDR) {
300 NGLOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr={:016X}", address); 304 LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr={:016X}", address);
301 return nullptr; 305 return nullptr;
302 } 306 }
303 307
@@ -344,9 +348,9 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached)
344 Core::System::GetInstance().GPU().memory_manager->GpuToCpuAddress(gpu_addr); 348 Core::System::GetInstance().GPU().memory_manager->GpuToCpuAddress(gpu_addr);
345 // The GPU <-> CPU virtual memory mapping is not 1:1 349 // The GPU <-> CPU virtual memory mapping is not 1:1
346 if (!maybe_vaddr) { 350 if (!maybe_vaddr) {
347 NGLOG_ERROR(HW_Memory, 351 LOG_ERROR(HW_Memory,
348 "Trying to flush a cached region to an invalid physical address {:016X}", 352 "Trying to flush a cached region to an invalid physical address {:016X}",
349 gpu_addr); 353 gpu_addr);
350 continue; 354 continue;
351 } 355 }
352 VAddr vaddr = *maybe_vaddr; 356 VAddr vaddr = *maybe_vaddr;
@@ -480,9 +484,9 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
480 484
481 switch (page_table.attributes[page_index]) { 485 switch (page_table.attributes[page_index]) {
482 case PageType::Unmapped: { 486 case PageType::Unmapped: {
483 NGLOG_ERROR(HW_Memory, 487 LOG_ERROR(HW_Memory,
484 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 488 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
485 current_vaddr, src_addr, size); 489 current_vaddr, src_addr, size);
486 std::memset(dest_buffer, 0, copy_amount); 490 std::memset(dest_buffer, 0, copy_amount);
487 break; 491 break;
488 } 492 }
@@ -544,9 +548,9 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
544 548
545 switch (page_table.attributes[page_index]) { 549 switch (page_table.attributes[page_index]) {
546 case PageType::Unmapped: { 550 case PageType::Unmapped: {
547 NGLOG_ERROR(HW_Memory, 551 LOG_ERROR(HW_Memory,
548 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 552 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
549 current_vaddr, dest_addr, size); 553 current_vaddr, dest_addr, size);
550 break; 554 break;
551 } 555 }
552 case PageType::Memory: { 556 case PageType::Memory: {
@@ -592,9 +596,9 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size
592 596
593 switch (page_table.attributes[page_index]) { 597 switch (page_table.attributes[page_index]) {
594 case PageType::Unmapped: { 598 case PageType::Unmapped: {
595 NGLOG_ERROR(HW_Memory, 599 LOG_ERROR(HW_Memory,
596 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 600 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
597 current_vaddr, dest_addr, size); 601 current_vaddr, dest_addr, size);
598 break; 602 break;
599 } 603 }
600 case PageType::Memory: { 604 case PageType::Memory: {
@@ -633,9 +637,9 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
633 637
634 switch (page_table.attributes[page_index]) { 638 switch (page_table.attributes[page_index]) {
635 case PageType::Unmapped: { 639 case PageType::Unmapped: {
636 NGLOG_ERROR(HW_Memory, 640 LOG_ERROR(HW_Memory,
637 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 641 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
638 current_vaddr, src_addr, size); 642 current_vaddr, src_addr, size);
639 ZeroBlock(process, dest_addr, copy_amount); 643 ZeroBlock(process, dest_addr, copy_amount);
640 break; 644 break;
641 } 645 }
@@ -688,7 +692,7 @@ boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) {
688PAddr VirtualToPhysicalAddress(const VAddr addr) { 692PAddr VirtualToPhysicalAddress(const VAddr addr) {
689 auto paddr = TryVirtualToPhysicalAddress(addr); 693 auto paddr = TryVirtualToPhysicalAddress(addr);
690 if (!paddr) { 694 if (!paddr) {
691 NGLOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:016X}", addr); 695 LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:016X}", addr);
692 // To help with debugging, set bit on address so that it's obviously invalid. 696 // To help with debugging, set bit on address so that it's obviously invalid.
693 return addr | 0x80000000; 697 return addr | 0x80000000;
694 } 698 }
diff --git a/src/core/memory.h b/src/core/memory.h
index 3f56a2c6a..8d5d017a4 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -188,6 +188,11 @@ enum : VAddr {
188 MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END, 188 MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END,
189 MAP_REGION_SIZE = 0x1000000000, 189 MAP_REGION_SIZE = 0x1000000000,
190 MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE, 190 MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE,
191
192 /// Kernel Virtual Address Range
193 KERNEL_REGION_VADDR = 0xFFFFFF8000000000,
194 KERNEL_REGION_SIZE = 0x7FFFE00000,
195 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
191}; 196};
192 197
193/// Currently active page table 198/// Currently active page table
@@ -197,6 +202,8 @@ PageTable* GetCurrentPageTable();
197/// Determines if the given VAddr is valid for the specified process. 202/// Determines if the given VAddr is valid for the specified process.
198bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); 203bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr);
199bool IsValidVirtualAddress(const VAddr addr); 204bool IsValidVirtualAddress(const VAddr addr);
205/// Determines if the given VAddr is a kernel address
206bool IsKernelVirtualAddress(const VAddr addr);
200 207
201bool IsValidPhysicalAddress(const PAddr addr); 208bool IsValidPhysicalAddress(const PAddr addr);
202 209
diff --git a/src/core/settings.h b/src/core/settings.h
index a7f1e5fa0..7150d9755 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -129,6 +129,7 @@ struct Values {
129 // Renderer 129 // Renderer
130 float resolution_factor; 130 float resolution_factor;
131 bool toggle_framelimit; 131 bool toggle_framelimit;
132 bool use_accurate_framebuffers;
132 133
133 float bg_red; 134 float bg_red;
134 float bg_green; 135 float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index a60aa1143..b9a603df3 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -42,14 +42,14 @@ u64 GetTelemetryId() {
42 if (FileUtil::Exists(filename)) { 42 if (FileUtil::Exists(filename)) {
43 FileUtil::IOFile file(filename, "rb"); 43 FileUtil::IOFile file(filename, "rb");
44 if (!file.IsOpen()) { 44 if (!file.IsOpen()) {
45 NGLOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 45 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
46 return {}; 46 return {};
47 } 47 }
48 file.ReadBytes(&telemetry_id, sizeof(u64)); 48 file.ReadBytes(&telemetry_id, sizeof(u64));
49 } else { 49 } else {
50 FileUtil::IOFile file(filename, "wb"); 50 FileUtil::IOFile file(filename, "wb");
51 if (!file.IsOpen()) { 51 if (!file.IsOpen()) {
52 NGLOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 52 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
53 return {}; 53 return {};
54 } 54 }
55 telemetry_id = GenerateTelemetryId(); 55 telemetry_id = GenerateTelemetryId();
@@ -65,7 +65,7 @@ u64 RegenerateTelemetryId() {
65 65
66 FileUtil::IOFile file(filename, "wb"); 66 FileUtil::IOFile file(filename, "wb");
67 if (!file.IsOpen()) { 67 if (!file.IsOpen()) {
68 NGLOG_ERROR(Core, "failed to open telemetry_id: {}", filename); 68 LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
69 return {}; 69 return {};
70 } 70 }
71 file.WriteBytes(&new_telemetry_id, sizeof(u64)); 71 file.WriteBytes(&new_telemetry_id, sizeof(u64));
@@ -161,6 +161,8 @@ TelemetrySession::TelemetrySession() {
161 Settings::values.resolution_factor); 161 Settings::values.resolution_factor);
162 AddField(Telemetry::FieldType::UserConfig, "Renderer_ToggleFramelimit", 162 AddField(Telemetry::FieldType::UserConfig, "Renderer_ToggleFramelimit",
163 Settings::values.toggle_framelimit); 163 Settings::values.toggle_framelimit);
164 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers",
165 Settings::values.use_accurate_framebuffers);
164 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 166 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
165 Settings::values.use_docked_mode); 167 Settings::values.use_docked_mode);
166} 168}
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp
index 2f848c994..af032f0c9 100644
--- a/src/core/tracer/recorder.cpp
+++ b/src/core/tracer/recorder.cpp
@@ -159,7 +159,7 @@ void Recorder::Finish(const std::string& filename) {
159 throw "Failed to write stream element"; 159 throw "Failed to write stream element";
160 } 160 }
161 } catch (const char* str) { 161 } catch (const char* str) {
162 NGLOG_ERROR(HW_GPU, "Writing CiTrace file failed: {}", str); 162 LOG_ERROR(HW_GPU, "Writing CiTrace file failed: {}", str);
163 } 163 }
164} 164}
165 165
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index 231a0f7af..8d117c2d4 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -32,7 +32,7 @@ public:
32 explicit SDLJoystick(int joystick_index) 32 explicit SDLJoystick(int joystick_index)
33 : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { 33 : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} {
34 if (!joystick) { 34 if (!joystick) {
35 NGLOG_ERROR(Input, "failed to open joystick {}", joystick_index); 35 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
36 } 36 }
37 } 37 }
38 38
@@ -204,7 +204,7 @@ public:
204 trigger_if_greater = false; 204 trigger_if_greater = false;
205 } else { 205 } else {
206 trigger_if_greater = true; 206 trigger_if_greater = true;
207 NGLOG_ERROR(Input, "Unknown direction '{}'", direction_name); 207 LOG_ERROR(Input, "Unknown direction '{}'", direction_name);
208 } 208 }
209 return std::make_unique<SDLAxisButton>(GetJoystick(joystick_index), axis, threshold, 209 return std::make_unique<SDLAxisButton>(GetJoystick(joystick_index), axis, threshold,
210 trigger_if_greater); 210 trigger_if_greater);
@@ -235,7 +235,7 @@ public:
235 235
236void Init() { 236void Init() {
237 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { 237 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
238 NGLOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 238 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
239 } else { 239 } else {
240 using namespace Input; 240 using namespace Input;
241 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); 241 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 281810357..c6431e722 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -9,6 +9,8 @@ add_library(video_core STATIC
9 engines/maxwell_3d.h 9 engines/maxwell_3d.h
10 engines/maxwell_compute.cpp 10 engines/maxwell_compute.cpp
11 engines/maxwell_compute.h 11 engines/maxwell_compute.h
12 engines/maxwell_dma.cpp
13 engines/maxwell_dma.h
12 engines/shader_bytecode.h 14 engines/shader_bytecode.h
13 gpu.cpp 15 gpu.cpp
14 gpu.h 16 gpu.h
@@ -39,6 +41,8 @@ add_library(video_core STATIC
39 renderer_opengl/maxwell_to_gl.h 41 renderer_opengl/maxwell_to_gl.h
40 renderer_opengl/renderer_opengl.cpp 42 renderer_opengl/renderer_opengl.cpp
41 renderer_opengl/renderer_opengl.h 43 renderer_opengl/renderer_opengl.h
44 textures/astc.cpp
45 textures/astc.h
42 textures/decoders.cpp 46 textures/decoders.cpp
43 textures/decoders.h 47 textures/decoders.h
44 textures/texture.h 48 textures/texture.h
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index d72d6f760..31ea3adad 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -16,6 +16,7 @@
16#include "video_core/engines/fermi_2d.h" 16#include "video_core/engines/fermi_2d.h"
17#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/engines/maxwell_compute.h" 18#include "video_core/engines/maxwell_compute.h"
19#include "video_core/engines/maxwell_dma.h"
19#include "video_core/gpu.h" 20#include "video_core/gpu.h"
20#include "video_core/renderer_base.h" 21#include "video_core/renderer_base.h"
21#include "video_core/video_core.h" 22#include "video_core/video_core.h"
@@ -28,21 +29,21 @@ enum class BufferMethods {
28}; 29};
29 30
30void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) { 31void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) {
31 NGLOG_WARNING(HW_GPU, 32 LOG_WARNING(HW_GPU,
32 "Processing method {:08X} on subchannel {} value " 33 "Processing method {:08X} on subchannel {} value "
33 "{:08X} remaining params {}", 34 "{:08X} remaining params {}",
34 method, subchannel, value, remaining_params); 35 method, subchannel, value, remaining_params);
35 36
36 if (method == static_cast<u32>(BufferMethods::BindObject)) { 37 if (method == static_cast<u32>(BufferMethods::BindObject)) {
37 // Bind the current subchannel to the desired engine id. 38 // Bind the current subchannel to the desired engine id.
38 NGLOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value); 39 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
39 bound_engines[subchannel] = static_cast<EngineID>(value); 40 bound_engines[subchannel] = static_cast<EngineID>(value);
40 return; 41 return;
41 } 42 }
42 43
43 if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) { 44 if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
44 // TODO(Subv): Research and implement these methods. 45 // TODO(Subv): Research and implement these methods.
45 NGLOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); 46 LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
46 return; 47 return;
47 } 48 }
48 49
@@ -60,8 +61,11 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
60 case EngineID::MAXWELL_COMPUTE_B: 61 case EngineID::MAXWELL_COMPUTE_B:
61 maxwell_compute->WriteReg(method, value); 62 maxwell_compute->WriteReg(method, value);
62 break; 63 break;
64 case EngineID::MAXWELL_DMA_COPY_A:
65 maxwell_dma->WriteReg(method, value);
66 break;
63 default: 67 default:
64 UNIMPLEMENTED(); 68 UNIMPLEMENTED_MSG("Unimplemented engine");
65 } 69 }
66} 70}
67 71
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index bbba8e380..9382a75e5 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -55,8 +55,10 @@ public:
55 virtual ~BreakPointObserver() { 55 virtual ~BreakPointObserver() {
56 auto context = context_weak.lock(); 56 auto context = context_weak.lock();
57 if (context) { 57 if (context) {
58 std::unique_lock<std::mutex> lock(context->breakpoint_mutex); 58 {
59 context->breakpoint_observers.remove(this); 59 std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
60 context->breakpoint_observers.remove(this);
61 }
60 62
61 // If we are the last observer to be destroyed, tell the debugger context that 63 // If we are the last observer to be destroyed, tell the debugger context that
62 // it is free to continue. In particular, this is required for a proper yuzu 64 // it is free to continue. In particular, this is required for a proper yuzu
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 6b9382f06..34053e393 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -26,8 +26,8 @@ void Fermi2D::WriteReg(u32 method, u32 value) {
26} 26}
27 27
28void Fermi2D::HandleSurfaceCopy() { 28void Fermi2D::HandleSurfaceCopy() {
29 NGLOG_WARNING(HW_GPU, "Requested a surface copy with operation {}", 29 LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}",
30 static_cast<u32>(regs.operation)); 30 static_cast<u32>(regs.operation));
31 31
32 const GPUVAddr source = regs.src.Address(); 32 const GPUVAddr source = regs.src.Address();
33 const GPUVAddr dest = regs.dst.Address(); 33 const GPUVAddr dest = regs.dst.Address();
@@ -47,6 +47,7 @@ void Fermi2D::HandleSurfaceCopy() {
47 47
48 if (regs.src.linear == regs.dst.linear) { 48 if (regs.src.linear == regs.dst.linear) {
49 // If the input layout and the output layout are the same, just perform a raw copy. 49 // If the input layout and the output layout are the same, just perform a raw copy.
50 ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
50 Memory::CopyBlock(dest_cpu, source_cpu, 51 Memory::CopyBlock(dest_cpu, source_cpu,
51 src_bytes_per_pixel * regs.dst.width * regs.dst.height); 52 src_bytes_per_pixel * regs.dst.width * regs.dst.height);
52 return; 53 return;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 86e9dc998..3bca16364 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -126,6 +126,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
126 DrawArrays(); 126 DrawArrays();
127 break; 127 break;
128 } 128 }
129 case MAXWELL3D_REG_INDEX(clear_buffers): {
130 ProcessClearBuffers();
131 break;
132 }
129 case MAXWELL3D_REG_INDEX(query.query_get): { 133 case MAXWELL3D_REG_INDEX(query.query_get): {
130 ProcessQueryGet(); 134 ProcessQueryGet();
131 break; 135 break;
@@ -207,8 +211,8 @@ void Maxwell3D::ProcessQueryGet() {
207} 211}
208 212
209void Maxwell3D::DrawArrays() { 213void Maxwell3D::DrawArrays() {
210 NGLOG_DEBUG(HW_GPU, "called, topology={}, count={}", 214 LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
211 static_cast<u32>(regs.draw.topology.Value()), regs.vertex_buffer.count); 215 regs.vertex_buffer.count);
212 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); 216 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
213 217
214 auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); 218 auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
@@ -328,8 +332,9 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
328 332
329 Texture::FullTextureInfo tex_info{}; 333 Texture::FullTextureInfo tex_info{};
330 // TODO(Subv): Use the shader to determine which textures are actually accessed. 334 // TODO(Subv): Use the shader to determine which textures are actually accessed.
331 tex_info.index = (current_texture - tex_info_buffer.address - TextureInfoOffset) / 335 tex_info.index =
332 sizeof(Texture::TextureHandle); 336 static_cast<u32>(current_texture - tex_info_buffer.address - TextureInfoOffset) /
337 sizeof(Texture::TextureHandle);
333 338
334 // Load the TIC data. 339 // Load the TIC data.
335 if (tex_handle.tic_id != 0) { 340 if (tex_handle.tic_id != 0) {
@@ -414,5 +419,13 @@ bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const {
414 UNREACHABLE(); 419 UNREACHABLE();
415} 420}
416 421
422void Maxwell3D::ProcessClearBuffers() {
423 ASSERT(regs.clear_buffers.R == regs.clear_buffers.G &&
424 regs.clear_buffers.R == regs.clear_buffers.B &&
425 regs.clear_buffers.R == regs.clear_buffers.A);
426
427 VideoCore::g_renderer->Rasterizer()->Clear();
428}
429
417} // namespace Engines 430} // namespace Engines
418} // namespace Tegra 431} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 2dc251205..5a7cf0107 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -280,6 +280,46 @@ public:
280 UnsignedInt = 0x2, 280 UnsignedInt = 0x2,
281 }; 281 };
282 282
283 enum class ComparisonOp : u32 {
284 // These values are used by Nouveau and most games, they correspond to the OpenGL token
285 // values for these operations.
286 Never = 0x200,
287 Less = 0x201,
288 Equal = 0x202,
289 LessEqual = 0x203,
290 Greater = 0x204,
291 NotEqual = 0x205,
292 GreaterEqual = 0x206,
293 Always = 0x207,
294
295 // These values are used by some games, they seem to be NV04 values.
296 NeverOld = 1,
297 LessOld = 2,
298 EqualOld = 3,
299 LessEqualOld = 4,
300 GreaterOld = 5,
301 NotEqualOld = 6,
302 GreaterEqualOld = 7,
303 AlwaysOld = 8,
304 };
305
306 struct Cull {
307 enum class FrontFace : u32 {
308 ClockWise = 0x0900,
309 CounterClockWise = 0x0901,
310 };
311
312 enum class CullFace : u32 {
313 Front = 0x0404,
314 Back = 0x0405,
315 FrontAndBack = 0x0408,
316 };
317
318 u32 enabled;
319 FrontFace front_face;
320 CullFace cull_face;
321 };
322
283 struct Blend { 323 struct Blend {
284 enum class Equation : u32 { 324 enum class Equation : u32 {
285 Add = 1, 325 Add = 1,
@@ -321,6 +361,24 @@ public:
321 INSERT_PADDING_WORDS(1); 361 INSERT_PADDING_WORDS(1);
322 }; 362 };
323 363
364 struct RenderTargetConfig {
365 u32 address_high;
366 u32 address_low;
367 u32 width;
368 u32 height;
369 Tegra::RenderTargetFormat format;
370 u32 block_dimensions;
371 u32 array_mode;
372 u32 layer_stride;
373 u32 base_layer;
374 INSERT_PADDING_WORDS(7);
375
376 GPUVAddr Address() const {
377 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
378 address_low);
379 }
380 };
381
324 union { 382 union {
325 struct { 383 struct {
326 INSERT_PADDING_WORDS(0x45); 384 INSERT_PADDING_WORDS(0x45);
@@ -333,23 +391,7 @@ public:
333 391
334 INSERT_PADDING_WORDS(0x1B8); 392 INSERT_PADDING_WORDS(0x1B8);
335 393
336 struct { 394 RenderTargetConfig rt[NumRenderTargets];
337 u32 address_high;
338 u32 address_low;
339 u32 width;
340 u32 height;
341 Tegra::RenderTargetFormat format;
342 u32 block_dimensions;
343 u32 array_mode;
344 u32 layer_stride;
345 u32 base_layer;
346 INSERT_PADDING_WORDS(7);
347
348 GPUVAddr Address() const {
349 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
350 address_low);
351 }
352 } rt[NumRenderTargets];
353 395
354 struct { 396 struct {
355 f32 scale_x; 397 f32 scale_x;
@@ -406,12 +448,17 @@ public:
406 u32 count; 448 u32 count;
407 } vertex_buffer; 449 } vertex_buffer;
408 450
409 INSERT_PADDING_WORDS(0x99); 451 INSERT_PADDING_WORDS(1);
452
453 float clear_color[4];
454 float clear_depth;
455
456 INSERT_PADDING_WORDS(0x93);
410 457
411 struct { 458 struct {
412 u32 address_high; 459 u32 address_high;
413 u32 address_low; 460 u32 address_low;
414 u32 format; 461 Tegra::DepthFormat format;
415 u32 block_dimensions; 462 u32 block_dimensions;
416 u32 layer_stride; 463 u32 layer_stride;
417 464
@@ -433,11 +480,23 @@ public:
433 }; 480 };
434 } rt_control; 481 } rt_control;
435 482
436 INSERT_PADDING_WORDS(0x31); 483 INSERT_PADDING_WORDS(0x2B);
484
485 u32 depth_test_enable;
486
487 INSERT_PADDING_WORDS(0x5);
437 488
438 u32 independent_blend_enable; 489 u32 independent_blend_enable;
439 490
440 INSERT_PADDING_WORDS(0x15); 491 u32 depth_write_enabled;
492
493 INSERT_PADDING_WORDS(0x7);
494
495 u32 d3d_cull_mode;
496
497 ComparisonOp depth_test_func;
498
499 INSERT_PADDING_WORDS(0xB);
441 500
442 struct { 501 struct {
443 u32 separate_alpha; 502 u32 separate_alpha;
@@ -453,7 +512,17 @@ public:
453 u32 enable[NumRenderTargets]; 512 u32 enable[NumRenderTargets];
454 } blend; 513 } blend;
455 514
456 INSERT_PADDING_WORDS(0x77); 515 INSERT_PADDING_WORDS(0xB);
516
517 union {
518 BitField<4, 1, u32> triangle_rast_flip;
519 } screen_y_control;
520
521 INSERT_PADDING_WORDS(0x21);
522
523 u32 vb_element_base;
524
525 INSERT_PADDING_WORDS(0x49);
457 526
458 struct { 527 struct {
459 u32 tsc_address_high; 528 u32 tsc_address_high;
@@ -479,7 +548,12 @@ public:
479 } 548 }
480 } tic; 549 } tic;
481 550
482 INSERT_PADDING_WORDS(0x22); 551 INSERT_PADDING_WORDS(0x21);
552
553 union {
554 BitField<2, 1, u32> coord_origin;
555 BitField<3, 10, u32> enable;
556 } point_coord_replace;
483 557
484 struct { 558 struct {
485 u32 code_address_high; 559 u32 code_address_high;
@@ -534,7 +608,27 @@ public:
534 } 608 }
535 } index_array; 609 } index_array;
536 610
537 INSERT_PADDING_WORDS(0xC7); 611 INSERT_PADDING_WORDS(0x7);
612
613 INSERT_PADDING_WORDS(0x46);
614
615 Cull cull;
616
617 INSERT_PADDING_WORDS(0x2B);
618
619 union {
620 u32 raw;
621 BitField<0, 1, u32> Z;
622 BitField<1, 1, u32> S;
623 BitField<2, 1, u32> R;
624 BitField<3, 1, u32> G;
625 BitField<4, 1, u32> B;
626 BitField<5, 1, u32> A;
627 BitField<6, 4, u32> RT;
628 BitField<10, 11, u32> layer;
629 } clear_buffers;
630
631 INSERT_PADDING_WORDS(0x4B);
538 632
539 struct { 633 struct {
540 u32 query_address_high; 634 u32 query_address_high;
@@ -716,6 +810,9 @@ private:
716 /// Handles writes to the macro uploading registers. 810 /// Handles writes to the macro uploading registers.
717 void ProcessMacroUpload(u32 data); 811 void ProcessMacroUpload(u32 data);
718 812
813 /// Handles a write to the CLEAR_BUFFERS register.
814 void ProcessClearBuffers();
815
719 /// Handles a write to the QUERY_GET register. 816 /// Handles a write to the QUERY_GET register.
720 void ProcessQueryGet(); 817 void ProcessQueryGet();
721 818
@@ -738,16 +835,27 @@ ASSERT_REG_POSITION(rt, 0x200);
738ASSERT_REG_POSITION(viewport_transform[0], 0x280); 835ASSERT_REG_POSITION(viewport_transform[0], 0x280);
739ASSERT_REG_POSITION(viewport, 0x300); 836ASSERT_REG_POSITION(viewport, 0x300);
740ASSERT_REG_POSITION(vertex_buffer, 0x35D); 837ASSERT_REG_POSITION(vertex_buffer, 0x35D);
838ASSERT_REG_POSITION(clear_color[0], 0x360);
839ASSERT_REG_POSITION(clear_depth, 0x364);
741ASSERT_REG_POSITION(zeta, 0x3F8); 840ASSERT_REG_POSITION(zeta, 0x3F8);
742ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458); 841ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
743ASSERT_REG_POSITION(rt_control, 0x487); 842ASSERT_REG_POSITION(rt_control, 0x487);
843ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
744ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); 844ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
845ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
846ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
847ASSERT_REG_POSITION(depth_test_func, 0x4C3);
745ASSERT_REG_POSITION(blend, 0x4CF); 848ASSERT_REG_POSITION(blend, 0x4CF);
849ASSERT_REG_POSITION(screen_y_control, 0x4EB);
850ASSERT_REG_POSITION(vb_element_base, 0x50D);
746ASSERT_REG_POSITION(tsc, 0x557); 851ASSERT_REG_POSITION(tsc, 0x557);
747ASSERT_REG_POSITION(tic, 0x55D); 852ASSERT_REG_POSITION(tic, 0x55D);
853ASSERT_REG_POSITION(point_coord_replace, 0x581);
748ASSERT_REG_POSITION(code_address, 0x582); 854ASSERT_REG_POSITION(code_address, 0x582);
749ASSERT_REG_POSITION(draw, 0x585); 855ASSERT_REG_POSITION(draw, 0x585);
750ASSERT_REG_POSITION(index_array, 0x5F2); 856ASSERT_REG_POSITION(index_array, 0x5F2);
857ASSERT_REG_POSITION(cull, 0x646);
858ASSERT_REG_POSITION(clear_buffers, 0x674);
751ASSERT_REG_POSITION(query, 0x6C0); 859ASSERT_REG_POSITION(query, 0x6C0);
752ASSERT_REG_POSITION(vertex_array[0], 0x700); 860ASSERT_REG_POSITION(vertex_array[0], 0x700);
753ASSERT_REG_POSITION(independent_blend, 0x780); 861ASSERT_REG_POSITION(independent_blend, 0x780);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
new file mode 100644
index 000000000..6e740713f
--- /dev/null
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -0,0 +1,73 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/memory.h"
6#include "video_core/engines/maxwell_dma.h"
7#include "video_core/textures/decoders.h"
8
9namespace Tegra {
10namespace Engines {
11
12MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
13
14void MaxwellDMA::WriteReg(u32 method, u32 value) {
15 ASSERT_MSG(method < Regs::NUM_REGS,
16 "Invalid MaxwellDMA register, increase the size of the Regs structure");
17
18 regs.reg_array[method] = value;
19
20#define MAXWELLDMA_REG_INDEX(field_name) \
21 (offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
22
23 switch (method) {
24 case MAXWELLDMA_REG_INDEX(exec): {
25 HandleCopy();
26 break;
27 }
28 }
29
30#undef MAXWELLDMA_REG_INDEX
31}
32
33void MaxwellDMA::HandleCopy() {
34 LOG_WARNING(HW_GPU, "Requested a DMA copy");
35
36 const GPUVAddr source = regs.src_address.Address();
37 const GPUVAddr dest = regs.dst_address.Address();
38
39 const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source);
40 const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest);
41
42 // TODO(Subv): Perform more research and implement all features of this engine.
43 ASSERT(regs.exec.enable_swizzle == 0);
44 ASSERT(regs.exec.enable_2d == 1);
45 ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
46 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
47 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
48 ASSERT(regs.src_params.pos_x == 0);
49 ASSERT(regs.src_params.pos_y == 0);
50 ASSERT(regs.dst_params.pos_x == 0);
51 ASSERT(regs.dst_params.pos_y == 0);
52
53 if (regs.exec.is_dst_linear == regs.exec.is_src_linear) {
54 Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count * regs.y_count);
55 return;
56 }
57
58 u8* src_buffer = Memory::GetPointer(source_cpu);
59 u8* dst_buffer = Memory::GetPointer(dest_cpu);
60
61 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
62 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
63 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer,
64 dst_buffer, true, regs.src_params.BlockHeight());
65 } else {
66 // If the input is linear and the output is tiled, swizzle the input and copy it over.
67 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer,
68 src_buffer, false, regs.dst_params.BlockHeight());
69 }
70}
71
72} // namespace Engines
73} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
new file mode 100644
index 000000000..905749bde
--- /dev/null
+++ b/src/video_core/engines/maxwell_dma.h
@@ -0,0 +1,155 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/assert.h"
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h"
14
15namespace Tegra {
16namespace Engines {
17
18class MaxwellDMA final {
19public:
20 explicit MaxwellDMA(MemoryManager& memory_manager);
21 ~MaxwellDMA() = default;
22
23 /// Write the value to the register identified by method.
24 void WriteReg(u32 method, u32 value);
25
26 struct Regs {
27 static constexpr size_t NUM_REGS = 0x1D6;
28
29 struct Parameters {
30 union {
31 BitField<0, 4, u32> block_depth;
32 BitField<4, 4, u32> block_height;
33 BitField<8, 4, u32> block_width;
34 };
35 u32 size_x;
36 u32 size_y;
37 u32 size_z;
38 u32 pos_z;
39 union {
40 BitField<0, 16, u32> pos_x;
41 BitField<16, 16, u32> pos_y;
42 };
43
44 u32 BlockHeight() const {
45 return 1 << block_height;
46 }
47 };
48
49 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
50
51 enum class CopyMode : u32 {
52 None = 0,
53 Unk1 = 1,
54 Unk2 = 2,
55 };
56
57 enum class QueryMode : u32 {
58 None = 0,
59 Short = 1,
60 Long = 2,
61 };
62
63 enum class QueryIntr : u32 {
64 None = 0,
65 Block = 1,
66 NonBlock = 2,
67 };
68
69 union {
70 struct {
71 INSERT_PADDING_WORDS(0xC0);
72
73 struct {
74 union {
75 BitField<0, 2, CopyMode> copy_mode;
76 BitField<2, 1, u32> flush;
77
78 BitField<3, 2, QueryMode> query_mode;
79 BitField<5, 2, QueryIntr> query_intr;
80
81 BitField<7, 1, u32> is_src_linear;
82 BitField<8, 1, u32> is_dst_linear;
83
84 BitField<9, 1, u32> enable_2d;
85 BitField<10, 1, u32> enable_swizzle;
86 };
87 } exec;
88
89 INSERT_PADDING_WORDS(0x3F);
90
91 struct {
92 u32 address_high;
93 u32 address_low;
94
95 GPUVAddr Address() const {
96 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
97 address_low);
98 }
99 } src_address;
100
101 struct {
102 u32 address_high;
103 u32 address_low;
104
105 GPUVAddr Address() const {
106 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
107 address_low);
108 }
109 } dst_address;
110
111 u32 src_pitch;
112 u32 dst_pitch;
113 u32 x_count;
114 u32 y_count;
115
116 INSERT_PADDING_WORDS(0xBB);
117
118 Parameters dst_params;
119
120 INSERT_PADDING_WORDS(1);
121
122 Parameters src_params;
123
124 INSERT_PADDING_WORDS(0x13);
125 };
126 std::array<u32, NUM_REGS> reg_array;
127 };
128 } regs{};
129
130 MemoryManager& memory_manager;
131
132private:
133 /// Performs the copy from the source buffer to the destination buffer as configured in the
134 /// registers.
135 void HandleCopy();
136};
137
138#define ASSERT_REG_POSITION(field_name, position) \
139 static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
140 "Field " #field_name " has invalid position")
141
142ASSERT_REG_POSITION(exec, 0xC0);
143ASSERT_REG_POSITION(src_address, 0x100);
144ASSERT_REG_POSITION(dst_address, 0x102);
145ASSERT_REG_POSITION(src_pitch, 0x104);
146ASSERT_REG_POSITION(dst_pitch, 0x105);
147ASSERT_REG_POSITION(x_count, 0x106);
148ASSERT_REG_POSITION(y_count, 0x107);
149ASSERT_REG_POSITION(dst_params, 0x1C3);
150ASSERT_REG_POSITION(src_params, 0x1CA);
151
152#undef ASSERT_REG_POSITION
153
154} // namespace Engines
155} // namespace Tegra
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index ec8dbd370..2bc1782ad 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -142,6 +142,7 @@ enum class PredCondition : u64 {
142 GreaterThan = 4, 142 GreaterThan = 4,
143 NotEqual = 5, 143 NotEqual = 5,
144 GreaterEqual = 6, 144 GreaterEqual = 6,
145 NotEqualWithNan = 13,
145 // TODO(Subv): Other condition types 146 // TODO(Subv): Other condition types
146}; 147};
147 148
@@ -165,7 +166,7 @@ enum class SubOp : u64 {
165 Lg2 = 0x3, 166 Lg2 = 0x3,
166 Rcp = 0x4, 167 Rcp = 0x4,
167 Rsq = 0x5, 168 Rsq = 0x5,
168 Min = 0x8, 169 Sqrt = 0x8,
169}; 170};
170 171
171enum class F2iRoundingOp : u64 { 172enum class F2iRoundingOp : u64 {
@@ -193,6 +194,13 @@ enum class UniformType : u64 {
193 Double = 5, 194 Double = 5,
194}; 195};
195 196
197enum class IMinMaxExchange : u64 {
198 None = 0,
199 XLo = 1,
200 XMed = 2,
201 XHi = 3,
202};
203
196union Instruction { 204union Instruction {
197 Instruction& operator=(const Instruction& instr) { 205 Instruction& operator=(const Instruction& instr) {
198 value = instr.value; 206 value = instr.value;
@@ -209,20 +217,19 @@ union Instruction {
209 } pred; 217 } pred;
210 BitField<19, 1, u64> negate_pred; 218 BitField<19, 1, u64> negate_pred;
211 BitField<20, 8, Register> gpr20; 219 BitField<20, 8, Register> gpr20;
212 BitField<20, 7, SubOp> sub_op; 220 BitField<20, 4, SubOp> sub_op;
213 BitField<28, 8, Register> gpr28; 221 BitField<28, 8, Register> gpr28;
214 BitField<39, 8, Register> gpr39; 222 BitField<39, 8, Register> gpr39;
215 BitField<48, 16, u64> opcode; 223 BitField<48, 16, u64> opcode;
216 BitField<50, 1, u64> saturate_a;
217 224
218 union { 225 union {
219 BitField<20, 19, u64> imm20_19; 226 BitField<20, 19, u64> imm20_19;
220 BitField<20, 32, u64> imm20_32; 227 BitField<20, 32, s64> imm20_32;
221 BitField<45, 1, u64> negate_b; 228 BitField<45, 1, u64> negate_b;
222 BitField<46, 1, u64> abs_a; 229 BitField<46, 1, u64> abs_a;
223 BitField<48, 1, u64> negate_a; 230 BitField<48, 1, u64> negate_a;
224 BitField<49, 1, u64> abs_b; 231 BitField<49, 1, u64> abs_b;
225 BitField<50, 1, u64> abs_d; 232 BitField<50, 1, u64> saturate_d;
226 BitField<56, 1, u64> negate_imm; 233 BitField<56, 1, u64> negate_imm;
227 234
228 union { 235 union {
@@ -231,10 +238,18 @@ union Instruction {
231 } fmnmx; 238 } fmnmx;
232 239
233 union { 240 union {
241 BitField<39, 1, u64> invert_a;
242 BitField<40, 1, u64> invert_b;
243 BitField<41, 2, LogicOperation> operation;
244 BitField<44, 2, u64> unk44;
245 BitField<48, 3, Pred> pred48;
246 } lop;
247
248 union {
234 BitField<53, 2, LogicOperation> operation; 249 BitField<53, 2, LogicOperation> operation;
235 BitField<55, 1, u64> invert_a; 250 BitField<55, 1, u64> invert_a;
236 BitField<56, 1, u64> invert_b; 251 BitField<56, 1, u64> invert_b;
237 } lop; 252 } lop32i;
238 253
239 float GetImm20_19() const { 254 float GetImm20_19() const {
240 float result{}; 255 float result{};
@@ -247,7 +262,7 @@ union Instruction {
247 262
248 float GetImm20_32() const { 263 float GetImm20_32() const {
249 float result{}; 264 float result{};
250 u32 imm{static_cast<u32>(imm20_32)}; 265 s32 imm{static_cast<s32>(imm20_32)};
251 std::memcpy(&result, &imm, sizeof(imm)); 266 std::memcpy(&result, &imm, sizeof(imm));
252 return result; 267 return result;
253 } 268 }
@@ -271,6 +286,18 @@ union Instruction {
271 } alu_integer; 286 } alu_integer;
272 287
273 union { 288 union {
289 BitField<39, 3, u64> pred;
290 BitField<42, 1, u64> negate_pred;
291 BitField<43, 2, IMinMaxExchange> exchange;
292 BitField<48, 1, u64> is_signed;
293 } imnmx;
294
295 union {
296 BitField<54, 1, u64> saturate;
297 BitField<56, 1, u64> negate_a;
298 } iadd32i;
299
300 union {
274 BitField<20, 8, u64> shift_position; 301 BitField<20, 8, u64> shift_position;
275 BitField<28, 8, u64> shift_length; 302 BitField<28, 8, u64> shift_length;
276 BitField<48, 1, u64> negate_b; 303 BitField<48, 1, u64> negate_b;
@@ -316,6 +343,19 @@ union Instruction {
316 } isetp; 343 } isetp;
317 344
318 union { 345 union {
346 BitField<0, 3, u64> pred0;
347 BitField<3, 3, u64> pred3;
348 BitField<12, 3, u64> pred12;
349 BitField<15, 1, u64> neg_pred12;
350 BitField<24, 2, PredOperation> cond;
351 BitField<29, 3, u64> pred29;
352 BitField<32, 1, u64> neg_pred29;
353 BitField<39, 3, u64> pred39;
354 BitField<42, 1, u64> neg_pred39;
355 BitField<45, 2, PredOperation> op;
356 } psetp;
357
358 union {
319 BitField<39, 3, u64> pred39; 359 BitField<39, 3, u64> pred39;
320 BitField<42, 1, u64> neg_pred; 360 BitField<42, 1, u64> neg_pred;
321 BitField<43, 1, u64> neg_a; 361 BitField<43, 1, u64> neg_a;
@@ -339,7 +379,8 @@ union Instruction {
339 } iset; 379 } iset;
340 380
341 union { 381 union {
342 BitField<10, 2, Register::Size> size; 382 BitField<8, 2, Register::Size> dest_size;
383 BitField<10, 2, Register::Size> src_size;
343 BitField<12, 1, u64> is_output_signed; 384 BitField<12, 1, u64> is_output_signed;
344 BitField<13, 1, u64> is_input_signed; 385 BitField<13, 1, u64> is_input_signed;
345 BitField<41, 2, u64> selector; 386 BitField<41, 2, u64> selector;
@@ -359,7 +400,7 @@ union Instruction {
359 BitField<31, 4, u64> component_mask; 400 BitField<31, 4, u64> component_mask;
360 401
361 bool IsComponentEnabled(size_t component) const { 402 bool IsComponentEnabled(size_t component) const {
362 return ((1 << component) & component_mask) != 0; 403 return ((1ull << component) & component_mask) != 0;
363 } 404 }
364 } tex; 405 } tex;
365 406
@@ -378,7 +419,7 @@ union Instruction {
378 419
379 ASSERT(component_mask_selector < mask.size()); 420 ASSERT(component_mask_selector < mask.size());
380 421
381 return ((1 << component) & mask[component_mask_selector]) != 0; 422 return ((1ull << component) & mask[component_mask_selector]) != 0;
382 } 423 }
383 } texs; 424 } texs;
384 425
@@ -424,6 +465,8 @@ public:
424 enum class Id { 465 enum class Id {
425 KIL, 466 KIL,
426 SSY, 467 SSY,
468 SYNC,
469 DEPBAR,
427 BFE_C, 470 BFE_C,
428 BFE_R, 471 BFE_R,
429 BFE_IMM, 472 BFE_IMM,
@@ -451,6 +494,7 @@ public:
451 IADD_C, 494 IADD_C,
452 IADD_R, 495 IADD_R,
453 IADD_IMM, 496 IADD_IMM,
497 IADD32I,
454 ISCADD_C, // Scale and Add 498 ISCADD_C, // Scale and Add
455 ISCADD_R, 499 ISCADD_R,
456 ISCADD_IMM, 500 ISCADD_IMM,
@@ -470,6 +514,9 @@ public:
470 I2I_C, 514 I2I_C,
471 I2I_R, 515 I2I_R,
472 I2I_IMM, 516 I2I_IMM,
517 LOP_C,
518 LOP_R,
519 LOP_IMM,
473 LOP32I, 520 LOP32I,
474 MOV_C, 521 MOV_C,
475 MOV_R, 522 MOV_R,
@@ -509,12 +556,14 @@ public:
509 enum class Type { 556 enum class Type {
510 Trivial, 557 Trivial,
511 Arithmetic, 558 Arithmetic,
559 ArithmeticImmediate,
512 ArithmeticInteger, 560 ArithmeticInteger,
561 ArithmeticIntegerImmediate,
513 Bfe, 562 Bfe,
514 Logic,
515 Shift, 563 Shift,
516 Ffma, 564 Ffma,
517 Flow, 565 Flow,
566 Synch,
518 Memory, 567 Memory,
519 FloatSet, 568 FloatSet,
520 FloatSetPredicate, 569 FloatSetPredicate,
@@ -619,10 +668,12 @@ private:
619 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 668 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
620 INST("111000101001----", Id::SSY, Type::Flow, "SSY"), 669 INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
621 INST("111000100100----", Id::BRA, Type::Flow, "BRA"), 670 INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
671 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
672 INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"),
622 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 673 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
623 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 674 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
624 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 675 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
625 INST("1100000000111---", Id::TEX, Type::Memory, "TEX"), 676 INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
626 INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), 677 INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"),
627 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), 678 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
628 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), 679 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
@@ -638,10 +689,11 @@ private:
638 INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"), 689 INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
639 INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"), 690 INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
640 INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"), 691 INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),
641 INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"), 692 INST("00011110--------", Id::FMUL32_IMM, Type::ArithmeticImmediate, "FMUL32_IMM"),
642 INST("0100110000010---", Id::IADD_C, Type::ArithmeticInteger, "IADD_C"), 693 INST("0100110000010---", Id::IADD_C, Type::ArithmeticInteger, "IADD_C"),
643 INST("0101110000010---", Id::IADD_R, Type::ArithmeticInteger, "IADD_R"), 694 INST("0101110000010---", Id::IADD_R, Type::ArithmeticInteger, "IADD_R"),
644 INST("0011100-00010---", Id::IADD_IMM, Type::ArithmeticInteger, "IADD_IMM"), 695 INST("0011100-00010---", Id::IADD_IMM, Type::ArithmeticInteger, "IADD_IMM"),
696 INST("0001110---------", Id::IADD32I, Type::ArithmeticIntegerImmediate, "IADD32I"),
645 INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"), 697 INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"),
646 INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"), 698 INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"),
647 INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "ISCADD_IMM"), 699 INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "ISCADD_IMM"),
@@ -658,17 +710,20 @@ private:
658 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), 710 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
659 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), 711 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
660 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), 712 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
661 INST("000000010000----", Id::MOV32_IMM, Type::Arithmetic, "MOV32_IMM"), 713 INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
662 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), 714 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
663 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), 715 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
664 INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), 716 INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"),
665 INST("0100110000100---", Id::IMNMX_C, Type::Arithmetic, "FMNMX_IMM"), 717 INST("0100110000100---", Id::IMNMX_C, Type::ArithmeticInteger, "IMNMX_C"),
666 INST("0101110000100---", Id::IMNMX_R, Type::Arithmetic, "FMNMX_IMM"), 718 INST("0101110000100---", Id::IMNMX_R, Type::ArithmeticInteger, "IMNMX_R"),
667 INST("0011100-00100---", Id::IMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), 719 INST("0011100-00100---", Id::IMNMX_IMM, Type::ArithmeticInteger, "IMNMX_IMM"),
668 INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), 720 INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
669 INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), 721 INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
670 INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), 722 INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
671 INST("000001----------", Id::LOP32I, Type::Logic, "LOP32I"), 723 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
724 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
725 INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"),
726 INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"),
672 INST("0100110001001---", Id::SHL_C, Type::Shift, "SHL_C"), 727 INST("0100110001001---", Id::SHL_C, Type::Shift, "SHL_C"),
673 INST("0101110001001---", Id::SHL_R, Type::Shift, "SHL_R"), 728 INST("0101110001001---", Id::SHL_R, Type::Shift, "SHL_R"),
674 INST("0011100-01001---", Id::SHL_IMM, Type::Shift, "SHL_IMM"), 729 INST("0011100-01001---", Id::SHL_IMM, Type::Shift, "SHL_IMM"),
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 66351fe6e..e36483145 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -5,6 +5,7 @@
5#include "video_core/engines/fermi_2d.h" 5#include "video_core/engines/fermi_2d.h"
6#include "video_core/engines/maxwell_3d.h" 6#include "video_core/engines/maxwell_3d.h"
7#include "video_core/engines/maxwell_compute.h" 7#include "video_core/engines/maxwell_compute.h"
8#include "video_core/engines/maxwell_dma.h"
8#include "video_core/gpu.h" 9#include "video_core/gpu.h"
9 10
10namespace Tegra { 11namespace Tegra {
@@ -14,6 +15,7 @@ GPU::GPU() {
14 maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager); 15 maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
15 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); 16 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
16 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 17 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
18 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
17} 19}
18 20
19GPU::~GPU() = default; 21GPU::~GPU() = default;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 5852b9619..cc5ca656e 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -16,6 +16,7 @@ namespace Tegra {
16enum class RenderTargetFormat : u32 { 16enum class RenderTargetFormat : u32 {
17 NONE = 0x0, 17 NONE = 0x0,
18 RGBA32_FLOAT = 0xC0, 18 RGBA32_FLOAT = 0xC0,
19 RGBA32_UINT = 0xC2,
19 RGBA16_FLOAT = 0xCA, 20 RGBA16_FLOAT = 0xCA,
20 RGB10_A2_UNORM = 0xD1, 21 RGB10_A2_UNORM = 0xD1,
21 RGBA8_UNORM = 0xD5, 22 RGBA8_UNORM = 0xD5,
@@ -23,6 +24,15 @@ enum class RenderTargetFormat : u32 {
23 R11G11B10_FLOAT = 0xE0, 24 R11G11B10_FLOAT = 0xE0,
24}; 25};
25 26
27enum class DepthFormat : u32 {
28 Z32_FLOAT = 0xA,
29 Z16_UNORM = 0x13,
30 S8_Z24_UNORM = 0x14,
31 Z24_X8_UNORM = 0x15,
32 Z24_S8_UNORM = 0x16,
33 Z24_C8_UNORM = 0x18,
34};
35
26/// Returns the number of bytes per pixel of each rendertarget format. 36/// Returns the number of bytes per pixel of each rendertarget format.
27u32 RenderTargetBytesPerPixel(RenderTargetFormat format); 37u32 RenderTargetBytesPerPixel(RenderTargetFormat format);
28 38
@@ -63,6 +73,7 @@ namespace Engines {
63class Fermi2D; 73class Fermi2D;
64class Maxwell3D; 74class Maxwell3D;
65class MaxwellCompute; 75class MaxwellCompute;
76class MaxwellDMA;
66} // namespace Engines 77} // namespace Engines
67 78
68enum class EngineID { 79enum class EngineID {
@@ -103,6 +114,8 @@ private:
103 std::unique_ptr<Engines::Fermi2D> fermi_2d; 114 std::unique_ptr<Engines::Fermi2D> fermi_2d;
104 /// Compute engine 115 /// Compute engine
105 std::unique_ptr<Engines::MaxwellCompute> maxwell_compute; 116 std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
117 /// DMA engine
118 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
106}; 119};
107 120
108} // namespace Tegra 121} // namespace Tegra
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 5cefce9fc..2f814a184 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -100,9 +100,9 @@ boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
100 100
101boost::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { 101boost::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
102 VAddr base_addr = PageSlot(gpu_addr); 102 VAddr base_addr = PageSlot(gpu_addr);
103 ASSERT(base_addr != static_cast<u64>(PageStatus::Unmapped));
104 103
105 if (base_addr == static_cast<u64>(PageStatus::Allocated)) { 104 if (base_addr == static_cast<u64>(PageStatus::Allocated) ||
105 base_addr == static_cast<u64>(PageStatus::Unmapped)) {
106 return {}; 106 return {};
107 } 107 }
108 108
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index f0e48a802..499e84b89 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -19,6 +19,9 @@ public:
19 /// Draw the current batch of vertex arrays 19 /// Draw the current batch of vertex arrays
20 virtual void DrawArrays() = 0; 20 virtual void DrawArrays() = 0;
21 21
22 /// Clear the current framebuffer
23 virtual void Clear() = 0;
24
22 /// Notify rasterizer that the specified Maxwell register has been changed 25 /// Notify rasterizer that the specified Maxwell register has been changed
23 virtual void NotifyMaxwellRegisterChanged(u32 method) = 0; 26 virtual void NotifyMaxwellRegisterChanged(u32 method) = 0;
24 27
@@ -51,9 +54,8 @@ public:
51 } 54 }
52 55
53 /// Attempt to use a faster method to display the framebuffer to screen 56 /// Attempt to use a faster method to display the framebuffer to screen
54 virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer, 57 virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
55 VAddr framebuffer_addr, u32 pixel_stride, 58 u32 pixel_stride, ScreenInfo& screen_info) {
56 ScreenInfo& screen_info) {
57 return false; 59 return false;
58 } 60 }
59 61
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 6f05f24a0..ea138d402 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -112,7 +112,7 @@ RasterizerOpenGL::RasterizerOpenGL() {
112 112
113 glEnable(GL_BLEND); 113 glEnable(GL_BLEND);
114 114
115 NGLOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); 115 LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!");
116} 116}
117 117
118RasterizerOpenGL::~RasterizerOpenGL() { 118RasterizerOpenGL::~RasterizerOpenGL() {
@@ -146,7 +146,6 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
146 u64 size = end - start + 1; 146 u64 size = end - start + 1;
147 147
148 // Copy vertex array data 148 // Copy vertex array data
149 res_cache.FlushRegion(start, size, nullptr);
150 Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size); 149 Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size);
151 150
152 // Bind the vertex array to the buffer at the current offset. 151 // Bind the vertex array to the buffer at the current offset.
@@ -166,9 +165,9 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
166 // assume every shader uses them all. 165 // assume every shader uses them all.
167 for (unsigned index = 0; index < 16; ++index) { 166 for (unsigned index = 0; index < 16; ++index) {
168 auto& attrib = regs.vertex_attrib_format[index]; 167 auto& attrib = regs.vertex_attrib_format[index];
169 NGLOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", 168 LOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
170 index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), 169 index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
171 attrib.offset.Value(), attrib.IsNormalized()); 170 attrib.offset.Value(), attrib.IsNormalized());
172 171
173 auto& buffer = regs.vertex_array[attrib.buffer]; 172 auto& buffer = regs.vertex_array[attrib.buffer];
174 ASSERT(buffer.IsEnabled()); 173 ASSERT(buffer.IsEnabled());
@@ -197,8 +196,8 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
197 ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!"); 196 ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!");
198 197
199 // Next available bindpoints to use when uploading the const buffers and textures to the GLSL 198 // Next available bindpoints to use when uploading the const buffers and textures to the GLSL
200 // shaders. 199 // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
201 u32 current_constbuffer_bindpoint = 0; 200 u32 current_constbuffer_bindpoint = uniform_buffers.size();
202 u32 current_texture_bindpoint = 0; 201 u32 current_texture_bindpoint = 0;
203 202
204 for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) { 203 for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) {
@@ -252,8 +251,8 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
252 break; 251 break;
253 } 252 }
254 default: 253 default:
255 NGLOG_CRITICAL(HW_GPU, "Unimplemented shader index={}, enable={}, offset=0x{:08X}", 254 LOG_CRITICAL(HW_GPU, "Unimplemented shader index={}, enable={}, offset=0x{:08X}", index,
256 index, shader_config.enable.Value(), shader_config.offset); 255 shader_config.enable.Value(), shader_config.offset);
257 UNREACHABLE(); 256 UNREACHABLE();
258 } 257 }
259 258
@@ -298,17 +297,16 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
298 return true; 297 return true;
299} 298}
300 299
301void RasterizerOpenGL::DrawArrays() { 300std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb,
302 if (accelerate_draw == AccelDraw::Disabled) 301 bool using_depth_fb) {
303 return;
304
305 MICROPROFILE_SCOPE(OpenGL_Drawing);
306 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 302 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
307 303
308 // TODO(bunnei): Implement these 304 // Sync the depth test state before configuring the framebuffer surfaces.
305 SyncDepthTestState();
306
307 // TODO(bunnei): Implement this
309 const bool has_stencil = false; 308 const bool has_stencil = false;
310 const bool using_color_fb = true; 309
311 const bool using_depth_fb = false;
312 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; 310 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
313 311
314 const bool write_color_fb = 312 const bool write_color_fb =
@@ -325,35 +323,21 @@ void RasterizerOpenGL::DrawArrays() {
325 std::tie(color_surface, depth_surface, surfaces_rect) = 323 std::tie(color_surface, depth_surface, surfaces_rect) =
326 res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect); 324 res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect);
327 325
328 const u16 res_scale = color_surface != nullptr
329 ? color_surface->res_scale
330 : (depth_surface == nullptr ? 1u : depth_surface->res_scale);
331
332 MathUtil::Rectangle<u32> draw_rect{ 326 MathUtil::Rectangle<u32> draw_rect{
327 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left,
328 surfaces_rect.left, surfaces_rect.right)), // Left
329 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top,
330 surfaces_rect.bottom, surfaces_rect.top)), // Top
331 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right,
332 surfaces_rect.left, surfaces_rect.right)), // Right
333 static_cast<u32>( 333 static_cast<u32>(
334 std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale, 334 std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom,
335 surfaces_rect.left, surfaces_rect.right)), // Left 335 surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
336 static_cast<u32>(
337 std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top * res_scale,
338 surfaces_rect.bottom, surfaces_rect.top)), // Top
339 static_cast<u32>(
340 std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right * res_scale,
341 surfaces_rect.left, surfaces_rect.right)), // Right
342 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
343 viewport_rect.bottom * res_scale,
344 surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
345 336
346 // Bind the framebuffer surfaces 337 // Bind the framebuffer surfaces
347 BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); 338 BindFramebufferSurfaces(color_surface, depth_surface, has_stencil);
348 339
349 // Sync the viewport 340 SyncViewport(surfaces_rect);
350 SyncViewport(surfaces_rect, res_scale);
351
352 // Sync the blend state registers
353 SyncBlendState();
354
355 // TODO(bunnei): Sync framebuffer_scale uniform here
356 // TODO(bunnei): Sync scissorbox uniform(s) here
357 341
358 // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable 342 // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable
359 // scissor test to prevent drawing outside of the framebuffer region 343 // scissor test to prevent drawing outside of the framebuffer region
@@ -364,6 +348,66 @@ void RasterizerOpenGL::DrawArrays() {
364 state.scissor.height = draw_rect.GetHeight(); 348 state.scissor.height = draw_rect.GetHeight();
365 state.Apply(); 349 state.Apply();
366 350
351 // Only return the surface to be marked as dirty if writing to it is enabled.
352 return std::make_pair(write_color_fb ? color_surface : nullptr,
353 write_depth_fb ? depth_surface : nullptr);
354}
355
356void RasterizerOpenGL::Clear() {
357 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
358
359 bool use_color_fb = false;
360 bool use_depth_fb = false;
361
362 GLbitfield clear_mask = 0;
363 if (regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B &&
364 regs.clear_buffers.A) {
365 clear_mask |= GL_COLOR_BUFFER_BIT;
366 use_color_fb = true;
367 }
368 if (regs.clear_buffers.Z) {
369 clear_mask |= GL_DEPTH_BUFFER_BIT;
370 use_depth_fb = true;
371 }
372
373 if (clear_mask == 0)
374 return;
375
376 auto [dirty_color_surface, dirty_depth_surface] =
377 ConfigureFramebuffers(use_color_fb, use_depth_fb);
378
379 // TODO(Subv): Support clearing only partial colors.
380 glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2],
381 regs.clear_color[3]);
382 glClearDepth(regs.clear_depth);
383
384 glClear(clear_mask);
385
386 // Mark framebuffer surfaces as dirty
387 if (dirty_color_surface != nullptr) {
388 res_cache.MarkSurfaceAsDirty(dirty_color_surface);
389 }
390 if (dirty_depth_surface != nullptr) {
391 res_cache.MarkSurfaceAsDirty(dirty_depth_surface);
392 }
393}
394
395void RasterizerOpenGL::DrawArrays() {
396 if (accelerate_draw == AccelDraw::Disabled)
397 return;
398
399 MICROPROFILE_SCOPE(OpenGL_Drawing);
400 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
401
402 auto [dirty_color_surface, dirty_depth_surface] =
403 ConfigureFramebuffers(true, regs.zeta.Address() != 0);
404
405 SyncBlendState();
406 SyncCullMode();
407
408 // TODO(bunnei): Sync framebuffer_scale uniform here
409 // TODO(bunnei): Sync scissorbox uniform(s) here
410
367 // Draw the vertex batch 411 // Draw the vertex batch
368 const bool is_indexed = accelerate_draw == AccelDraw::Indexed; 412 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
369 const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; 413 const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()};
@@ -420,14 +464,16 @@ void RasterizerOpenGL::DrawArrays() {
420 464
421 const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)}; 465 const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)};
422 if (is_indexed) { 466 if (is_indexed) {
423 const GLint index_min{static_cast<GLint>(regs.index_array.first)}; 467 const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)};
424 const GLint index_max{static_cast<GLint>(regs.index_array.first + regs.index_array.count)}; 468
425 glDrawRangeElementsBaseVertex(primitive_mode, index_min, index_max, regs.index_array.count, 469 // Adjust the index buffer offset so it points to the first desired index.
426 MaxwellToGL::IndexFormat(regs.index_array.format), 470 index_buffer_offset += regs.index_array.first * regs.index_array.FormatSizeInBytes();
427 reinterpret_cast<const void*>(index_buffer_offset), 471
428 -index_min); 472 glDrawElementsBaseVertex(primitive_mode, regs.index_array.count,
473 MaxwellToGL::IndexFormat(regs.index_array.format),
474 reinterpret_cast<const void*>(index_buffer_offset), base_vertex);
429 } else { 475 } else {
430 glDrawArrays(primitive_mode, 0, regs.vertex_buffer.count); 476 glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count);
431 } 477 }
432 478
433 // Disable scissor test 479 // Disable scissor test
@@ -437,24 +483,16 @@ void RasterizerOpenGL::DrawArrays() {
437 483
438 // Unbind textures for potential future use as framebuffer attachments 484 // Unbind textures for potential future use as framebuffer attachments
439 for (auto& texture_unit : state.texture_units) { 485 for (auto& texture_unit : state.texture_units) {
440 texture_unit.texture_2d = 0; 486 texture_unit.Unbind();
441 } 487 }
442 state.Apply(); 488 state.Apply();
443 489
444 // Mark framebuffer surfaces as dirty 490 // Mark framebuffer surfaces as dirty
445 MathUtil::Rectangle<u32> draw_rect_unscaled{ 491 if (dirty_color_surface != nullptr) {
446 draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale, 492 res_cache.MarkSurfaceAsDirty(dirty_color_surface);
447 draw_rect.bottom / res_scale};
448
449 if (color_surface != nullptr && write_color_fb) {
450 auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled);
451 res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
452 color_surface);
453 } 493 }
454 if (depth_surface != nullptr && write_depth_fb) { 494 if (dirty_depth_surface != nullptr) {
455 auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled); 495 res_cache.MarkSurfaceAsDirty(dirty_depth_surface);
456 res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
457 depth_surface);
458 } 496 }
459} 497}
460 498
@@ -462,7 +500,7 @@ void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {}
462 500
463void RasterizerOpenGL::FlushAll() { 501void RasterizerOpenGL::FlushAll() {
464 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 502 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
465 res_cache.FlushAll(); 503 res_cache.FlushRegion(0, Kernel::VMManager::MAX_ADDRESS);
466} 504}
467 505
468void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) { 506void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) {
@@ -472,13 +510,13 @@ void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) {
472 510
473void RasterizerOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, u64 size) { 511void RasterizerOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, u64 size) {
474 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 512 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
475 res_cache.InvalidateRegion(addr, size, nullptr); 513 res_cache.InvalidateRegion(addr, size);
476} 514}
477 515
478void RasterizerOpenGL::FlushAndInvalidateRegion(Tegra::GPUVAddr addr, u64 size) { 516void RasterizerOpenGL::FlushAndInvalidateRegion(Tegra::GPUVAddr addr, u64 size) {
479 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 517 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
480 res_cache.FlushRegion(addr, size); 518 res_cache.FlushRegion(addr, size);
481 res_cache.InvalidateRegion(addr, size, nullptr); 519 res_cache.InvalidateRegion(addr, size);
482} 520}
483 521
484bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) { 522bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) {
@@ -497,45 +535,28 @@ bool RasterizerOpenGL::AccelerateFill(const void* config) {
497 return true; 535 return true;
498} 536}
499 537
500bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer, 538bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
501 VAddr framebuffer_addr, u32 pixel_stride, 539 VAddr framebuffer_addr, u32 pixel_stride,
502 ScreenInfo& screen_info) { 540 ScreenInfo& screen_info) {
503 if (framebuffer_addr == 0) { 541 if (!framebuffer_addr) {
504 return false; 542 return {};
505 } 543 }
544
506 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 545 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
507 546
508 SurfaceParams src_params; 547 const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)};
509 src_params.cpu_addr = framebuffer_addr; 548 if (!surface) {
510 src_params.addr = res_cache.TryFindFramebufferGpuAddress(framebuffer_addr).get_value_or(0); 549 return {};
511 src_params.width = std::min(framebuffer.width, pixel_stride);
512 src_params.height = framebuffer.height;
513 src_params.stride = pixel_stride;
514 src_params.is_tiled = true;
515 src_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
516 src_params.pixel_format =
517 SurfaceParams::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format);
518 src_params.component_type =
519 SurfaceParams::ComponentTypeFromGPUPixelFormat(framebuffer.pixel_format);
520 src_params.UpdateParams();
521
522 MathUtil::Rectangle<u32> src_rect;
523 Surface src_surface;
524 std::tie(src_surface, src_rect) =
525 res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true);
526
527 if (src_surface == nullptr) {
528 return false;
529 } 550 }
530 551
531 u32 scaled_width = src_surface->GetScaledWidth(); 552 // Verify that the cached surface is the same size and format as the requested framebuffer
532 u32 scaled_height = src_surface->GetScaledHeight(); 553 const auto& params{surface->GetSurfaceParams()};
554 const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)};
555 ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
556 ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
557 ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different");
533 558
534 screen_info.display_texcoords = MathUtil::Rectangle<float>( 559 screen_info.display_texture = surface->Texture().handle;
535 (float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
536 (float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
537
538 screen_info.display_texture = src_surface->texture.handle;
539 560
540 return true; 561 return true;
541} 562}
@@ -608,32 +629,44 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
608 629
609 boost::optional<VAddr> addr = gpu.memory_manager->GpuToCpuAddress(buffer.address); 630 boost::optional<VAddr> addr = gpu.memory_manager->GpuToCpuAddress(buffer.address);
610 631
611 std::vector<u8> data; 632 size_t size = 0;
633
612 if (used_buffer.IsIndirect()) { 634 if (used_buffer.IsIndirect()) {
613 // Buffer is accessed indirectly, so upload the entire thing 635 // Buffer is accessed indirectly, so upload the entire thing
614 data.resize(buffer.size * sizeof(float)); 636 size = buffer.size * sizeof(float);
637
638 if (size > MaxConstbufferSize) {
639 LOG_ERROR(HW_GPU, "indirect constbuffer size {} exceeds maximum {}", size,
640 MaxConstbufferSize);
641 size = MaxConstbufferSize;
642 }
615 } else { 643 } else {
616 // Buffer is accessed directly, upload just what we use 644 // Buffer is accessed directly, upload just what we use
617 data.resize(used_buffer.GetSize() * sizeof(float)); 645 size = used_buffer.GetSize() * sizeof(float);
618 } 646 }
619 647
648 // Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
649 // UBO alignment requirements.
650 size = Common::AlignUp(size, sizeof(GLvec4));
651 ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
652
653 std::vector<u8> data(size);
620 Memory::ReadBlock(*addr, data.data(), data.size()); 654 Memory::ReadBlock(*addr, data.data(), data.size());
621 655
622 glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer_draw_state.ssbo); 656 glBindBuffer(GL_UNIFORM_BUFFER, buffer_draw_state.ssbo);
623 glBufferData(GL_SHADER_STORAGE_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); 657 glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW);
624 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 658 glBindBuffer(GL_UNIFORM_BUFFER, 0);
625 659
626 // Now configure the bindpoint of the buffer inside the shader 660 // Now configure the bindpoint of the buffer inside the shader
627 std::string buffer_name = used_buffer.GetName(); 661 std::string buffer_name = used_buffer.GetName();
628 GLuint index = 662 GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, buffer_name.c_str());
629 glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, buffer_name.c_str());
630 if (index != -1) 663 if (index != -1)
631 glShaderStorageBlockBinding(program, index, buffer_draw_state.bindpoint); 664 glUniformBlockBinding(program, index, buffer_draw_state.bindpoint);
632 } 665 }
633 666
634 state.Apply(); 667 state.Apply();
635 668
636 return current_bindpoint + entries.size(); 669 return current_bindpoint + static_cast<u32>(entries.size());
637} 670}
638 671
639u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, 672u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
@@ -653,16 +686,23 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program,
653 686
654 // Bind the uniform to the sampler. 687 // Bind the uniform to the sampler.
655 GLint uniform = glGetUniformLocation(program, entry.GetName().c_str()); 688 GLint uniform = glGetUniformLocation(program, entry.GetName().c_str());
656 ASSERT(uniform != -1); 689 if (uniform == -1) {
690 continue;
691 }
692
657 glProgramUniform1i(program, uniform, current_bindpoint); 693 glProgramUniform1i(program, uniform, current_bindpoint);
658 694
659 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); 695 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
660 ASSERT(texture.enabled); 696
697 if (!texture.enabled) {
698 state.texture_units[current_bindpoint].texture_2d = 0;
699 continue;
700 }
661 701
662 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); 702 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
663 Surface surface = res_cache.GetTextureSurface(texture); 703 Surface surface = res_cache.GetTextureSurface(texture);
664 if (surface != nullptr) { 704 if (surface != nullptr) {
665 state.texture_units[current_bindpoint].texture_2d = surface->texture.handle; 705 state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle;
666 state.texture_units[current_bindpoint].swizzle.r = 706 state.texture_units[current_bindpoint].swizzle.r =
667 MaxwellToGL::SwizzleSource(texture.tic.x_source); 707 MaxwellToGL::SwizzleSource(texture.tic.x_source);
668 state.texture_units[current_bindpoint].swizzle.g = 708 state.texture_units[current_bindpoint].swizzle.g =
@@ -679,7 +719,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program,
679 719
680 state.Apply(); 720 state.Apply();
681 721
682 return current_unit + entries.size(); 722 return current_unit + static_cast<u32>(entries.size());
683} 723}
684 724
685void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, 725void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
@@ -688,16 +728,16 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
688 state.Apply(); 728 state.Apply();
689 729
690 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 730 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
691 color_surface != nullptr ? color_surface->texture.handle : 0, 0); 731 color_surface != nullptr ? color_surface->Texture().handle : 0, 0);
692 if (depth_surface != nullptr) { 732 if (depth_surface != nullptr) {
693 if (has_stencil) { 733 if (has_stencil) {
694 // attach both depth and stencil 734 // attach both depth and stencil
695 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 735 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
696 depth_surface->texture.handle, 0); 736 depth_surface->Texture().handle, 0);
697 } else { 737 } else {
698 // attach depth 738 // attach depth
699 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 739 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
700 depth_surface->texture.handle, 0); 740 depth_surface->Texture().handle, 0);
701 // clear stencil attachment 741 // clear stencil attachment
702 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); 742 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
703 } 743 }
@@ -708,14 +748,14 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
708 } 748 }
709} 749}
710 750
711void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale) { 751void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) {
712 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 752 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
713 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; 753 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
714 754
715 state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left * res_scale; 755 state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left;
716 state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale; 756 state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom;
717 state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth() * res_scale); 757 state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth());
718 state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight() * res_scale); 758 state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight());
719} 759}
720 760
721void RasterizerOpenGL::SyncClipEnabled() { 761void RasterizerOpenGL::SyncClipEnabled() {
@@ -727,7 +767,27 @@ void RasterizerOpenGL::SyncClipCoef() {
727} 767}
728 768
729void RasterizerOpenGL::SyncCullMode() { 769void RasterizerOpenGL::SyncCullMode() {
730 UNREACHABLE(); 770 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
771
772 state.cull.enabled = regs.cull.enabled != 0;
773
774 if (state.cull.enabled) {
775 state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
776 state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
777
778 const bool flip_triangles{regs.screen_y_control.triangle_rast_flip == 0 ||
779 regs.viewport_transform[0].scale_y < 0.0f};
780
781 // If the GPU is configured to flip the rasterized triangles, then we need to flip the
782 // notion of front and back. Note: We flip the triangles when the value of the register is 0
783 // because OpenGL already does it for us.
784 if (flip_triangles) {
785 if (state.cull.front_face == GL_CCW)
786 state.cull.front_face = GL_CW;
787 else if (state.cull.front_face == GL_CW)
788 state.cull.front_face = GL_CCW;
789 }
790 }
731} 791}
732 792
733void RasterizerOpenGL::SyncDepthScale() { 793void RasterizerOpenGL::SyncDepthScale() {
@@ -738,9 +798,20 @@ void RasterizerOpenGL::SyncDepthOffset() {
738 UNREACHABLE(); 798 UNREACHABLE();
739} 799}
740 800
801void RasterizerOpenGL::SyncDepthTestState() {
802 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
803
804 state.depth.test_enabled = regs.depth_test_enable != 0;
805 state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
806
807 if (!state.depth.test_enabled)
808 return;
809
810 state.depth.test_func = MaxwellToGL::ComparisonOp(regs.depth_test_func);
811}
812
741void RasterizerOpenGL::SyncBlendState() { 813void RasterizerOpenGL::SyncBlendState() {
742 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 814 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
743 ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented");
744 815
745 // TODO(Subv): Support more than just render target 0. 816 // TODO(Subv): Support more than just render target 0.
746 state.blend.enabled = regs.blend.enable[0] != 0; 817 state.blend.enabled = regs.blend.enable[0] != 0;
@@ -748,6 +819,7 @@ void RasterizerOpenGL::SyncBlendState() {
748 if (!state.blend.enabled) 819 if (!state.blend.enabled)
749 return; 820 return;
750 821
822 ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented");
751 ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented"); 823 ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented");
752 state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb); 824 state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb);
753 state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb); 825 state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b7c8cf843..c406142e4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <memory> 9#include <memory>
10#include <utility>
10#include <vector> 11#include <vector>
11#include <glad/glad.h> 12#include <glad/glad.h>
12#include "common/common_types.h" 13#include "common/common_types.h"
@@ -28,6 +29,7 @@ public:
28 ~RasterizerOpenGL() override; 29 ~RasterizerOpenGL() override;
29 30
30 void DrawArrays() override; 31 void DrawArrays() override;
32 void Clear() override;
31 void NotifyMaxwellRegisterChanged(u32 method) override; 33 void NotifyMaxwellRegisterChanged(u32 method) override;
32 void FlushAll() override; 34 void FlushAll() override;
33 void FlushRegion(Tegra::GPUVAddr addr, u64 size) override; 35 void FlushRegion(Tegra::GPUVAddr addr, u64 size) override;
@@ -54,6 +56,11 @@ public:
54 OGLShader shader; 56 OGLShader shader;
55 }; 57 };
56 58
59 /// Maximum supported size that a constbuffer can have in bytes.
60 static constexpr size_t MaxConstbufferSize = 0x10000;
61 static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
62 "The maximum size of a constbuffer must be a multiple of the size of GLvec4");
63
57private: 64private:
58 class SamplerInfo { 65 class SamplerInfo {
59 public: 66 public:
@@ -76,6 +83,10 @@ private:
76 u32 border_color_a; 83 u32 border_color_a;
77 }; 84 };
78 85
86 /// Configures the color and depth framebuffer states and returns the dirty <Color, Depth>
87 /// surfaces if writing was enabled.
88 std::pair<Surface, Surface> ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb);
89
79 /// Binds the framebuffer color and depth surface 90 /// Binds the framebuffer color and depth surface
80 void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, 91 void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface,
81 bool has_stencil); 92 bool has_stencil);
@@ -104,7 +115,7 @@ private:
104 u32 current_unit, const std::vector<GLShader::SamplerEntry>& entries); 115 u32 current_unit, const std::vector<GLShader::SamplerEntry>& entries);
105 116
106 /// Syncs the viewport to match the guest state 117 /// Syncs the viewport to match the guest state
107 void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale); 118 void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect);
108 119
109 /// Syncs the clip enabled status to match the guest state 120 /// Syncs the clip enabled status to match the guest state
110 void SyncClipEnabled(); 121 void SyncClipEnabled();
@@ -121,6 +132,9 @@ private:
121 /// Syncs the depth offset to match the guest state 132 /// Syncs the depth offset to match the guest state
122 void SyncDepthOffset(); 133 void SyncDepthOffset();
123 134
135 /// Syncs the depth test state to match the guest state
136 void SyncDepthTestState();
137
124 /// Syncs the blend state to match the guest state 138 /// Syncs the blend state to match the guest state
125 void SyncBlendState(); 139 void SyncBlendState();
126 140
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index ff48a2669..323ff7408 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -1,36 +1,23 @@
1// Copyright 2015 Citra Emulator Project 1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <atomic>
7#include <cstring>
8#include <iterator>
9#include <memory>
10#include <utility>
11#include <vector>
12#include <boost/optional.hpp>
13#include <boost/range/iterator_range.hpp>
14#include <glad/glad.h> 6#include <glad/glad.h>
7
15#include "common/alignment.h" 8#include "common/alignment.h"
16#include "common/bit_field.h" 9#include "common/assert.h"
17#include "common/color.h"
18#include "common/logging/log.h"
19#include "common/math_util.h"
20#include "common/microprofile.h" 10#include "common/microprofile.h"
21#include "common/scope_exit.h" 11#include "common/scope_exit.h"
22#include "core/core.h" 12#include "core/core.h"
23#include "core/frontend/emu_window.h"
24#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
25#include "core/hle/kernel/vm_manager.h"
26#include "core/memory.h" 14#include "core/memory.h"
27#include "core/settings.h" 15#include "core/settings.h"
28#include "video_core/engines/maxwell_3d.h" 16#include "video_core/engines/maxwell_3d.h"
29#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 17#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
30#include "video_core/renderer_opengl/gl_state.h" 18#include "video_core/textures/astc.h"
31#include "video_core/textures/decoders.h" 19#include "video_core/textures/decoders.h"
32#include "video_core/utils.h" 20#include "video_core/utils.h"
33#include "video_core/video_core.h"
34 21
35using SurfaceType = SurfaceParams::SurfaceType; 22using SurfaceType = SurfaceParams::SurfaceType;
36using PixelFormat = SurfaceParams::PixelFormat; 23using PixelFormat = SurfaceParams::PixelFormat;
@@ -40,89 +27,178 @@ struct FormatTuple {
40 GLint internal_format; 27 GLint internal_format;
41 GLenum format; 28 GLenum format;
42 GLenum type; 29 GLenum type;
30 ComponentType component_type;
43 bool compressed; 31 bool compressed;
44}; 32};
45 33
34/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
35 const Tegra::Texture::FullTextureInfo& config) {
36
37 SurfaceParams params{};
38 params.addr = config.tic.Address();
39 params.is_tiled = config.tic.IsTiled();
40 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
41 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format);
42 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
43 params.type = GetFormatType(params.pixel_format);
44 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
45 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
46 params.unaligned_height = config.tic.Height();
47 params.size_in_bytes = params.SizeInBytes();
48 return params;
49}
50
51/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(
52 const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config) {
53
54 SurfaceParams params{};
55 params.addr = config.Address();
56 params.is_tiled = true;
57 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
58 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
59 params.component_type = ComponentTypeFromRenderTarget(config.format);
60 params.type = GetFormatType(params.pixel_format);
61 params.width = config.width;
62 params.height = config.height;
63 params.unaligned_height = config.height;
64 params.size_in_bytes = params.SizeInBytes();
65 return params;
66}
67
68/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
69 const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config, Tegra::GPUVAddr zeta_address,
70 Tegra::DepthFormat format) {
71
72 SurfaceParams params{};
73 params.addr = zeta_address;
74 params.is_tiled = true;
75 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
76 params.pixel_format = PixelFormatFromDepthFormat(format);
77 params.component_type = ComponentTypeFromDepthFormat(format);
78 params.type = GetFormatType(params.pixel_format);
79 params.size_in_bytes = params.SizeInBytes();
80 params.width = config.width;
81 params.height = config.height;
82 params.unaligned_height = config.height;
83 params.size_in_bytes = params.SizeInBytes();
84 return params;
85}
86
46static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ 87static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
47 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // ABGR8 88 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8
48 {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, false}, // B5G6R5 89 {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5
49 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false}, // A2B10G10R10 90 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm,
50 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5 91 false}, // A2B10G10R10
51 {GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8 92 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5
52 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F 93 {GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8
53 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F 94 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
54 {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1 95 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
55 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23 96 false}, // R11FG11FB10F
56 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45 97 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
57 {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1 98 {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
99 true}, // DXT1
100 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
101 true}, // DXT23
102 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
103 true}, // DXT45
104 {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1
105 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
106 true}, // BC7U
107 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
108
109 // DepthStencil formats
110 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
111 false}, // Z24S8
112 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
113 false}, // S8Z24
114 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
58}}; 115}};
59 116
60static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { 117static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
61 const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); 118 ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
62 if (type == SurfaceType::ColorTexture) { 119 auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)];
63 ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); 120 ASSERT(component_type == format.component_type);
64 // For now only UNORM components are supported, or either R11FG11FB10F or RGBA16F which are
65 // type FLOAT
66 ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F ||
67 pixel_format == PixelFormat::R11FG11FB10F);
68 return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
69 } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
70 // TODO(Subv): Implement depth formats
71 ASSERT_MSG(false, "Unimplemented");
72 }
73 121
74 UNREACHABLE(); 122 return format;
75 return {};
76} 123}
77 124
78template <typename Map, typename Interval> 125VAddr SurfaceParams::GetCpuAddr() const {
79constexpr auto RangeFromInterval(Map& map, const Interval& interval) { 126 const auto& gpu = Core::System::GetInstance().GPU();
80 return boost::make_iterator_range(map.equal_range(interval)); 127 return *gpu.memory_manager->GpuToCpuAddress(addr);
81} 128}
82 129
83static u16 GetResolutionScaleFactor() { 130static bool IsPixelFormatASTC(PixelFormat format) {
84 return static_cast<u16>(!Settings::values.resolution_factor 131 switch (format) {
85 ? VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio() 132 case PixelFormat::ASTC_2D_4X4:
86 : Settings::values.resolution_factor); 133 return true;
134 default:
135 return false;
136 }
137}
138
139static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
140 switch (format) {
141 case PixelFormat::ASTC_2D_4X4:
142 return {4, 4};
143 default:
144 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
145 UNREACHABLE();
146 }
147}
148
149MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
150 u32 actual_height{unaligned_height};
151 if (IsPixelFormatASTC(pixel_format)) {
152 // ASTC formats must stop at the ATSC block size boundary
153 actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second);
154 }
155 return {0, actual_height, width, 0};
87} 156}
88 157
89template <bool morton_to_gl, PixelFormat format> 158template <bool morton_to_gl, PixelFormat format>
90void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr base, 159void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr addr) {
91 Tegra::GPUVAddr start, Tegra::GPUVAddr end) {
92 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 160 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
93 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 161 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
94 const auto& gpu = Core::System::GetInstance().GPU(); 162 const auto& gpu = Core::System::GetInstance().GPU();
95 163
96 if (morton_to_gl) { 164 if (morton_to_gl) {
97 auto data = Tegra::Texture::UnswizzleTexture( 165 if (SurfaceParams::GetFormatType(format) == SurfaceType::ColorTexture) {
98 *gpu.memory_manager->GpuToCpuAddress(base), 166 auto data = Tegra::Texture::UnswizzleTexture(
99 SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height); 167 *gpu.memory_manager->GpuToCpuAddress(addr),
100 std::memcpy(gl_buffer, data.data(), data.size()); 168 SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height);
169 std::memcpy(gl_buffer, data.data(), data.size());
170 } else {
171 auto data = Tegra::Texture::UnswizzleDepthTexture(
172 *gpu.memory_manager->GpuToCpuAddress(addr),
173 SurfaceParams::DepthFormatFromPixelFormat(format), stride, height, block_height);
174 std::memcpy(gl_buffer, data.data(), data.size());
175 }
101 } else { 176 } else {
102 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check 177 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
103 // the configuration for this and perform more generic un/swizzle 178 // check the configuration for this and perform more generic un/swizzle
104 NGLOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 179 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
105 VideoCore::MortonCopyPixels128( 180 VideoCore::MortonCopyPixels128(
106 stride, height, bytes_per_pixel, gl_bytes_per_pixel, 181 stride, height, bytes_per_pixel, gl_bytes_per_pixel,
107 Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(base)), gl_buffer, 182 Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer,
108 morton_to_gl); 183 morton_to_gl);
109 } 184 }
110} 185}
111 186
112static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr, 187static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
113 Tegra::GPUVAddr),
114 SurfaceParams::MaxPixelFormat> 188 SurfaceParams::MaxPixelFormat>
115 morton_to_gl_fns = { 189 morton_to_gl_fns = {
116 MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, 190 MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
117 MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>, 191 MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>,
118 MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>, 192 MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>,
119 MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::DXT1>, 193 MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>,
120 MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>, 194 MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
121 MortonCopy<true, PixelFormat::DXN1>, 195 MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
196 MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
197 MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
198 MortonCopy<true, PixelFormat::Z32F>,
122}; 199};
123 200
124static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr, 201static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
125 Tegra::GPUVAddr),
126 SurfaceParams::MaxPixelFormat> 202 SurfaceParams::MaxPixelFormat>
127 gl_to_morton_fns = { 203 gl_to_morton_fns = {
128 MortonCopy<false, PixelFormat::ABGR8>, 204 MortonCopy<false, PixelFormat::ABGR8>,
@@ -132,11 +208,17 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra:
132 MortonCopy<false, PixelFormat::R8>, 208 MortonCopy<false, PixelFormat::R8>,
133 MortonCopy<false, PixelFormat::RGBA16F>, 209 MortonCopy<false, PixelFormat::RGBA16F>,
134 MortonCopy<false, PixelFormat::R11FG11FB10F>, 210 MortonCopy<false, PixelFormat::R11FG11FB10F>,
135 // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1 formats is not yet supported 211 MortonCopy<false, PixelFormat::RGBA32UI>,
212 // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1/BC7U formats is not yet supported
213 nullptr,
136 nullptr, 214 nullptr,
137 nullptr, 215 nullptr,
138 nullptr, 216 nullptr,
139 nullptr, 217 nullptr,
218 MortonCopy<false, PixelFormat::ABGR8>,
219 MortonCopy<false, PixelFormat::Z24S8>,
220 MortonCopy<false, PixelFormat::S8Z24>,
221 MortonCopy<false, PixelFormat::Z32F>,
140}; 222};
141 223
142// Allocate an uninitialized texture of appropriate size and format for the surface 224// Allocate an uninitialized texture of appropriate size and format for the surface
@@ -166,374 +248,144 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup
166 cur_state.Apply(); 248 cur_state.Apply();
167} 249}
168 250
169static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, 251CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) {
170 const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, 252 texture.Create();
171 GLuint read_fb_handle, GLuint draw_fb_handle) { 253 const auto& rect{params.GetRect()};
172 254 AllocateSurfaceTexture(texture.handle,
173 glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, dst_tex, 255 GetFormatTuple(params.pixel_format, params.component_type),
174 GL_TEXTURE_2D, 0, dst_rect.left, dst_rect.bottom, 0, src_rect.GetWidth(), 256 rect.GetWidth(), rect.GetHeight());
175 src_rect.GetHeight(), 0); 257}
176 return true; 258
177} 259static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
178 260 union S8Z24 {
179static bool FillSurface(const Surface& surface, const u8* fill_data, 261 BitField<0, 24, u32> z24;
180 const MathUtil::Rectangle<u32>& fill_rect, GLuint draw_fb_handle) { 262 BitField<24, 8, u32> s8;
181 UNREACHABLE(); 263 };
182 return {}; 264 static_assert(sizeof(S8Z24) == 4, "S8Z24 is incorrect size");
183} 265
184 266 union Z24S8 {
185SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const { 267 BitField<0, 8, u32> s8;
186 SurfaceParams params = *this; 268 BitField<8, 24, u32> z24;
187 const u32 tiled_size = is_tiled ? 8 : 1; 269 };
188 const u64 stride_tiled_bytes = BytesInPixels(stride * tiled_size); 270 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
189 Tegra::GPUVAddr aligned_start = 271
190 addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes); 272 S8Z24 input_pixel{};
191 Tegra::GPUVAddr aligned_end = 273 Z24S8 output_pixel{};
192 addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes); 274 for (size_t y = 0; y < height; ++y) {
193 275 for (size_t x = 0; x < width; ++x) {
194 if (aligned_end - aligned_start > stride_tiled_bytes) { 276 const size_t offset{y * width + x};
195 params.addr = aligned_start; 277 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24));
196 params.height = static_cast<u32>((aligned_end - aligned_start) / BytesInPixels(stride)); 278 output_pixel.s8.Assign(input_pixel.s8);
197 } else { 279 output_pixel.z24.Assign(input_pixel.z24);
198 // 1 row 280 std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8));
199 ASSERT(aligned_end - aligned_start == stride_tiled_bytes);
200 const u64 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1);
201 aligned_start =
202 addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment);
203 aligned_end =
204 addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment);
205 params.addr = aligned_start;
206 params.width = static_cast<u32>(PixelsInBytes(aligned_end - aligned_start) / tiled_size);
207 params.stride = params.width;
208 params.height = tiled_size;
209 }
210 params.UpdateParams();
211
212 return params;
213}
214
215SurfaceInterval SurfaceParams::GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const {
216 if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) {
217 return {};
218 }
219
220 if (is_tiled) {
221 unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8;
222 unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8;
223 unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8;
224 unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8;
225 }
226
227 const u32 stride_tiled = !is_tiled ? stride : stride * 8;
228
229 const u32 pixel_offset =
230 stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) +
231 unscaled_rect.left;
232
233 const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth();
234
235 return {addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels)};
236}
237
238MathUtil::Rectangle<u32> SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const {
239 const u32 begin_pixel_index = static_cast<u32>(PixelsInBytes(sub_surface.addr - addr));
240
241 if (is_tiled) {
242 const int x0 = (begin_pixel_index % (stride * 8)) / 8;
243 const int y0 = (begin_pixel_index / (stride * 8)) * 8;
244 // Top to bottom
245 return MathUtil::Rectangle<u32>(x0, height - y0, x0 + sub_surface.width,
246 height - (y0 + sub_surface.height));
247 }
248
249 const int x0 = begin_pixel_index % stride;
250 const int y0 = begin_pixel_index / stride;
251 // Bottom to top
252 return MathUtil::Rectangle<u32>(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0);
253}
254
255MathUtil::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const {
256 auto rect = GetSubRect(sub_surface);
257 rect.left = rect.left * res_scale;
258 rect.right = rect.right * res_scale;
259 rect.top = rect.top * res_scale;
260 rect.bottom = rect.bottom * res_scale;
261 return rect;
262}
263
264bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
265 return std::tie(other_surface.addr, other_surface.width, other_surface.height,
266 other_surface.stride, other_surface.block_height, other_surface.pixel_format,
267 other_surface.component_type,
268 other_surface.is_tiled) == std::tie(addr, width, height, stride, block_height,
269 pixel_format, component_type, is_tiled) &&
270 pixel_format != PixelFormat::Invalid;
271}
272
273bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const {
274 return sub_surface.addr >= addr && sub_surface.end <= end &&
275 sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid &&
276 sub_surface.is_tiled == is_tiled && sub_surface.block_height == block_height &&
277 sub_surface.component_type == component_type &&
278 (sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
279 (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) &&
280 GetSubRect(sub_surface).left + sub_surface.width <= stride;
281}
282
283bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const {
284 return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format &&
285 addr <= expanded_surface.end && expanded_surface.addr <= end &&
286 is_tiled == expanded_surface.is_tiled && block_height == expanded_surface.block_height &&
287 component_type == expanded_surface.component_type && stride == expanded_surface.stride &&
288 (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) %
289 BytesInPixels(stride * (is_tiled ? 8 : 1)) ==
290 0;
291}
292
293bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
294 if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr ||
295 end < texcopy_params.end) {
296 return false;
297 }
298 if (texcopy_params.block_height != block_height ||
299 texcopy_params.component_type != component_type)
300 return false;
301
302 if (texcopy_params.width != texcopy_params.stride) {
303 const u32 tile_stride = static_cast<u32>(BytesInPixels(stride * (is_tiled ? 8 : 1)));
304 return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
305 texcopy_params.width % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
306 (texcopy_params.height == 1 || texcopy_params.stride == tile_stride) &&
307 ((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride;
308 }
309 return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval();
310}
311
312VAddr SurfaceParams::GetCpuAddr() const {
313 // When this function is used, only cpu_addr or (GPU) addr should be set, not both
314 ASSERT(!(cpu_addr && addr));
315 const auto& gpu = Core::System::GetInstance().GPU();
316 return cpu_addr.get_value_or(*gpu.memory_manager->GpuToCpuAddress(addr));
317}
318
319bool CachedSurface::CanFill(const SurfaceParams& dest_surface,
320 SurfaceInterval fill_interval) const {
321 if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
322 boost::icl::first(fill_interval) >= addr &&
323 boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
324 dest_surface.FromInterval(fill_interval).GetInterval() ==
325 fill_interval) { // make sure interval is a rectangle in dest surface
326 if (fill_size * CHAR_BIT != dest_surface.GetFormatBpp()) {
327 // Check if bits repeat for our fill_size
328 const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / CHAR_BIT, 1u);
329 std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
330
331 for (u32 i = 0; i < dest_bytes_per_pixel; ++i)
332 std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
333
334 for (u32 i = 0; i < fill_size; ++i)
335 if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0],
336 dest_bytes_per_pixel) != 0)
337 return false;
338
339 if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4))
340 return false;
341 } 281 }
342 return true;
343 } 282 }
344 return false;
345}
346
347bool CachedSurface::CanCopy(const SurfaceParams& dest_surface,
348 SurfaceInterval copy_interval) const {
349 SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval);
350 ASSERT(subrect_params.GetInterval() == copy_interval);
351 if (CanSubRect(subrect_params))
352 return true;
353
354 if (CanFill(dest_surface, copy_interval))
355 return true;
356
357 return false;
358} 283}
359 284/**
360SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) const { 285 * Helper function to perform software conversion (as needed) when loading a buffer from Switch
361 SurfaceInterval result{}; 286 * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
362 const auto valid_regions = 287 * typical desktop GPUs.
363 SurfaceRegions(GetInterval() & src_surface->GetInterval()) - src_surface->invalid_regions; 288 */
364 for (auto& valid_interval : valid_regions) { 289static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
365 const SurfaceInterval aligned_interval{ 290 u32 width, u32 height) {
366 addr + Common::AlignUp(boost::icl::first(valid_interval) - addr, 291 switch (pixel_format) {
367 BytesInPixels(is_tiled ? 8 * 8 : 1)), 292 case PixelFormat::ASTC_2D_4X4: {
368 addr + Common::AlignDown(boost::icl::last_next(valid_interval) - addr, 293 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
369 BytesInPixels(is_tiled ? 8 * 8 : 1))}; 294 u32 block_width{};
370 295 u32 block_height{};
371 if (BytesInPixels(is_tiled ? 8 * 8 : 1) > boost::icl::length(valid_interval) || 296 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
372 boost::icl::length(aligned_interval) == 0) { 297 data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height);
373 continue; 298 break;
374 } 299 }
375 300 case PixelFormat::S8Z24:
376 // Get the rectangle within aligned_interval 301 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
377 const u32 stride_bytes = static_cast<u32>(BytesInPixels(stride)) * (is_tiled ? 8 : 1); 302 ConvertS8Z24ToZ24S8(data, width, height);
378 SurfaceInterval rect_interval{ 303 break;
379 addr + Common::AlignUp(boost::icl::first(aligned_interval) - addr, stride_bytes), 304 }
380 addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - addr, stride_bytes), 305}
381 }; 306
382 if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) { 307/**
383 // 1 row 308 * Helper function to perform software conversion (as needed) when flushing a buffer to Switch
384 rect_interval = aligned_interval; 309 * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
385 } else if (boost::icl::length(rect_interval) == 0) { 310 * typical desktop GPUs.
386 // 2 rows that do not make a rectangle, return the larger one 311 */
387 const SurfaceInterval row1{boost::icl::first(aligned_interval), 312static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format,
388 boost::icl::first(rect_interval)}; 313 u32 /*width*/, u32 /*height*/) {
389 const SurfaceInterval row2{boost::icl::first(rect_interval), 314 switch (pixel_format) {
390 boost::icl::last_next(aligned_interval)}; 315 case PixelFormat::ASTC_2D_4X4:
391 rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2; 316 case PixelFormat::S8Z24:
392 } 317 LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}",
393 318 static_cast<u32>(pixel_format));
394 if (boost::icl::length(rect_interval) > boost::icl::length(result)) { 319 UNREACHABLE();
395 result = rect_interval; 320 break;
396 }
397 } 321 }
398 return result;
399}
400
401void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface,
402 SurfaceInterval copy_interval) {
403 SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval);
404 ASSERT(subrect_params.GetInterval() == copy_interval);
405
406 ASSERT(src_surface != dst_surface);
407
408 // This is only called when CanCopy is true, no need to run checks here
409 if (src_surface->type == SurfaceType::Fill) {
410 // FillSurface needs a 4 bytes buffer
411 const u64 fill_offset =
412 (boost::icl::first(copy_interval) - src_surface->addr) % src_surface->fill_size;
413 std::array<u8, 4> fill_buffer;
414
415 u64 fill_buff_pos = fill_offset;
416 for (int i : {0, 1, 2, 3})
417 fill_buffer[i] = src_surface->fill_data[fill_buff_pos++ % src_surface->fill_size];
418
419 FillSurface(dst_surface, &fill_buffer[0], dst_surface->GetScaledSubRect(subrect_params),
420 draw_framebuffer.handle);
421 return;
422 }
423 if (src_surface->CanSubRect(subrect_params)) {
424 BlitTextures(src_surface->texture.handle, src_surface->GetScaledSubRect(subrect_params),
425 dst_surface->texture.handle, dst_surface->GetScaledSubRect(subrect_params),
426 src_surface->type, read_framebuffer.handle, draw_framebuffer.handle);
427 return;
428 }
429 UNREACHABLE();
430} 322}
431 323
432MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 324MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
433void CachedSurface::LoadGLBuffer(Tegra::GPUVAddr load_start, Tegra::GPUVAddr load_end) { 325void CachedSurface::LoadGLBuffer() {
434 ASSERT(type != SurfaceType::Fill); 326 ASSERT(params.type != SurfaceType::Fill);
435 327
436 u8* const texture_src_data = Memory::GetPointer(GetCpuAddr()); 328 u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr());
437 if (texture_src_data == nullptr)
438 return;
439 329
440 if (gl_buffer == nullptr) { 330 ASSERT(texture_src_data);
441 gl_buffer_size = GetActualWidth() * GetActualHeight() * GetGLBytesPerPixel(pixel_format);
442 gl_buffer.reset(new u8[gl_buffer_size]);
443 }
444 331
445 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 332 gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
446 333
447 ASSERT(load_start >= addr && load_end <= end); 334 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
448 const u64 start_offset = load_start - addr;
449 335
450 if (!is_tiled) { 336 if (!params.is_tiled) {
451 const u32 bytes_per_pixel{GetFormatBpp() >> 3}; 337 const u32 bytes_per_pixel{params.GetFormatBpp() >> 3};
452 338
453 std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset, 339 std::memcpy(gl_buffer.data(), texture_src_data,
454 bytes_per_pixel * width * height); 340 bytes_per_pixel * params.width * params.height);
455 } else { 341 } else {
456 morton_to_gl_fns[static_cast<size_t>(pixel_format)](GetActualWidth(), block_height, 342 morton_to_gl_fns[static_cast<size_t>(params.pixel_format)](
457 GetActualHeight(), &gl_buffer[0], addr, 343 params.width, params.block_height, params.height, gl_buffer.data(), params.addr);
458 load_start, load_end);
459 } 344 }
345
346 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height);
460} 347}
461 348
462MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 349MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
463void CachedSurface::FlushGLBuffer(Tegra::GPUVAddr flush_start, Tegra::GPUVAddr flush_end) { 350void CachedSurface::FlushGLBuffer() {
464 u8* const dst_buffer = Memory::GetPointer(GetCpuAddr()); 351 u8* const dst_buffer = Memory::GetPointer(params.GetCpuAddr());
465 if (dst_buffer == nullptr)
466 return;
467
468 ASSERT(gl_buffer_size == width * height * GetGLBytesPerPixel(pixel_format));
469 352
470 // TODO: Should probably be done in ::Memory:: and check for other regions too 353 ASSERT(dst_buffer);
471 // same as loadglbuffer() 354 ASSERT(gl_buffer.size() ==
472 if (flush_start < Memory::VRAM_VADDR_END && flush_end > Memory::VRAM_VADDR_END) 355 params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
473 flush_end = Memory::VRAM_VADDR_END;
474
475 if (flush_start < Memory::VRAM_VADDR && flush_end > Memory::VRAM_VADDR)
476 flush_start = Memory::VRAM_VADDR;
477 356
478 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); 357 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
479 358
480 ASSERT(flush_start >= addr && flush_end <= end); 359 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
481 const u64 start_offset = flush_start - addr; 360 params.height);
482 const u64 end_offset = flush_end - addr;
483
484 if (type == SurfaceType::Fill) {
485 const u64 coarse_start_offset = start_offset - (start_offset % fill_size);
486 const u64 backup_bytes = start_offset % fill_size;
487 std::array<u8, 4> backup_data;
488 if (backup_bytes)
489 std::memcpy(&backup_data[0], &dst_buffer[coarse_start_offset], backup_bytes);
490
491 for (u64 offset = coarse_start_offset; offset < end_offset; offset += fill_size) {
492 std::memcpy(&dst_buffer[offset], &fill_data[0],
493 std::min(fill_size, end_offset - offset));
494 }
495 361
496 if (backup_bytes) 362 if (!params.is_tiled) {
497 std::memcpy(&dst_buffer[coarse_start_offset], &backup_data[0], backup_bytes); 363 std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
498 } else if (!is_tiled) {
499 std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start);
500 } else { 364 } else {
501 gl_to_morton_fns[static_cast<size_t>(pixel_format)]( 365 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
502 stride, block_height, height, &gl_buffer[0], addr, flush_start, flush_end); 366 params.width, params.block_height, params.height, gl_buffer.data(), params.addr);
503 } 367 }
504} 368}
505 369
506MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 370MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
507void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, 371void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
508 GLuint draw_fb_handle) { 372 if (params.type == SurfaceType::Fill)
509 if (type == SurfaceType::Fill)
510 return; 373 return;
511 374
512 MICROPROFILE_SCOPE(OpenGL_TextureUL); 375 MICROPROFILE_SCOPE(OpenGL_TextureUL);
513 376
514 ASSERT(gl_buffer_size == 377 ASSERT(gl_buffer.size() ==
515 GetActualWidth() * GetActualHeight() * GetGLBytesPerPixel(pixel_format)); 378 params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
379
380 const auto& rect{params.GetRect()};
516 381
517 // Load data from memory to the surface 382 // Load data from memory to the surface
518 GLint x0 = static_cast<GLint>(rect.left); 383 GLint x0 = static_cast<GLint>(rect.left);
519 GLint y0 = static_cast<GLint>(rect.bottom); 384 GLint y0 = static_cast<GLint>(rect.bottom);
520 size_t buffer_offset = (y0 * stride + x0) * GetGLBytesPerPixel(pixel_format); 385 size_t buffer_offset = (y0 * params.width + x0) * GetGLBytesPerPixel(params.pixel_format);
521 386
522 const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type); 387 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
523 GLuint target_tex = texture.handle; 388 GLuint target_tex = texture.handle;
524
525 // If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
526 // surface
527 OGLTexture unscaled_tex;
528 if (res_scale != 1) {
529 x0 = 0;
530 y0 = 0;
531
532 unscaled_tex.Create();
533 AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight());
534 target_tex = unscaled_tex.handle;
535 }
536
537 OpenGLState cur_state = OpenGLState::GetCurState(); 389 OpenGLState cur_state = OpenGLState::GetCurState();
538 390
539 GLuint old_tex = cur_state.texture_units[0].texture_2d; 391 GLuint old_tex = cur_state.texture_units[0].texture_2d;
@@ -541,15 +393,15 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
541 cur_state.Apply(); 393 cur_state.Apply();
542 394
543 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 395 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
544 ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0); 396 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0);
545 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride)); 397 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width));
546 398
547 glActiveTexture(GL_TEXTURE0); 399 glActiveTexture(GL_TEXTURE0);
548 if (tuple.compressed) { 400 if (tuple.compressed) {
549 glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, 401 glCompressedTexImage2D(
550 static_cast<GLsizei>(rect.GetWidth() * GetCompresssionFactor()), 402 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
551 static_cast<GLsizei>(rect.GetHeight() * GetCompresssionFactor()), 0, 403 static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
552 size, &gl_buffer[buffer_offset]); 404 &gl_buffer[buffer_offset]);
553 } else { 405 } else {
554 glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), 406 glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
555 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 407 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
@@ -560,845 +412,250 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
560 412
561 cur_state.texture_units[0].texture_2d = old_tex; 413 cur_state.texture_units[0].texture_2d = old_tex;
562 cur_state.Apply(); 414 cur_state.Apply();
563
564 if (res_scale != 1) {
565 auto scaled_rect = rect;
566 scaled_rect.left *= res_scale;
567 scaled_rect.top *= res_scale;
568 scaled_rect.right *= res_scale;
569 scaled_rect.bottom *= res_scale;
570
571 BlitTextures(unscaled_tex.handle, {0, rect.GetHeight(), rect.GetWidth(), 0}, texture.handle,
572 scaled_rect, type, read_fb_handle, draw_fb_handle);
573 }
574} 415}
575 416
576MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64)); 417MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64));
577void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, 418void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
578 GLuint draw_fb_handle) { 419 if (params.type == SurfaceType::Fill)
579 if (type == SurfaceType::Fill)
580 return; 420 return;
581 421
582 MICROPROFILE_SCOPE(OpenGL_TextureDL); 422 MICROPROFILE_SCOPE(OpenGL_TextureDL);
583 423
584 if (gl_buffer == nullptr) { 424 gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
585 gl_buffer_size = width * height * GetGLBytesPerPixel(pixel_format);
586 gl_buffer.reset(new u8[gl_buffer_size]);
587 }
588 425
589 OpenGLState state = OpenGLState::GetCurState(); 426 OpenGLState state = OpenGLState::GetCurState();
590 OpenGLState prev_state = state; 427 OpenGLState prev_state = state;
591 SCOPE_EXIT({ prev_state.Apply(); }); 428 SCOPE_EXIT({ prev_state.Apply(); });
592 429
593 const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type); 430 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
594 431
595 // Ensure no bad interactions with GL_PACK_ALIGNMENT 432 // Ensure no bad interactions with GL_PACK_ALIGNMENT
596 ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0); 433 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0);
597 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride)); 434 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
598 size_t buffer_offset = (rect.bottom * stride + rect.left) * GetGLBytesPerPixel(pixel_format);
599
600 // If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
601 if (res_scale != 1) {
602 auto scaled_rect = rect;
603 scaled_rect.left *= res_scale;
604 scaled_rect.top *= res_scale;
605 scaled_rect.right *= res_scale;
606 scaled_rect.bottom *= res_scale;
607
608 OGLTexture unscaled_tex;
609 unscaled_tex.Create();
610
611 MathUtil::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
612 AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight());
613 BlitTextures(texture.handle, scaled_rect, unscaled_tex.handle, unscaled_tex_rect, type,
614 read_fb_handle, draw_fb_handle);
615
616 state.texture_units[0].texture_2d = unscaled_tex.handle;
617 state.Apply();
618
619 glActiveTexture(GL_TEXTURE0);
620 glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]);
621 } else {
622 state.ResetTexture(texture.handle);
623 state.draw.read_framebuffer = read_fb_handle;
624 state.Apply();
625
626 if (type == SurfaceType::ColorTexture) {
627 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
628 texture.handle, 0);
629 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
630 0, 0);
631 } else if (type == SurfaceType::Depth) {
632 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
633 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
634 texture.handle, 0);
635 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
636 } else {
637 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
638 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
639 texture.handle, 0);
640 }
641 glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
642 static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
643 tuple.format, tuple.type, &gl_buffer[buffer_offset]);
644 }
645 435
646 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 436 const auto& rect{params.GetRect()};
647} 437 size_t buffer_offset =
648 438 (rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format);
649enum class MatchFlags {
650 None = 0,
651 Invalid = 1, // Flag that can be applied to other match types, invalid matches require
652 // validation before they can be used
653 Exact = 1 << 1, // Surfaces perfectly match
654 SubRect = 1 << 2, // Surface encompasses params
655 Copy = 1 << 3, // Surface we can copy from
656 Expand = 1 << 4, // Surface that can expand params
657 TexCopy = 1 << 5 // Surface that will match a display transfer "texture copy" parameters
658};
659
660constexpr MatchFlags operator|(MatchFlags lhs, MatchFlags rhs) {
661 return static_cast<MatchFlags>(static_cast<int>(lhs) | static_cast<int>(rhs));
662}
663 439
664constexpr MatchFlags operator&(MatchFlags lhs, MatchFlags rhs) { 440 state.UnbindTexture(texture.handle);
665 return static_cast<MatchFlags>(static_cast<int>(lhs) & static_cast<int>(rhs)); 441 state.draw.read_framebuffer = read_fb_handle;
666} 442 state.Apply();
667 443
668/// Get the best surface match (and its match type) for the given flags 444 if (params.type == SurfaceType::ColorTexture) {
669template <MatchFlags find_flags> 445 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
670Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params, 446 texture.handle, 0);
671 ScaleMatch match_scale_type, 447 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
672 boost::optional<SurfaceInterval> validate_interval = boost::none) { 448 0);
673 Surface match_surface = nullptr; 449 } else if (params.type == SurfaceType::Depth) {
674 bool match_valid = false; 450 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
675 u32 match_scale = 0; 451 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
676 SurfaceInterval match_interval{}; 452 texture.handle, 0);
677 453 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
678 for (auto& pair : RangeFromInterval(surface_cache, params.GetInterval())) { 454 } else {
679 for (auto& surface : pair.second) { 455 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
680 bool res_scale_matched = match_scale_type == ScaleMatch::Exact 456 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
681 ? (params.res_scale == surface->res_scale) 457 texture.handle, 0);
682 : (params.res_scale <= surface->res_scale);
683 // validity will be checked in GetCopyableInterval
684 bool is_valid =
685 (find_flags & MatchFlags::Copy) != MatchFlags::None
686 ? true
687 : surface->IsRegionValid(validate_interval.value_or(params.GetInterval()));
688
689 if ((find_flags & MatchFlags::Invalid) == MatchFlags::None && !is_valid)
690 continue;
691
692 auto IsMatch_Helper = [&](auto check_type, auto match_fn) {
693 if ((find_flags & check_type) == MatchFlags::None)
694 return;
695
696 bool matched;
697 SurfaceInterval surface_interval;
698 std::tie(matched, surface_interval) = match_fn();
699 if (!matched)
700 return;
701
702 if (!res_scale_matched && match_scale_type != ScaleMatch::Ignore &&
703 surface->type != SurfaceType::Fill)
704 return;
705
706 // Found a match, update only if this is better than the previous one
707 auto UpdateMatch = [&] {
708 match_surface = surface;
709 match_valid = is_valid;
710 match_scale = surface->res_scale;
711 match_interval = surface_interval;
712 };
713
714 if (surface->res_scale > match_scale) {
715 UpdateMatch();
716 return;
717 } else if (surface->res_scale < match_scale) {
718 return;
719 }
720
721 if (is_valid && !match_valid) {
722 UpdateMatch();
723 return;
724 } else if (is_valid != match_valid) {
725 return;
726 }
727
728 if (boost::icl::length(surface_interval) > boost::icl::length(match_interval)) {
729 UpdateMatch();
730 }
731 };
732 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Exact>{}, [&] {
733 return std::make_pair(surface->ExactMatch(params), surface->GetInterval());
734 });
735 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::SubRect>{}, [&] {
736 return std::make_pair(surface->CanSubRect(params), surface->GetInterval());
737 });
738 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Copy>{}, [&] {
739 auto copy_interval =
740 params.FromInterval(*validate_interval).GetCopyableInterval(surface);
741 bool matched = boost::icl::length(copy_interval & *validate_interval) != 0 &&
742 surface->CanCopy(params, copy_interval);
743 return std::make_pair(matched, copy_interval);
744 });
745 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Expand>{}, [&] {
746 return std::make_pair(surface->CanExpand(params), surface->GetInterval());
747 });
748 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::TexCopy>{}, [&] {
749 return std::make_pair(surface->CanTexCopy(params), surface->GetInterval());
750 });
751 }
752 } 458 }
753 return match_surface; 459 glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
460 static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
461 tuple.format, tuple.type, &gl_buffer[buffer_offset]);
462
463 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
754} 464}
755 465
756RasterizerCacheOpenGL::RasterizerCacheOpenGL() { 466RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
757 read_framebuffer.Create(); 467 read_framebuffer.Create();
758 draw_framebuffer.Create(); 468 draw_framebuffer.Create();
759
760 attributeless_vao.Create();
761
762 d24s8_abgr_buffer.Create();
763 d24s8_abgr_buffer_size = 0;
764
765 const char* vs_source = R"(
766#version 330 core
767const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
768void main() {
769 gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
770}
771)";
772 const char* fs_source = R"(
773#version 330 core
774
775uniform samplerBuffer tbo;
776uniform vec2 tbo_size;
777uniform vec4 viewport;
778
779out vec4 color;
780
781void main() {
782 vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw;
783 int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x);
784 color = texelFetch(tbo, tbo_offset).rabg;
785}
786)";
787 d24s8_abgr_shader.CreateFromSource(vs_source, nullptr, fs_source);
788
789 OpenGLState state = OpenGLState::GetCurState();
790 GLuint old_program = state.draw.shader_program;
791 state.draw.shader_program = d24s8_abgr_shader.handle;
792 state.Apply();
793
794 GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo");
795 ASSERT(tbo_u_id != -1);
796 glUniform1i(tbo_u_id, 0);
797
798 state.draw.shader_program = old_program;
799 state.Apply();
800
801 d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size");
802 ASSERT(d24s8_abgr_tbo_size_u_id != -1);
803 d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport");
804 ASSERT(d24s8_abgr_viewport_u_id != -1);
805} 469}
806 470
807RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { 471RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
808 FlushAll(); 472 while (!surface_cache.empty()) {
809 while (!surface_cache.empty()) 473 UnregisterSurface(surface_cache.begin()->second);
810 UnregisterSurface(*surface_cache.begin()->second.begin());
811}
812
813bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface,
814 const MathUtil::Rectangle<u32>& src_rect,
815 const Surface& dst_surface,
816 const MathUtil::Rectangle<u32>& dst_rect) {
817 if (!SurfaceParams::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format))
818 return false;
819
820 return BlitTextures(src_surface->texture.handle, src_rect, dst_surface->texture.handle,
821 dst_rect, src_surface->type, read_framebuffer.handle,
822 draw_framebuffer.handle);
823}
824
825void RasterizerCacheOpenGL::ConvertD24S8toABGR(GLuint src_tex,
826 const MathUtil::Rectangle<u32>& src_rect,
827 GLuint dst_tex,
828 const MathUtil::Rectangle<u32>& dst_rect) {
829 OpenGLState prev_state = OpenGLState::GetCurState();
830 SCOPE_EXIT({ prev_state.Apply(); });
831
832 OpenGLState state;
833 state.draw.read_framebuffer = read_framebuffer.handle;
834 state.draw.draw_framebuffer = draw_framebuffer.handle;
835 state.Apply();
836
837 glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle);
838
839 GLsizeiptr target_pbo_size = src_rect.GetWidth() * src_rect.GetHeight() * 4;
840 if (target_pbo_size > d24s8_abgr_buffer_size) {
841 d24s8_abgr_buffer_size = target_pbo_size * 2;
842 glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY);
843 }
844
845 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
846 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, src_tex,
847 0);
848 glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom),
849 static_cast<GLsizei>(src_rect.GetWidth()),
850 static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
851 0);
852
853 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
854
855 // PBO now contains src_tex in RABG format
856 state.draw.shader_program = d24s8_abgr_shader.handle;
857 state.draw.vertex_array = attributeless_vao.handle;
858 state.viewport.x = static_cast<GLint>(dst_rect.left);
859 state.viewport.y = static_cast<GLint>(dst_rect.bottom);
860 state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth());
861 state.viewport.height = static_cast<GLsizei>(dst_rect.GetHeight());
862 state.Apply();
863
864 OGLTexture tbo;
865 tbo.Create();
866 glActiveTexture(GL_TEXTURE0);
867 glBindTexture(GL_TEXTURE_BUFFER, tbo.handle);
868 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle);
869
870 glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast<GLfloat>(src_rect.GetWidth()),
871 static_cast<GLfloat>(src_rect.GetHeight()));
872 glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x),
873 static_cast<GLfloat>(state.viewport.y), static_cast<GLfloat>(state.viewport.width),
874 static_cast<GLfloat>(state.viewport.height));
875
876 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0);
877 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
878 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
879
880 glBindTexture(GL_TEXTURE_BUFFER, 0);
881}
882
883Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
884 bool load_if_create) {
885 if (params.addr == 0 || params.height * params.width == 0) {
886 return nullptr;
887 }
888 // Use GetSurfaceSubRect instead
889 ASSERT(params.width == params.stride);
890
891 ASSERT(!params.is_tiled ||
892 (params.GetActualWidth() % 8 == 0 && params.GetActualHeight() % 8 == 0));
893
894 // Check for an exact match in existing surfaces
895 Surface surface =
896 FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
897
898 if (surface == nullptr) {
899 u16 target_res_scale = params.res_scale;
900 if (match_res_scale != ScaleMatch::Exact) {
901 // This surface may have a subrect of another surface with a higher res_scale, find it
902 // to adjust our params
903 SurfaceParams find_params = params;
904 Surface expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(
905 surface_cache, find_params, match_res_scale);
906 if (expandable != nullptr && expandable->res_scale > target_res_scale) {
907 target_res_scale = expandable->res_scale;
908 }
909 }
910 SurfaceParams new_params = params;
911 new_params.res_scale = target_res_scale;
912 surface = CreateSurface(new_params);
913 RegisterSurface(surface);
914 }
915
916 if (load_if_create) {
917 ValidateSurface(surface, params.addr, params.size);
918 }
919
920 return surface;
921}
922
923boost::optional<Tegra::GPUVAddr> RasterizerCacheOpenGL::TryFindFramebufferGpuAddress(
924 VAddr cpu_addr) const {
925 // Tries to find the GPU address of a framebuffer based on the CPU address. This is because
926 // final output framebuffers are specified by CPU address, but internally our GPU cache uses GPU
927 // addresses. We iterate through all cached framebuffers, and compare their starting CPU address
928 // to the one provided. This is obviously not great, and won't work if the framebuffer overlaps
929 // surfaces.
930
931 std::vector<Tegra::GPUVAddr> gpu_addresses;
932 for (const auto& pair : surface_cache) {
933 for (const auto& surface : pair.second) {
934 const VAddr surface_cpu_addr = surface->GetCpuAddr();
935 if (cpu_addr >= surface_cpu_addr && cpu_addr < (surface_cpu_addr + surface->size)) {
936 ASSERT_MSG(cpu_addr == surface_cpu_addr, "overlapping surfaces are unsupported");
937 gpu_addresses.push_back(surface->addr);
938 }
939 }
940 } 474 }
941
942 if (gpu_addresses.empty()) {
943 return {};
944 }
945
946 ASSERT_MSG(gpu_addresses.size() == 1, ">1 surface is unsupported");
947 return gpu_addresses[0];
948}
949
950SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params,
951 ScaleMatch match_res_scale,
952 bool load_if_create) {
953 if (params.addr == 0 || params.height * params.width == 0) {
954 return std::make_tuple(nullptr, MathUtil::Rectangle<u32>{});
955 }
956
957 // Attempt to find encompassing surface
958 Surface surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params,
959 match_res_scale);
960
961 // Check if FindMatch failed because of res scaling
962 // If that's the case create a new surface with
963 // the dimensions of the lower res_scale surface
964 // to suggest it should not be used again
965 if (surface == nullptr && match_res_scale != ScaleMatch::Ignore) {
966 surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params,
967 ScaleMatch::Ignore);
968 if (surface != nullptr) {
969 ASSERT(surface->res_scale < params.res_scale);
970 SurfaceParams new_params = *surface;
971 new_params.res_scale = params.res_scale;
972
973 surface = CreateSurface(new_params);
974 RegisterSurface(surface);
975 }
976 }
977
978 SurfaceParams aligned_params = params;
979 if (params.is_tiled) {
980 aligned_params.height = Common::AlignUp(params.height, 8);
981 aligned_params.width = Common::AlignUp(params.width, 8);
982 aligned_params.stride = Common::AlignUp(params.stride, 8);
983 aligned_params.UpdateParams();
984 }
985
986 // Check for a surface we can expand before creating a new one
987 if (surface == nullptr) {
988 surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, aligned_params,
989 match_res_scale);
990 if (surface != nullptr) {
991 aligned_params.width = aligned_params.stride;
992 aligned_params.UpdateParams();
993
994 SurfaceParams new_params = *surface;
995 new_params.addr = std::min(aligned_params.addr, surface->addr);
996 new_params.end = std::max(aligned_params.end, surface->end);
997 new_params.size = new_params.end - new_params.addr;
998 new_params.height = static_cast<u32>(
999 new_params.size / aligned_params.BytesInPixels(aligned_params.stride));
1000 ASSERT(new_params.size % aligned_params.BytesInPixels(aligned_params.stride) == 0);
1001
1002 Surface new_surface = CreateSurface(new_params);
1003 DuplicateSurface(surface, new_surface);
1004
1005 // Delete the expanded surface, this can't be done safely yet
1006 // because it may still be in use
1007 remove_surfaces.emplace(surface);
1008
1009 surface = new_surface;
1010 RegisterSurface(new_surface);
1011 }
1012 }
1013
1014 // No subrect found - create and return a new surface
1015 if (surface == nullptr) {
1016 SurfaceParams new_params = aligned_params;
1017 // Can't have gaps in a surface
1018 new_params.width = aligned_params.stride;
1019 new_params.UpdateParams();
1020 // GetSurface will create the new surface and possibly adjust res_scale if necessary
1021 surface = GetSurface(new_params, match_res_scale, load_if_create);
1022 } else if (load_if_create) {
1023 ValidateSurface(surface, aligned_params.addr, aligned_params.size);
1024 }
1025
1026 return std::make_tuple(surface, surface->GetScaledSubRect(params));
1027} 475}
1028 476
1029Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { 477Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
1030 auto& gpu = Core::System::GetInstance().GPU(); 478 return GetSurface(SurfaceParams::CreateForTexture(config));
1031
1032 SurfaceParams params;
1033 params.addr = config.tic.Address();
1034 params.is_tiled = config.tic.IsTiled();
1035 params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
1036
1037 params.width = Common::AlignUp(config.tic.Width(), params.GetCompresssionFactor()) /
1038 params.GetCompresssionFactor();
1039 params.height = Common::AlignUp(config.tic.Height(), params.GetCompresssionFactor()) /
1040 params.GetCompresssionFactor();
1041
1042 // TODO(Subv): Different types per component are not supported.
1043 ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() &&
1044 config.tic.r_type.Value() == config.tic.b_type.Value() &&
1045 config.tic.r_type.Value() == config.tic.a_type.Value());
1046
1047 params.component_type = SurfaceParams::ComponentTypeFromTexture(config.tic.r_type.Value());
1048
1049 if (config.tic.IsTiled()) {
1050 params.block_height = config.tic.BlockHeight();
1051 params.width = Common::AlignUp(params.width, params.block_height);
1052 params.height = Common::AlignUp(params.height, params.block_height);
1053 } else {
1054 // Use the texture-provided stride value if the texture isn't tiled.
1055 params.stride = static_cast<u32>(params.PixelsInBytes(config.tic.Pitch()));
1056 }
1057
1058 params.UpdateParams();
1059
1060 if (params.GetActualWidth() % 8 != 0 || params.GetActualHeight() % 8 != 0 ||
1061 params.stride != params.width) {
1062 Surface src_surface;
1063 MathUtil::Rectangle<u32> rect;
1064 std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
1065
1066 rect = rect.Scale(params.GetCompresssionFactor());
1067
1068 params.res_scale = src_surface->res_scale;
1069 Surface tmp_surface = CreateSurface(params);
1070
1071 auto dst_rect = tmp_surface->GetScaledRect().Scale(params.GetCompresssionFactor());
1072 BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, dst_rect,
1073 SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
1074 draw_framebuffer.handle);
1075
1076 remove_surfaces.emplace(tmp_surface);
1077 return tmp_surface;
1078 }
1079
1080 return GetSurface(params, ScaleMatch::Ignore, true);
1081} 479}
1082 480
1083SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( 481SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
1084 bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) { 482 bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) {
1085 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 483 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
1086 const auto& config = regs.rt[0];
1087 484
1088 // TODO(bunnei): This is hard corded to use just the first render buffer 485 // TODO(bunnei): This is hard corded to use just the first render buffer
1089 NGLOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); 486 LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!");
1090
1091 // update resolution_scale_factor and reset cache if changed
1092 // TODO (bunnei): This code was ported as-is from Citra, and is technically not thread-safe. We
1093 // need to fix this before making the renderer multi-threaded.
1094 static u16 resolution_scale_factor = GetResolutionScaleFactor();
1095 if (resolution_scale_factor != GetResolutionScaleFactor()) {
1096 resolution_scale_factor = GetResolutionScaleFactor();
1097 FlushAll();
1098 while (!surface_cache.empty())
1099 UnregisterSurface(*surface_cache.begin()->second.begin());
1100 }
1101
1102 MathUtil::Rectangle<u32> viewport_clamped{
1103 static_cast<u32>(std::clamp(viewport.left, 0, static_cast<s32>(config.width))),
1104 static_cast<u32>(std::clamp(viewport.top, 0, static_cast<s32>(config.height))),
1105 static_cast<u32>(std::clamp(viewport.right, 0, static_cast<s32>(config.width))),
1106 static_cast<u32>(std::clamp(viewport.bottom, 0, static_cast<s32>(config.height)))};
1107 487
1108 // get color and depth surfaces 488 // get color and depth surfaces
1109 SurfaceParams color_params; 489 SurfaceParams color_params{};
1110 color_params.is_tiled = true; 490 SurfaceParams depth_params{};
1111 color_params.res_scale = resolution_scale_factor; 491
1112 color_params.width = config.width; 492 if (using_color_fb) {
1113 color_params.height = config.height; 493 color_params = SurfaceParams::CreateForFramebuffer(regs.rt[0]);
1114 // TODO(Subv): Can framebuffers use a different block height? 494 }
1115 color_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 495
1116 SurfaceParams depth_params = color_params; 496 if (using_depth_fb) {
1117 497 depth_params =
1118 color_params.addr = config.Address(); 498 SurfaceParams::CreateForDepthBuffer(regs.rt[0], regs.zeta.Address(), regs.zeta.format);
1119 color_params.pixel_format = SurfaceParams::PixelFormatFromRenderTargetFormat(config.format);
1120 color_params.component_type = SurfaceParams::ComponentTypeFromRenderTarget(config.format);
1121 color_params.UpdateParams();
1122
1123 ASSERT_MSG(!using_depth_fb, "depth buffer is unimplemented");
1124 // depth_params.addr = config.GetDepthBufferPhysicalAddress();
1125 // depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format);
1126 // depth_params.UpdateParams();
1127
1128 auto color_vp_interval = color_params.GetSubRectInterval(viewport_clamped);
1129 auto depth_vp_interval = depth_params.GetSubRectInterval(viewport_clamped);
1130
1131 // Make sure that framebuffers don't overlap if both color and depth are being used
1132 if (using_color_fb && using_depth_fb &&
1133 boost::icl::length(color_vp_interval & depth_vp_interval)) {
1134 NGLOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; "
1135 "overlapping framebuffers not supported!");
1136 using_depth_fb = false;
1137 } 499 }
1138 500
1139 MathUtil::Rectangle<u32> color_rect{}; 501 MathUtil::Rectangle<u32> color_rect{};
1140 Surface color_surface = nullptr; 502 Surface color_surface;
1141 if (using_color_fb) 503 if (using_color_fb) {
1142 std::tie(color_surface, color_rect) = 504 color_surface = GetSurface(color_params);
1143 GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); 505 if (color_surface) {
506 color_rect = color_surface->GetSurfaceParams().GetRect();
507 }
508 }
1144 509
1145 MathUtil::Rectangle<u32> depth_rect{}; 510 MathUtil::Rectangle<u32> depth_rect{};
1146 Surface depth_surface = nullptr; 511 Surface depth_surface;
1147 if (using_depth_fb) 512 if (using_depth_fb) {
1148 std::tie(depth_surface, depth_rect) = 513 depth_surface = GetSurface(depth_params);
1149 GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); 514 if (depth_surface) {
515 depth_rect = depth_surface->GetSurfaceParams().GetRect();
516 }
517 }
1150 518
1151 MathUtil::Rectangle<u32> fb_rect{}; 519 MathUtil::Rectangle<u32> fb_rect{};
1152 if (color_surface != nullptr && depth_surface != nullptr) { 520 if (color_surface && depth_surface) {
1153 fb_rect = color_rect; 521 fb_rect = color_rect;
1154 // Color and Depth surfaces must have the same dimensions and offsets 522 // Color and Depth surfaces must have the same dimensions and offsets
1155 if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top || 523 if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top ||
1156 color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) { 524 color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) {
1157 color_surface = GetSurface(color_params, ScaleMatch::Exact, false); 525 color_surface = GetSurface(color_params);
1158 depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); 526 depth_surface = GetSurface(depth_params);
1159 fb_rect = color_surface->GetScaledRect(); 527 fb_rect = color_surface->GetSurfaceParams().GetRect();
1160 } 528 }
1161 } else if (color_surface != nullptr) { 529 } else if (color_surface) {
1162 fb_rect = color_rect; 530 fb_rect = color_rect;
1163 } else if (depth_surface != nullptr) { 531 } else if (depth_surface) {
1164 fb_rect = depth_rect; 532 fb_rect = depth_rect;
1165 } 533 }
1166 534
1167 if (color_surface != nullptr) {
1168 ValidateSurface(color_surface, boost::icl::first(color_vp_interval),
1169 boost::icl::length(color_vp_interval));
1170 }
1171 if (depth_surface != nullptr) {
1172 ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval),
1173 boost::icl::length(depth_vp_interval));
1174 }
1175
1176 return std::make_tuple(color_surface, depth_surface, fb_rect); 535 return std::make_tuple(color_surface, depth_surface, fb_rect);
1177} 536}
1178 537
1179Surface RasterizerCacheOpenGL::GetFillSurface(const void* config) { 538void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
1180 UNREACHABLE(); 539 surface->LoadGLBuffer();
1181 return {}; 540 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
1182} 541}
1183 542
1184SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams& params) { 543void RasterizerCacheOpenGL::MarkSurfaceAsDirty(const Surface& surface) {
1185 MathUtil::Rectangle<u32> rect{}; 544 if (Settings::values.use_accurate_framebuffers) {
545 // If enabled, always flush dirty surfaces
546 surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
547 surface->FlushGLBuffer();
548 } else {
549 // Otherwise, don't mark surfaces that we write to as cached, because the resulting loads
550 // and flushes are very slow and do not seem to improve accuracy
551 const auto& params{surface->GetSurfaceParams()};
552 Memory::RasterizerMarkRegionCached(params.addr, params.size_in_bytes, false);
553 }
554}
1186 555
1187 Surface match_surface = FindMatch<MatchFlags::TexCopy | MatchFlags::Invalid>( 556Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
1188 surface_cache, params, ScaleMatch::Ignore); 557 if (params.addr == 0 || params.height * params.width == 0) {
558 return {};
559 }
1189 560
1190 if (match_surface != nullptr) { 561 const auto& gpu = Core::System::GetInstance().GPU();
1191 ValidateSurface(match_surface, params.addr, params.size); 562 // Don't try to create any entries in the cache if the address of the texture is invalid.
563 if (gpu.memory_manager->GpuToCpuAddress(params.addr) == boost::none)
564 return {};
1192 565
1193 SurfaceParams match_subrect; 566 // Check for an exact match in existing surfaces
1194 if (params.width != params.stride) { 567 const auto& surface_key{SurfaceKey::Create(params)};
1195 const u32 tiled_size = match_surface->is_tiled ? 8 : 1; 568 const auto& search{surface_cache.find(surface_key)};
1196 match_subrect = params; 569 Surface surface;
1197 match_subrect.width = 570 if (search != surface_cache.end()) {
1198 static_cast<u32>(match_surface->PixelsInBytes(params.width) / tiled_size); 571 surface = search->second;
1199 match_subrect.stride = 572 if (Settings::values.use_accurate_framebuffers) {
1200 static_cast<u32>(match_surface->PixelsInBytes(params.stride) / tiled_size); 573 // Reload the surface from Switch memory
1201 match_subrect.height *= tiled_size; 574 LoadSurface(surface);
1202 } else {
1203 match_subrect = match_surface->FromInterval(params.GetInterval());
1204 ASSERT(match_subrect.GetInterval() == params.GetInterval());
1205 } 575 }
1206 576 } else {
1207 rect = match_surface->GetScaledSubRect(match_subrect); 577 surface = std::make_shared<CachedSurface>(params);
578 RegisterSurface(surface);
579 LoadSurface(surface);
1208 } 580 }
1209 581
1210 return std::make_tuple(match_surface, rect); 582 return surface;
1211} 583}
1212 584
1213void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface, 585Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr cpu_addr) const {
1214 const Surface& dest_surface) { 586 // Tries to find the GPU address of a framebuffer based on the CPU address. This is because
1215 ASSERT(dest_surface->addr <= src_surface->addr && dest_surface->end >= src_surface->end); 587 // final output framebuffers are specified by CPU address, but internally our GPU cache uses
1216 588 // GPU addresses. We iterate through all cached framebuffers, and compare their starting CPU
1217 BlitSurfaces(src_surface, src_surface->GetScaledRect(), dest_surface, 589 // address to the one provided. This is obviously not great, and won't work if the
1218 dest_surface->GetScaledSubRect(*src_surface)); 590 // framebuffer overlaps surfaces.
1219 591
1220 dest_surface->invalid_regions -= src_surface->GetInterval(); 592 std::vector<Surface> surfaces;
1221 dest_surface->invalid_regions += src_surface->invalid_regions; 593 for (const auto& surface : surface_cache) {
1222 594 const auto& params = surface.second->GetSurfaceParams();
1223 SurfaceRegions regions; 595 const VAddr surface_cpu_addr = params.GetCpuAddr();
1224 for (auto& pair : RangeFromInterval(dirty_regions, src_surface->GetInterval())) { 596 if (cpu_addr >= surface_cpu_addr && cpu_addr < (surface_cpu_addr + params.size_in_bytes)) {
1225 if (pair.second == src_surface) { 597 ASSERT_MSG(cpu_addr == surface_cpu_addr, "overlapping surfaces are unsupported");
1226 regions += pair.first; 598 surfaces.push_back(surface.second);
1227 } 599 }
1228 } 600 }
1229 for (auto& interval : regions) {
1230 dirty_regions.set({interval, dest_surface});
1231 }
1232}
1233 601
1234void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, Tegra::GPUVAddr addr, 602 if (surfaces.empty()) {
1235 u64 size) { 603 return {};
1236 if (size == 0)
1237 return;
1238
1239 const SurfaceInterval validate_interval(addr, addr + size);
1240
1241 if (surface->type == SurfaceType::Fill) {
1242 // Sanity check, fill surfaces will always be valid when used
1243 ASSERT(surface->IsRegionValid(validate_interval));
1244 return;
1245 } 604 }
1246 605
1247 while (true) { 606 ASSERT_MSG(surfaces.size() == 1, ">1 surface is unsupported");
1248 const auto it = surface->invalid_regions.find(validate_interval);
1249 if (it == surface->invalid_regions.end())
1250 break;
1251
1252 const auto interval = *it & validate_interval;
1253 // Look for a valid surface to copy from
1254 SurfaceParams params = surface->FromInterval(interval);
1255
1256 Surface copy_surface =
1257 FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
1258 if (copy_surface != nullptr) {
1259 SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface);
1260 CopySurface(copy_surface, surface, copy_interval);
1261 surface->invalid_regions.erase(copy_interval);
1262 continue;
1263 }
1264 607
1265 // Load data from Switch memory 608 return surfaces[0];
1266 FlushRegion(params.addr, params.size);
1267 surface->LoadGLBuffer(params.addr, params.end);
1268 surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
1269 draw_framebuffer.handle);
1270 surface->invalid_regions.erase(params.GetInterval());
1271 }
1272} 609}
1273 610
1274void RasterizerCacheOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size, Surface flush_surface) { 611void RasterizerCacheOpenGL::FlushRegion(Tegra::GPUVAddr /*addr*/, size_t /*size*/) {
1275 if (size == 0) 612 // TODO(bunnei): This is unused in the current implementation of the rasterizer cache. We should
1276 return; 613 // probably implement this in the future, but for now, the `use_accurate_framebufers` setting
1277 614 // can be used to always flush.
1278 const SurfaceInterval flush_interval(addr, addr + size); 615}
1279 SurfaceRegions flushed_intervals;
1280
1281 for (auto& pair : RangeFromInterval(dirty_regions, flush_interval)) {
1282 // small sizes imply that this most likely comes from the cpu, flush the entire region
1283 // the point is to avoid thousands of small writes every frame if the cpu decides to access
1284 // that region, anything higher than 8 you're guaranteed it comes from a service
1285 const auto interval = size <= 8 ? pair.first : pair.first & flush_interval;
1286 auto& surface = pair.second;
1287
1288 if (flush_surface != nullptr && surface != flush_surface)
1289 continue;
1290 616
1291 // Sanity check, this surface is the last one that marked this region dirty 617void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) {
1292 ASSERT(surface->IsRegionValid(interval)); 618 for (const auto& pair : surface_cache) {
619 const auto& surface{pair.second};
620 const auto& params{surface->GetSurfaceParams()};
1293 621
1294 if (surface->type != SurfaceType::Fill) { 622 if (params.IsOverlappingRegion(addr, size)) {
1295 SurfaceParams params = surface->FromInterval(interval); 623 UnregisterSurface(surface);
1296 surface->DownloadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
1297 draw_framebuffer.handle);
1298 } 624 }
1299 surface->FlushGLBuffer(boost::icl::first(interval), boost::icl::last_next(interval));
1300 flushed_intervals += interval;
1301 } 625 }
1302 // Reset dirty regions
1303 dirty_regions -= flushed_intervals;
1304} 626}
1305 627
1306void RasterizerCacheOpenGL::FlushAll() { 628void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) {
1307 FlushRegion(0, Kernel::VMManager::MAX_ADDRESS); 629 const auto& params{surface->GetSurfaceParams()};
1308} 630 const auto& surface_key{SurfaceKey::Create(params)};
631 const auto& search{surface_cache.find(surface_key)};
1309 632
1310void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, u64 size, 633 if (search != surface_cache.end()) {
1311 const Surface& region_owner) { 634 // Registered already
1312 if (size == 0)
1313 return; 635 return;
1314
1315 const SurfaceInterval invalid_interval(addr, addr + size);
1316
1317 if (region_owner != nullptr) {
1318 ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end);
1319 // Surfaces can't have a gap
1320 ASSERT(region_owner->width == region_owner->stride);
1321 region_owner->invalid_regions.erase(invalid_interval);
1322 }
1323
1324 for (auto& pair : RangeFromInterval(surface_cache, invalid_interval)) {
1325 for (auto& cached_surface : pair.second) {
1326 if (cached_surface == region_owner)
1327 continue;
1328
1329 // If cpu is invalidating this region we want to remove it
1330 // to (likely) mark the memory pages as uncached
1331 if (region_owner == nullptr && size <= 8) {
1332 FlushRegion(cached_surface->addr, cached_surface->size, cached_surface);
1333 remove_surfaces.emplace(cached_surface);
1334 continue;
1335 }
1336
1337 const auto interval = cached_surface->GetInterval() & invalid_interval;
1338 cached_surface->invalid_regions.insert(interval);
1339
1340 // Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
1341 if (cached_surface->type == SurfaceType::Fill &&
1342 cached_surface->IsSurfaceFullyInvalid()) {
1343 remove_surfaces.emplace(cached_surface);
1344 }
1345 }
1346 } 636 }
1347 637
1348 if (region_owner != nullptr) 638 surface_cache[surface_key] = surface;
1349 dirty_regions.set({invalid_interval, region_owner}); 639 UpdatePagesCachedCount(params.addr, params.size_in_bytes, 1);
1350 else
1351 dirty_regions.erase(invalid_interval);
1352
1353 for (auto& remove_surface : remove_surfaces) {
1354 if (remove_surface == region_owner) {
1355 Surface expanded_surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(
1356 surface_cache, *region_owner, ScaleMatch::Ignore);
1357 ASSERT(expanded_surface);
1358
1359 if ((region_owner->invalid_regions - expanded_surface->invalid_regions).empty()) {
1360 DuplicateSurface(region_owner, expanded_surface);
1361 } else {
1362 continue;
1363 }
1364 }
1365 UnregisterSurface(remove_surface);
1366 }
1367
1368 remove_surfaces.clear();
1369} 640}
1370 641
1371Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { 642void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
1372 Surface surface = std::make_shared<CachedSurface>(); 643 const auto& params{surface->GetSurfaceParams()};
1373 static_cast<SurfaceParams&>(*surface) = params; 644 const auto& surface_key{SurfaceKey::Create(params)};
1374 645 const auto& search{surface_cache.find(surface_key)};
1375 surface->texture.Create();
1376
1377 surface->gl_buffer_size = 0;
1378 surface->invalid_regions.insert(surface->GetInterval());
1379 AllocateSurfaceTexture(surface->texture.handle,
1380 GetFormatTuple(surface->pixel_format, surface->component_type),
1381 surface->GetScaledWidth(), surface->GetScaledHeight());
1382
1383 return surface;
1384}
1385 646
1386void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) { 647 if (search == surface_cache.end()) {
1387 if (surface->registered) { 648 // Unregistered already
1388 return; 649 return;
1389 } 650 }
1390 surface->registered = true; 651
1391 surface_cache.add({surface->GetInterval(), SurfaceSet{surface}}); 652 UpdatePagesCachedCount(params.addr, params.size_in_bytes, -1);
1392 UpdatePagesCachedCount(surface->addr, surface->size, 1); 653 surface_cache.erase(search);
1393} 654}
1394 655
1395void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { 656template <typename Map, typename Interval>
1396 if (!surface->registered) { 657constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
1397 return; 658 return boost::make_iterator_range(map.equal_range(interval));
1398 }
1399 surface->registered = false;
1400 UpdatePagesCachedCount(surface->addr, surface->size, -1);
1401 surface_cache.subtract({surface->GetInterval(), SurfaceSet{surface}});
1402} 659}
1403 660
1404void RasterizerCacheOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) { 661void RasterizerCacheOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0f43e863d..1bedae992 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -1,57 +1,26 @@
1// Copyright 2015 Citra Emulator Project 1// Copyright 2018 yuzu Emulator Project
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 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <map>
8#include <memory> 9#include <memory>
9#include <set> 10#include <vector>
10#include <tuple>
11#ifdef __GNUC__
12#pragma GCC diagnostic push
13#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
14#endif
15#include <boost/icl/interval_map.hpp> 11#include <boost/icl/interval_map.hpp>
16#include <boost/icl/interval_set.hpp>
17#ifdef __GNUC__
18#pragma GCC diagnostic pop
19#endif
20#include <boost/optional.hpp>
21#include <glad/glad.h>
22#include "common/assert.h"
23#include "common/common_funcs.h"
24#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/hash.h"
25#include "common/math_util.h" 14#include "common/math_util.h"
26#include "video_core/gpu.h" 15#include "video_core/engines/maxwell_3d.h"
27#include "video_core/memory_manager.h"
28#include "video_core/renderer_opengl/gl_resource_manager.h" 16#include "video_core/renderer_opengl/gl_resource_manager.h"
29#include "video_core/textures/texture.h" 17#include "video_core/textures/texture.h"
30 18
31struct CachedSurface; 19class CachedSurface;
32using Surface = std::shared_ptr<CachedSurface>; 20using Surface = std::shared_ptr<CachedSurface>;
33using SurfaceSet = std::set<Surface>;
34
35using SurfaceRegions = boost::icl::interval_set<Tegra::GPUVAddr>;
36using SurfaceMap = boost::icl::interval_map<Tegra::GPUVAddr, Surface>;
37using SurfaceCache = boost::icl::interval_map<Tegra::GPUVAddr, SurfaceSet>;
38
39using SurfaceInterval = SurfaceCache::interval_type;
40static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() &&
41 std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(),
42 "incorrect interval types");
43
44using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>;
45using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; 21using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
46
47using PageMap = boost::icl::interval_map<u64, int>; 22using PageMap = boost::icl::interval_map<u64, int>;
48 23
49enum class ScaleMatch {
50 Exact, // only accept same res scale
51 Upscale, // only allow higher scale than params
52 Ignore // accept every scaled res
53};
54
55struct SurfaceParams { 24struct SurfaceParams {
56 enum class PixelFormat { 25 enum class PixelFormat {
57 ABGR8 = 0, 26 ABGR8 = 0,
@@ -61,12 +30,24 @@ struct SurfaceParams {
61 R8 = 4, 30 R8 = 4,
62 RGBA16F = 5, 31 RGBA16F = 5,
63 R11FG11FB10F = 6, 32 R11FG11FB10F = 6,
64 DXT1 = 7, 33 RGBA32UI = 7,
65 DXT23 = 8, 34 DXT1 = 8,
66 DXT45 = 9, 35 DXT23 = 9,
67 DXN1 = 10, // This is also known as BC4 36 DXT45 = 10,
37 DXN1 = 11, // This is also known as BC4
38 BC7U = 12,
39 ASTC_2D_4X4 = 13,
68 40
69 Max, 41 MaxColorFormat,
42
43 // DepthStencil formats
44 Z24S8 = 14,
45 S8Z24 = 15,
46 Z32F = 16,
47
48 MaxDepthStencilFormat,
49
50 Max = MaxDepthStencilFormat,
70 Invalid = 255, 51 Invalid = 255,
71 }; 52 };
72 53
@@ -92,10 +73,10 @@ struct SurfaceParams {
92 /** 73 /**
93 * Gets the compression factor for the specified PixelFormat. This applies to just the 74 * Gets the compression factor for the specified PixelFormat. This applies to just the
94 * "compressed width" and "compressed height", not the overall compression factor of a 75 * "compressed width" and "compressed height", not the overall compression factor of a
95 * compressed image. This is used for maintaining proper surface sizes for compressed texture 76 * compressed image. This is used for maintaining proper surface sizes for compressed
96 * formats. 77 * texture formats.
97 */ 78 */
98 static constexpr u32 GetCompresssionFactor(PixelFormat format) { 79 static constexpr u32 GetCompressionFactor(PixelFormat format) {
99 if (format == PixelFormat::Invalid) 80 if (format == PixelFormat::Invalid)
100 return 0; 81 return 0;
101 82
@@ -107,18 +88,21 @@ struct SurfaceParams {
107 1, // R8 88 1, // R8
108 1, // RGBA16F 89 1, // RGBA16F
109 1, // R11FG11FB10F 90 1, // R11FG11FB10F
91 1, // RGBA32UI
110 4, // DXT1 92 4, // DXT1
111 4, // DXT23 93 4, // DXT23
112 4, // DXT45 94 4, // DXT45
113 4, // DXN1 95 4, // DXN1
96 4, // BC7U
97 4, // ASTC_2D_4X4
98 1, // Z24S8
99 1, // S8Z24
100 1, // Z32F
114 }}; 101 }};
115 102
116 ASSERT(static_cast<size_t>(format) < compression_factor_table.size()); 103 ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
117 return compression_factor_table[static_cast<size_t>(format)]; 104 return compression_factor_table[static_cast<size_t>(format)];
118 } 105 }
119 u32 GetCompresssionFactor() const {
120 return GetCompresssionFactor(pixel_format);
121 }
122 106
123 static constexpr u32 GetFormatBpp(PixelFormat format) { 107 static constexpr u32 GetFormatBpp(PixelFormat format) {
124 if (format == PixelFormat::Invalid) 108 if (format == PixelFormat::Invalid)
@@ -132,10 +116,16 @@ struct SurfaceParams {
132 8, // R8 116 8, // R8
133 64, // RGBA16F 117 64, // RGBA16F
134 32, // R11FG11FB10F 118 32, // R11FG11FB10F
119 128, // RGBA32UI
135 64, // DXT1 120 64, // DXT1
136 128, // DXT23 121 128, // DXT23
137 128, // DXT45 122 128, // DXT45
138 64, // DXN1 123 64, // DXN1
124 128, // BC7U
125 32, // ASTC_2D_4X4
126 32, // Z24S8
127 32, // S8Z24
128 32, // Z32F
139 }}; 129 }};
140 130
141 ASSERT(static_cast<size_t>(format) < bpp_table.size()); 131 ASSERT(static_cast<size_t>(format) < bpp_table.size());
@@ -145,6 +135,20 @@ struct SurfaceParams {
145 return GetFormatBpp(pixel_format); 135 return GetFormatBpp(pixel_format);
146 } 136 }
147 137
138 static PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
139 switch (format) {
140 case Tegra::DepthFormat::S8_Z24_UNORM:
141 return PixelFormat::S8Z24;
142 case Tegra::DepthFormat::Z24_S8_UNORM:
143 return PixelFormat::Z24S8;
144 case Tegra::DepthFormat::Z32_FLOAT:
145 return PixelFormat::Z32F;
146 default:
147 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
148 UNREACHABLE();
149 }
150 }
151
148 static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { 152 static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
149 switch (format) { 153 switch (format) {
150 case Tegra::RenderTargetFormat::RGBA8_UNORM: 154 case Tegra::RenderTargetFormat::RGBA8_UNORM:
@@ -156,18 +160,10 @@ struct SurfaceParams {
156 return PixelFormat::RGBA16F; 160 return PixelFormat::RGBA16F;
157 case Tegra::RenderTargetFormat::R11G11B10_FLOAT: 161 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
158 return PixelFormat::R11FG11FB10F; 162 return PixelFormat::R11FG11FB10F;
163 case Tegra::RenderTargetFormat::RGBA32_UINT:
164 return PixelFormat::RGBA32UI;
159 default: 165 default:
160 NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 166 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
161 UNREACHABLE();
162 }
163 }
164
165 static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
166 switch (format) {
167 case Tegra::FramebufferConfig::PixelFormat::ABGR8:
168 return PixelFormat::ABGR8;
169 default:
170 NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
171 UNREACHABLE(); 167 UNREACHABLE();
172 } 168 }
173 } 169 }
@@ -189,6 +185,8 @@ struct SurfaceParams {
189 return PixelFormat::RGBA16F; 185 return PixelFormat::RGBA16F;
190 case Tegra::Texture::TextureFormat::BF10GF11RF11: 186 case Tegra::Texture::TextureFormat::BF10GF11RF11:
191 return PixelFormat::R11FG11FB10F; 187 return PixelFormat::R11FG11FB10F;
188 case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
189 return PixelFormat::RGBA32UI;
192 case Tegra::Texture::TextureFormat::DXT1: 190 case Tegra::Texture::TextureFormat::DXT1:
193 return PixelFormat::DXT1; 191 return PixelFormat::DXT1;
194 case Tegra::Texture::TextureFormat::DXT23: 192 case Tegra::Texture::TextureFormat::DXT23:
@@ -197,8 +195,12 @@ struct SurfaceParams {
197 return PixelFormat::DXT45; 195 return PixelFormat::DXT45;
198 case Tegra::Texture::TextureFormat::DXN1: 196 case Tegra::Texture::TextureFormat::DXN1:
199 return PixelFormat::DXN1; 197 return PixelFormat::DXN1;
198 case Tegra::Texture::TextureFormat::BC7U:
199 return PixelFormat::BC7U;
200 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
201 return PixelFormat::ASTC_2D_4X4;
200 default: 202 default:
201 NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 203 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
202 UNREACHABLE(); 204 UNREACHABLE();
203 } 205 }
204 } 206 }
@@ -220,6 +222,8 @@ struct SurfaceParams {
220 return Tegra::Texture::TextureFormat::R16_G16_B16_A16; 222 return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
221 case PixelFormat::R11FG11FB10F: 223 case PixelFormat::R11FG11FB10F:
222 return Tegra::Texture::TextureFormat::BF10GF11RF11; 224 return Tegra::Texture::TextureFormat::BF10GF11RF11;
225 case PixelFormat::RGBA32UI:
226 return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
223 case PixelFormat::DXT1: 227 case PixelFormat::DXT1:
224 return Tegra::Texture::TextureFormat::DXT1; 228 return Tegra::Texture::TextureFormat::DXT1;
225 case PixelFormat::DXT23: 229 case PixelFormat::DXT23:
@@ -228,6 +232,23 @@ struct SurfaceParams {
228 return Tegra::Texture::TextureFormat::DXT45; 232 return Tegra::Texture::TextureFormat::DXT45;
229 case PixelFormat::DXN1: 233 case PixelFormat::DXN1:
230 return Tegra::Texture::TextureFormat::DXN1; 234 return Tegra::Texture::TextureFormat::DXN1;
235 case PixelFormat::BC7U:
236 return Tegra::Texture::TextureFormat::BC7U;
237 case PixelFormat::ASTC_2D_4X4:
238 return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
239 default:
240 UNREACHABLE();
241 }
242 }
243
244 static Tegra::DepthFormat DepthFormatFromPixelFormat(PixelFormat format) {
245 switch (format) {
246 case PixelFormat::S8Z24:
247 return Tegra::DepthFormat::S8_Z24_UNORM;
248 case PixelFormat::Z24S8:
249 return Tegra::DepthFormat::Z24_S8_UNORM;
250 case PixelFormat::Z32F:
251 return Tegra::DepthFormat::Z32_FLOAT;
231 default: 252 default:
232 UNREACHABLE(); 253 UNREACHABLE();
233 } 254 }
@@ -239,7 +260,7 @@ struct SurfaceParams {
239 case Tegra::Texture::ComponentType::UNORM: 260 case Tegra::Texture::ComponentType::UNORM:
240 return ComponentType::UNorm; 261 return ComponentType::UNorm;
241 default: 262 default:
242 NGLOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type)); 263 LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
243 UNREACHABLE(); 264 UNREACHABLE();
244 } 265 }
245 } 266 }
@@ -254,215 +275,153 @@ struct SurfaceParams {
254 case Tegra::RenderTargetFormat::RGBA16_FLOAT: 275 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
255 case Tegra::RenderTargetFormat::R11G11B10_FLOAT: 276 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
256 return ComponentType::Float; 277 return ComponentType::Float;
278 case Tegra::RenderTargetFormat::RGBA32_UINT:
279 return ComponentType::UInt;
257 default: 280 default:
258 NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 281 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
259 UNREACHABLE(); 282 UNREACHABLE();
260 } 283 }
261 } 284 }
262 285
263 static ComponentType ComponentTypeFromGPUPixelFormat( 286 static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
264 Tegra::FramebufferConfig::PixelFormat format) {
265 switch (format) { 287 switch (format) {
266 case Tegra::FramebufferConfig::PixelFormat::ABGR8: 288 case Tegra::FramebufferConfig::PixelFormat::ABGR8:
267 return ComponentType::UNorm; 289 return PixelFormat::ABGR8;
268 default: 290 default:
269 NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 291 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
270 UNREACHABLE(); 292 UNREACHABLE();
271 } 293 }
272 } 294 }
273 295
274 static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { 296 static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
275 SurfaceType a_type = GetFormatType(pixel_format_a); 297 switch (format) {
276 SurfaceType b_type = GetFormatType(pixel_format_b); 298 case Tegra::DepthFormat::S8_Z24_UNORM:
277 299 case Tegra::DepthFormat::Z24_S8_UNORM:
278 if (a_type == SurfaceType::ColorTexture && b_type == SurfaceType::ColorTexture) { 300 return ComponentType::UNorm;
279 return true; 301 case Tegra::DepthFormat::Z32_FLOAT:
280 } 302 return ComponentType::Float;
281 303 default:
282 if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { 304 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
283 return true; 305 UNREACHABLE();
284 }
285
286 if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) {
287 return true;
288 } 306 }
289
290 return false;
291 } 307 }
292 308
293 static SurfaceType GetFormatType(PixelFormat pixel_format) { 309 static SurfaceType GetFormatType(PixelFormat pixel_format) {
294 if (static_cast<size_t>(pixel_format) < MaxPixelFormat) { 310 if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxColorFormat)) {
295 return SurfaceType::ColorTexture; 311 return SurfaceType::ColorTexture;
296 } 312 }
297 313
314 if (static_cast<size_t>(pixel_format) <
315 static_cast<size_t>(PixelFormat::MaxDepthStencilFormat)) {
316 return SurfaceType::DepthStencil;
317 }
318
298 // TODO(Subv): Implement the other formats 319 // TODO(Subv): Implement the other formats
299 ASSERT(false); 320 ASSERT(false);
300 321
301 return SurfaceType::Invalid; 322 return SurfaceType::Invalid;
302 } 323 }
303 324
304 /// Update the params "size", "end" and "type" from the already set "addr", "width", "height" 325 /// Returns the rectangle corresponding to this surface
305 /// and "pixel_format" 326 MathUtil::Rectangle<u32> GetRect() const;
306 void UpdateParams() {
307 if (stride == 0) {
308 stride = width;
309 }
310 type = GetFormatType(pixel_format);
311 size = !is_tiled ? BytesInPixels(stride * (height - 1) + width)
312 : BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8);
313 end = addr + size;
314 }
315
316 SurfaceInterval GetInterval() const {
317 return SurfaceInterval::right_open(addr, end);
318 }
319
320 // Returns the outer rectangle containing "interval"
321 SurfaceParams FromInterval(SurfaceInterval interval) const;
322
323 SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const;
324
325 // Returns the region of the biggest valid rectange within interval
326 SurfaceInterval GetCopyableInterval(const Surface& src_surface) const;
327
328 /**
329 * Gets the actual width (in pixels) of the surface. This is provided because `width` is used
330 * for tracking the surface region in memory, which may be compressed for certain formats. In
331 * this scenario, `width` is actually the compressed width.
332 */
333 u32 GetActualWidth() const {
334 return width * GetCompresssionFactor();
335 }
336
337 /**
338 * Gets the actual height (in pixels) of the surface. This is provided because `height` is used
339 * for tracking the surface region in memory, which may be compressed for certain formats. In
340 * this scenario, `height` is actually the compressed height.
341 */
342 u32 GetActualHeight() const {
343 return height * GetCompresssionFactor();
344 }
345 327
346 u32 GetScaledWidth() const { 328 /// Returns the size of this surface in bytes, adjusted for compression
347 return width * res_scale; 329 size_t SizeInBytes() const {
330 const u32 compression_factor{GetCompressionFactor(pixel_format)};
331 ASSERT(width % compression_factor == 0);
332 ASSERT(height % compression_factor == 0);
333 return (width / compression_factor) * (height / compression_factor) *
334 GetFormatBpp(pixel_format) / CHAR_BIT;
348 } 335 }
349 336
350 u32 GetScaledHeight() const { 337 /// Returns the CPU virtual address for this surface
351 return height * res_scale; 338 VAddr GetCpuAddr() const;
352 }
353 339
354 MathUtil::Rectangle<u32> GetRect() const { 340 /// Returns true if the specified region overlaps with this surface's region in Switch memory
355 return {0, height, width, 0}; 341 bool IsOverlappingRegion(Tegra::GPUVAddr region_addr, size_t region_size) const {
342 return addr <= (region_addr + region_size) && region_addr <= (addr + size_in_bytes);
356 } 343 }
357 344
358 MathUtil::Rectangle<u32> GetScaledRect() const { 345 /// Creates SurfaceParams from a texture configuration
359 return {0, GetScaledHeight(), GetScaledWidth(), 0}; 346 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config);
360 } 347
348 /// Creates SurfaceParams from a framebuffer configuration
349 static SurfaceParams CreateForFramebuffer(
350 const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config);
351
352 /// Creates SurfaceParams for a depth buffer configuration
353 static SurfaceParams CreateForDepthBuffer(
354 const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config,
355 Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format);
356
357 Tegra::GPUVAddr addr;
358 bool is_tiled;
359 u32 block_height;
360 PixelFormat pixel_format;
361 ComponentType component_type;
362 SurfaceType type;
363 u32 width;
364 u32 height;
365 u32 unaligned_height;
366 size_t size_in_bytes;
367};
361 368
362 u64 PixelsInBytes(u64 size) const { 369/// Hashable variation of SurfaceParams, used for a key in the surface cache
363 return size * CHAR_BIT / GetFormatBpp(pixel_format); 370struct SurfaceKey : Common::HashableStruct<SurfaceParams> {
371 static SurfaceKey Create(const SurfaceParams& params) {
372 SurfaceKey res;
373 res.state = params;
374 return res;
364 } 375 }
376};
365 377
366 u64 BytesInPixels(u64 pixels) const { 378namespace std {
367 return pixels * GetFormatBpp(pixel_format) / CHAR_BIT; 379template <>
380struct hash<SurfaceKey> {
381 size_t operator()(const SurfaceKey& k) const {
382 return k.Hash();
368 } 383 }
369
370 VAddr GetCpuAddr() const;
371
372 bool ExactMatch(const SurfaceParams& other_surface) const;
373 bool CanSubRect(const SurfaceParams& sub_surface) const;
374 bool CanExpand(const SurfaceParams& expanded_surface) const;
375 bool CanTexCopy(const SurfaceParams& texcopy_params) const;
376
377 MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
378 MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const;
379
380 Tegra::GPUVAddr addr = 0;
381 Tegra::GPUVAddr end = 0;
382 boost::optional<VAddr> cpu_addr;
383 u64 size = 0;
384
385 u32 width = 0;
386 u32 height = 0;
387 u32 stride = 0;
388 u32 block_height = 0;
389 u16 res_scale = 1;
390
391 bool is_tiled = false;
392 PixelFormat pixel_format = PixelFormat::Invalid;
393 SurfaceType type = SurfaceType::Invalid;
394 ComponentType component_type = ComponentType::Invalid;
395}; 384};
385} // namespace std
396 386
397struct CachedSurface : SurfaceParams { 387class CachedSurface final {
398 bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; 388public:
399 bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; 389 CachedSurface(const SurfaceParams& params);
400
401 bool IsRegionValid(SurfaceInterval interval) const {
402 return (invalid_regions.find(interval) == invalid_regions.end());
403 }
404 390
405 bool IsSurfaceFullyInvalid() const { 391 const OGLTexture& Texture() const {
406 return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval()); 392 return texture;
407 } 393 }
408 394
409 bool registered = false; 395 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
410 SurfaceRegions invalid_regions; 396 if (format == SurfaceParams::PixelFormat::Invalid)
411
412 u64 fill_size = 0; /// Number of bytes to read from fill_data
413 std::array<u8, 4> fill_data;
414
415 OGLTexture texture;
416
417 static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
418 if (format == PixelFormat::Invalid)
419 return 0; 397 return 0;
420 398
421 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 399 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
422 } 400 }
423 401
424 std::unique_ptr<u8[]> gl_buffer; 402 const SurfaceParams& GetSurfaceParams() const {
425 size_t gl_buffer_size = 0; 403 return params;
404 }
426 405
427 // Read/Write data in Switch memory to/from gl_buffer 406 // Read/Write data in Switch memory to/from gl_buffer
428 void LoadGLBuffer(Tegra::GPUVAddr load_start, Tegra::GPUVAddr load_end); 407 void LoadGLBuffer();
429 void FlushGLBuffer(Tegra::GPUVAddr flush_start, Tegra::GPUVAddr flush_end); 408 void FlushGLBuffer();
430 409
431 // Upload/Download data in gl_buffer in/to this surface's texture 410 // Upload/Download data in gl_buffer in/to this surface's texture
432 void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, 411 void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
433 GLuint draw_fb_handle); 412 void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
434 void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, 413
435 GLuint draw_fb_handle); 414private:
415 OGLTexture texture;
416 std::vector<u8> gl_buffer;
417 SurfaceParams params;
436}; 418};
437 419
438class RasterizerCacheOpenGL : NonCopyable { 420class RasterizerCacheOpenGL final : NonCopyable {
439public: 421public:
440 RasterizerCacheOpenGL(); 422 RasterizerCacheOpenGL();
441 ~RasterizerCacheOpenGL(); 423 ~RasterizerCacheOpenGL();
442 424
443 /// Blit one surface's texture to another
444 bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect,
445 const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect);
446
447 void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect,
448 GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect);
449
450 /// Copy one surface's region to another
451 void CopySurface(const Surface& src_surface, const Surface& dst_surface,
452 SurfaceInterval copy_interval);
453
454 /// Load a texture from Switch memory to OpenGL and cache it (if not already cached)
455 Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
456 bool load_if_create);
457
458 /// Tries to find a framebuffer GPU address based on the provided CPU address
459 boost::optional<Tegra::GPUVAddr> TryFindFramebufferGpuAddress(VAddr cpu_addr) const;
460
461 /// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
462 /// Switch memory to OpenGL and caches it (if not already cached)
463 SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale,
464 bool load_if_create);
465
466 /// Get a surface based on the texture configuration 425 /// Get a surface based on the texture configuration
467 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); 426 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
468 427
@@ -470,29 +429,21 @@ public:
470 SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, 429 SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
471 const MathUtil::Rectangle<s32>& viewport); 430 const MathUtil::Rectangle<s32>& viewport);
472 431
473 /// Get a surface that matches the fill config 432 /// Marks the specified surface as "dirty", in that it is out of sync with Switch memory
474 Surface GetFillSurface(const void* config); 433 void MarkSurfaceAsDirty(const Surface& surface);
475 434
476 /// Get a surface that matches a "texture copy" display transfer config 435 /// Tries to find a framebuffer GPU address based on the provided CPU address
477 SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params); 436 Surface TryFindFramebufferSurface(VAddr cpu_addr) const;
478 437
479 /// Write any cached resources overlapping the region back to memory (if dirty) 438 /// Write any cached resources overlapping the region back to memory (if dirty)
480 void FlushRegion(Tegra::GPUVAddr addr, u64 size, Surface flush_surface = nullptr); 439 void FlushRegion(Tegra::GPUVAddr addr, size_t size);
481
482 /// Mark region as being invalidated by region_owner (nullptr if Switch memory)
483 void InvalidateRegion(Tegra::GPUVAddr addr, u64 size, const Surface& region_owner);
484 440
485 /// Flush all cached resources tracked by this cache manager 441 /// Mark the specified region as being invalidated
486 void FlushAll(); 442 void InvalidateRegion(Tegra::GPUVAddr addr, size_t size);
487 443
488private: 444private:
489 void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface); 445 void LoadSurface(const Surface& surface);
490 446 Surface GetSurface(const SurfaceParams& params);
491 /// Update surface's texture for given region when necessary
492 void ValidateSurface(const Surface& surface, Tegra::GPUVAddr addr, u64 size);
493
494 /// Create a new surface
495 Surface CreateSurface(const SurfaceParams& params);
496 447
497 /// Register surface into the cache 448 /// Register surface into the cache
498 void RegisterSurface(const Surface& surface); 449 void RegisterSurface(const Surface& surface);
@@ -503,18 +454,9 @@ private:
503 /// Increase/decrease the number of surface in pages touching the specified region 454 /// Increase/decrease the number of surface in pages touching the specified region
504 void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta); 455 void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta);
505 456
506 SurfaceCache surface_cache; 457 std::unordered_map<SurfaceKey, Surface> surface_cache;
507 PageMap cached_pages; 458 PageMap cached_pages;
508 SurfaceMap dirty_regions;
509 SurfaceSet remove_surfaces;
510 459
511 OGLFramebuffer read_framebuffer; 460 OGLFramebuffer read_framebuffer;
512 OGLFramebuffer draw_framebuffer; 461 OGLFramebuffer draw_framebuffer;
513
514 OGLVertexArray attributeless_vao;
515 OGLBuffer d24s8_abgr_buffer;
516 GLsizeiptr d24s8_abgr_buffer_size;
517 OGLProgram d24s8_abgr_shader;
518 GLint d24s8_abgr_tbo_size_u_id;
519 GLint d24s8_abgr_viewport_u_id;
520}; 462};
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 93f9172e7..0fed93ca5 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -38,7 +38,7 @@ public:
38 if (handle == 0) 38 if (handle == 0)
39 return; 39 return;
40 glDeleteTextures(1, &handle); 40 glDeleteTextures(1, &handle);
41 OpenGLState::GetCurState().ResetTexture(handle).Apply(); 41 OpenGLState::GetCurState().UnbindTexture(handle).Apply();
42 handle = 0; 42 handle = 0;
43 } 43 }
44 44
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 67726e7c6..5914077e8 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -9,6 +9,7 @@
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/engines/shader_bytecode.h" 11#include "video_core/engines/shader_bytecode.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h"
12#include "video_core/renderer_opengl/gl_shader_decompiler.h" 13#include "video_core/renderer_opengl/gl_shader_decompiler.h"
13 14
14namespace GLShader { 15namespace GLShader {
@@ -16,6 +17,7 @@ namespace Decompiler {
16 17
17using Tegra::Shader::Attribute; 18using Tegra::Shader::Attribute;
18using Tegra::Shader::Instruction; 19using Tegra::Shader::Instruction;
20using Tegra::Shader::LogicOperation;
19using Tegra::Shader::OpCode; 21using Tegra::Shader::OpCode;
20using Tegra::Shader::Register; 22using Tegra::Shader::Register;
21using Tegra::Shader::Sampler; 23using Tegra::Shader::Sampler;
@@ -266,6 +268,27 @@ public:
266 } 268 }
267 269
268 /** 270 /**
271 * Returns code that does an integer size conversion for the specified size.
272 * @param value Value to perform integer size conversion on.
273 * @param size Register size to use for conversion instructions.
274 * @returns GLSL string corresponding to the value converted to the specified size.
275 */
276 static std::string ConvertIntegerSize(const std::string& value, Register::Size size) {
277 switch (size) {
278 case Register::Size::Byte:
279 return "((" + value + " << 24) >> 24)";
280 case Register::Size::Short:
281 return "((" + value + " << 16) >> 16)";
282 case Register::Size::Word:
283 // Default - do nothing
284 return value;
285 default:
286 LOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size));
287 UNREACHABLE();
288 }
289 }
290
291 /**
269 * Gets a register as an float. 292 * Gets a register as an float.
270 * @param reg The register to get. 293 * @param reg The register to get.
271 * @param elem The element to use for the operation. 294 * @param elem The element to use for the operation.
@@ -281,15 +304,18 @@ public:
281 * @param reg The register to get. 304 * @param reg The register to get.
282 * @param elem The element to use for the operation. 305 * @param elem The element to use for the operation.
283 * @param is_signed Whether to get the register as a signed (or unsigned) integer. 306 * @param is_signed Whether to get the register as a signed (or unsigned) integer.
307 * @param size Register size to use for conversion instructions.
284 * @returns GLSL string corresponding to the register as an integer. 308 * @returns GLSL string corresponding to the register as an integer.
285 */ 309 */
286 std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0, 310 std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0, bool is_signed = true,
287 bool is_signed = true) { 311 Register::Size size = Register::Size::Word) {
288 const std::string func = GetGLSLConversionFunc( 312 const std::string func = GetGLSLConversionFunc(
289 GLSLRegister::Type::Float, 313 GLSLRegister::Type::Float,
290 is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger); 314 is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger);
291 315
292 return func + '(' + GetRegister(reg, elem) + ')'; 316 std::string value = func + '(' + GetRegister(reg, elem) + ')';
317
318 return ConvertIntegerSize(value, size);
293 } 319 }
294 320
295 /** 321 /**
@@ -299,13 +325,15 @@ public:
299 * @param value The code representing the value to assign. 325 * @param value The code representing the value to assign.
300 * @param dest_num_components Number of components in the destination. 326 * @param dest_num_components Number of components in the destination.
301 * @param value_num_components Number of components in the value. 327 * @param value_num_components Number of components in the value.
302 * @param is_abs Optional, when True, applies absolute value to output. 328 * @param is_saturated Optional, when True, saturates the provided value.
303 * @param dest_elem Optional, the destination element to use for the operation. 329 * @param dest_elem Optional, the destination element to use for the operation.
304 */ 330 */
305 void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, 331 void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
306 u64 dest_num_components, u64 value_num_components, bool is_abs = false, 332 u64 dest_num_components, u64 value_num_components,
307 u64 dest_elem = 0) { 333 bool is_saturated = false, u64 dest_elem = 0) {
308 SetRegister(reg, elem, value, dest_num_components, value_num_components, is_abs, dest_elem); 334
335 SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
336 dest_num_components, value_num_components, dest_elem);
309 } 337 }
310 338
311 /** 339 /**
@@ -315,18 +343,22 @@ public:
315 * @param value The code representing the value to assign. 343 * @param value The code representing the value to assign.
316 * @param dest_num_components Number of components in the destination. 344 * @param dest_num_components Number of components in the destination.
317 * @param value_num_components Number of components in the value. 345 * @param value_num_components Number of components in the value.
318 * @param is_abs Optional, when True, applies absolute value to output. 346 * @param is_saturated Optional, when True, saturates the provided value.
319 * @param dest_elem Optional, the destination element to use for the operation. 347 * @param dest_elem Optional, the destination element to use for the operation.
348 * @param size Register size to use for conversion instructions.
320 */ 349 */
321 void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, 350 void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
322 const std::string& value, u64 dest_num_components, 351 const std::string& value, u64 dest_num_components,
323 u64 value_num_components, bool is_abs = false, u64 dest_elem = 0) { 352 u64 value_num_components, bool is_saturated = false,
353 u64 dest_elem = 0, Register::Size size = Register::Size::Word) {
354 ASSERT_MSG(!is_saturated, "Unimplemented");
355
324 const std::string func = GetGLSLConversionFunc( 356 const std::string func = GetGLSLConversionFunc(
325 is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger, 357 is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger,
326 GLSLRegister::Type::Float); 358 GLSLRegister::Type::Float);
327 359
328 SetRegister(reg, elem, func + '(' + value + ')', dest_num_components, value_num_components, 360 SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
329 is_abs, dest_elem); 361 dest_num_components, value_num_components, dest_elem);
330 } 362 }
331 363
332 /** 364 /**
@@ -366,7 +398,8 @@ public:
366 /// Generates code representing a uniform (C buffer) register, interpreted as the input type. 398 /// Generates code representing a uniform (C buffer) register, interpreted as the input type.
367 std::string GetUniform(u64 index, u64 offset, GLSLRegister::Type type) { 399 std::string GetUniform(u64 index, u64 offset, GLSLRegister::Type type) {
368 declr_const_buffers[index].MarkAsUsed(index, offset, stage); 400 declr_const_buffers[index].MarkAsUsed(index, offset, stage);
369 std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset) + ']'; 401 std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset / 4) + "][" +
402 std::to_string(offset % 4) + ']';
370 403
371 if (type == GLSLRegister::Type::Float) { 404 if (type == GLSLRegister::Type::Float) {
372 return value; 405 return value;
@@ -380,8 +413,12 @@ public:
380 std::string GetUniformIndirect(u64 index, s64 offset, const Register& index_reg, 413 std::string GetUniformIndirect(u64 index, s64 offset, const Register& index_reg,
381 GLSLRegister::Type type) { 414 GLSLRegister::Type type) {
382 declr_const_buffers[index].MarkAsUsedIndirect(index, stage); 415 declr_const_buffers[index].MarkAsUsedIndirect(index, stage);
383 std::string value = 'c' + std::to_string(index) + "[(floatBitsToInt(" + 416
384 GetRegister(index_reg, 0) + ") + " + std::to_string(offset) + ") / 4]"; 417 std::string final_offset = "((floatBitsToInt(" + GetRegister(index_reg, 0) + ") + " +
418 std::to_string(offset) + ") / 4)";
419
420 std::string value =
421 'c' + std::to_string(index) + '[' + final_offset + " / 4][" + final_offset + " % 4]";
385 422
386 if (type == GLSLRegister::Type::Float) { 423 if (type == GLSLRegister::Type::Float) {
387 return value; 424 return value;
@@ -423,9 +460,10 @@ public:
423 460
424 unsigned const_buffer_layout = 0; 461 unsigned const_buffer_layout = 0;
425 for (const auto& entry : GetConstBuffersDeclarations()) { 462 for (const auto& entry : GetConstBuffersDeclarations()) {
426 declarations.AddLine("layout(std430) buffer " + entry.GetName()); 463 declarations.AddLine("layout(std140) uniform " + entry.GetName());
427 declarations.AddLine('{'); 464 declarations.AddLine('{');
428 declarations.AddLine(" float c" + std::to_string(entry.GetIndex()) + "[];"); 465 declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
466 "[MAX_CONSTBUFFER_ELEMENTS];");
429 declarations.AddLine("};"); 467 declarations.AddLine("};");
430 declarations.AddNewLine(); 468 declarations.AddNewLine();
431 ++const_buffer_layout; 469 ++const_buffer_layout;
@@ -500,13 +538,11 @@ private:
500 * @param value The code representing the value to assign. 538 * @param value The code representing the value to assign.
501 * @param dest_num_components Number of components in the destination. 539 * @param dest_num_components Number of components in the destination.
502 * @param value_num_components Number of components in the value. 540 * @param value_num_components Number of components in the value.
503 * @param is_abs Optional, when True, applies absolute value to output.
504 * @param dest_elem Optional, the destination element to use for the operation. 541 * @param dest_elem Optional, the destination element to use for the operation.
505 */ 542 */
506 void SetRegister(const Register& reg, u64 elem, const std::string& value, 543 void SetRegister(const Register& reg, u64 elem, const std::string& value,
507 u64 dest_num_components, u64 value_num_components, bool is_abs, 544 u64 dest_num_components, u64 value_num_components, u64 dest_elem) {
508 u64 dest_elem) { 545 std::string dest = GetRegister(reg, static_cast<u32>(dest_elem));
509 std::string dest = GetRegister(reg, dest_elem);
510 if (dest_num_components > 1) { 546 if (dest_num_components > 1) {
511 dest += GetSwizzle(elem); 547 dest += GetSwizzle(elem);
512 } 548 }
@@ -516,8 +552,6 @@ private:
516 src += GetSwizzle(elem); 552 src += GetSwizzle(elem);
517 } 553 }
518 554
519 src = is_abs ? "abs(" + src + ')' : src;
520
521 shader.AddLine(dest + " = " + src + ';'); 555 shader.AddLine(dest + " = " + src + ';');
522 } 556 }
523 557
@@ -547,7 +581,7 @@ private:
547 return "input_attribute_" + std::to_string(index); 581 return "input_attribute_" + std::to_string(index);
548 } 582 }
549 583
550 NGLOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); 584 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index);
551 UNREACHABLE(); 585 UNREACHABLE();
552 } 586 }
553 } 587 }
@@ -565,7 +599,7 @@ private:
565 return "output_attribute_" + std::to_string(index); 599 return "output_attribute_" + std::to_string(index);
566 } 600 }
567 601
568 NGLOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); 602 LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index);
569 UNREACHABLE(); 603 UNREACHABLE();
570 } 604 }
571 } 605 }
@@ -685,21 +719,31 @@ private:
685 /** 719 /**
686 * Returns the comparison string to use to compare two values in the 'set' family of 720 * Returns the comparison string to use to compare two values in the 'set' family of
687 * instructions. 721 * instructions.
688 * @params condition The condition used in the 'set'-family instruction. 722 * @param condition The condition used in the 'set'-family instruction.
723 * @param op_a First operand to use for the comparison.
724 * @param op_b Second operand to use for the comparison.
689 * @returns String corresponding to the GLSL operator that matches the desired comparison. 725 * @returns String corresponding to the GLSL operator that matches the desired comparison.
690 */ 726 */
691 std::string GetPredicateComparison(Tegra::Shader::PredCondition condition) const { 727 std::string GetPredicateComparison(Tegra::Shader::PredCondition condition,
728 const std::string& op_a, const std::string& op_b) const {
692 using Tegra::Shader::PredCondition; 729 using Tegra::Shader::PredCondition;
693 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { 730 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
694 {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, 731 {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
695 {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, 732 {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
696 {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, 733 {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="},
734 {PredCondition::NotEqualWithNan, "!="},
697 }; 735 };
698 736
699 auto comparison = PredicateComparisonStrings.find(condition); 737 const auto& comparison{PredicateComparisonStrings.find(condition)};
700 ASSERT_MSG(comparison != PredicateComparisonStrings.end(), 738 ASSERT_MSG(comparison != PredicateComparisonStrings.end(),
701 "Unknown predicate comparison operation"); 739 "Unknown predicate comparison operation");
702 return comparison->second; 740
741 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'};
742 if (condition == PredCondition::NotEqualWithNan) {
743 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')';
744 }
745
746 return predicate;
703 } 747 }
704 748
705 /** 749 /**
@@ -733,6 +777,31 @@ private:
733 return (absolute_offset % SchedPeriod) == 0; 777 return (absolute_offset % SchedPeriod) == 0;
734 } 778 }
735 779
780 void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
781 const std::string& op_b) {
782 switch (logic_op) {
783 case LogicOperation::And: {
784 regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " & " + op_b + ')', 1, 1);
785 break;
786 }
787 case LogicOperation::Or: {
788 regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " | " + op_b + ')', 1, 1);
789 break;
790 }
791 case LogicOperation::Xor: {
792 regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " ^ " + op_b + ')', 1, 1);
793 break;
794 }
795 case LogicOperation::PassB: {
796 regs.SetRegisterToInteger(dest, true, 0, op_b, 1, 1);
797 break;
798 }
799 default:
800 LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op));
801 UNREACHABLE();
802 }
803 }
804
736 /** 805 /**
737 * Compiles a single instruction from Tegra to GLSL. 806 * Compiles a single instruction from Tegra to GLSL.
738 * @param offset the offset of the Tegra shader instruction. 807 * @param offset the offset of the Tegra shader instruction.
@@ -750,8 +819,9 @@ private:
750 819
751 // Decoding failure 820 // Decoding failure
752 if (!opcode) { 821 if (!opcode) {
753 NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); 822 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value);
754 UNREACHABLE(); 823 UNREACHABLE();
824 return offset + 1;
755 } 825 }
756 826
757 shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName()); 827 shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName());
@@ -770,22 +840,25 @@ private:
770 840
771 switch (opcode->GetType()) { 841 switch (opcode->GetType()) {
772 case OpCode::Type::Arithmetic: { 842 case OpCode::Type::Arithmetic: {
773 std::string op_a = instr.alu.negate_a ? "-" : ""; 843 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
774 op_a += regs.GetRegisterAsFloat(instr.gpr8);
775 if (instr.alu.abs_a) { 844 if (instr.alu.abs_a) {
776 op_a = "abs(" + op_a + ')'; 845 op_a = "abs(" + op_a + ')';
777 } 846 }
778 847
779 std::string op_b = instr.alu.negate_b ? "-" : ""; 848 if (instr.alu.negate_a) {
849 op_a = "-(" + op_a + ')';
850 }
851
852 std::string op_b;
780 853
781 if (instr.is_b_imm) { 854 if (instr.is_b_imm) {
782 op_b += GetImmediate19(instr); 855 op_b = GetImmediate19(instr);
783 } else { 856 } else {
784 if (instr.is_b_gpr) { 857 if (instr.is_b_gpr) {
785 op_b += regs.GetRegisterAsFloat(instr.gpr20); 858 op_b = regs.GetRegisterAsFloat(instr.gpr20);
786 } else { 859 } else {
787 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 860 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
788 GLSLRegister::Type::Float); 861 GLSLRegister::Type::Float);
789 } 862 }
790 } 863 }
791 864
@@ -793,6 +866,10 @@ private:
793 op_b = "abs(" + op_b + ')'; 866 op_b = "abs(" + op_b + ')';
794 } 867 }
795 868
869 if (instr.alu.negate_b) {
870 op_b = "-(" + op_b + ')';
871 }
872
796 switch (opcode->GetId()) { 873 switch (opcode->GetId()) {
797 case OpCode::Id::MOV_C: 874 case OpCode::Id::MOV_C:
798 case OpCode::Id::MOV_R: { 875 case OpCode::Id::MOV_R: {
@@ -800,68 +877,53 @@ private:
800 break; 877 break;
801 } 878 }
802 879
803 case OpCode::Id::MOV32_IMM: {
804 // mov32i doesn't have abs or neg bits.
805 regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
806 break;
807 }
808 case OpCode::Id::FMUL_C: 880 case OpCode::Id::FMUL_C:
809 case OpCode::Id::FMUL_R: 881 case OpCode::Id::FMUL_R:
810 case OpCode::Id::FMUL_IMM: { 882 case OpCode::Id::FMUL_IMM: {
811 ASSERT_MSG(!instr.saturate_a, "Unimplemented"); 883 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
812 884 instr.alu.saturate_d);
813 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, instr.alu.abs_d);
814 break;
815 }
816 case OpCode::Id::FMUL32_IMM: {
817 // fmul32i doesn't have abs or neg bits.
818 regs.SetRegisterToFloat(
819 instr.gpr0, 0,
820 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
821 break; 885 break;
822 } 886 }
823 case OpCode::Id::FADD_C: 887 case OpCode::Id::FADD_C:
824 case OpCode::Id::FADD_R: 888 case OpCode::Id::FADD_R:
825 case OpCode::Id::FADD_IMM: { 889 case OpCode::Id::FADD_IMM: {
826 ASSERT_MSG(!instr.saturate_a, "Unimplemented"); 890 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
827 891 instr.alu.saturate_d);
828 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, instr.alu.abs_d);
829 break; 892 break;
830 } 893 }
831 case OpCode::Id::MUFU: { 894 case OpCode::Id::MUFU: {
832 ASSERT_MSG(!instr.saturate_a, "Unimplemented");
833
834 switch (instr.sub_op) { 895 switch (instr.sub_op) {
835 case SubOp::Cos: 896 case SubOp::Cos:
836 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, 897 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
837 instr.alu.abs_d); 898 instr.alu.saturate_d);
838 break; 899 break;
839 case SubOp::Sin: 900 case SubOp::Sin:
840 regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1, 901 regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
841 instr.alu.abs_d); 902 instr.alu.saturate_d);
842 break; 903 break;
843 case SubOp::Ex2: 904 case SubOp::Ex2:
844 regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1, 905 regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
845 instr.alu.abs_d); 906 instr.alu.saturate_d);
846 break; 907 break;
847 case SubOp::Lg2: 908 case SubOp::Lg2:
848 regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1, 909 regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
849 instr.alu.abs_d); 910 instr.alu.saturate_d);
850 break; 911 break;
851 case SubOp::Rcp: 912 case SubOp::Rcp:
852 regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, instr.alu.abs_d); 913 regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
914 instr.alu.saturate_d);
853 break; 915 break;
854 case SubOp::Rsq: 916 case SubOp::Rsq:
855 regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1, 917 regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
856 instr.alu.abs_d); 918 instr.alu.saturate_d);
857 break; 919 break;
858 case SubOp::Min: 920 case SubOp::Sqrt:
859 regs.SetRegisterToFloat(instr.gpr0, 0, "min(" + op_a + "," + op_b + ')', 1, 1, 921 regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1,
860 instr.alu.abs_d); 922 instr.alu.saturate_d);
861 break; 923 break;
862 default: 924 default:
863 NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", 925 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}",
864 static_cast<unsigned>(instr.sub_op.Value())); 926 static_cast<unsigned>(instr.sub_op.Value()));
865 UNREACHABLE(); 927 UNREACHABLE();
866 } 928 }
867 break; 929 break;
@@ -884,16 +946,31 @@ private:
884 // Currently RRO is only implemented as a register move. 946 // Currently RRO is only implemented as a register move.
885 // Usage of `abs_b` and `negate_b` here should also be correct. 947 // Usage of `abs_b` and `negate_b` here should also be correct.
886 regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1); 948 regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1);
887 NGLOG_WARNING(HW_GPU, "RRO instruction is incomplete"); 949 LOG_WARNING(HW_GPU, "RRO instruction is incomplete");
888 break; 950 break;
889 } 951 }
890 default: { 952 default: {
891 NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); 953 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName());
892 UNREACHABLE(); 954 UNREACHABLE();
893 } 955 }
894 } 956 }
895 break; 957 break;
896 } 958 }
959 case OpCode::Type::ArithmeticImmediate: {
960 switch (opcode->GetId()) {
961 case OpCode::Id::MOV32_IMM: {
962 regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
963 break;
964 }
965 case OpCode::Id::FMUL32_IMM: {
966 regs.SetRegisterToFloat(
967 instr.gpr0, 0,
968 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
969 break;
970 }
971 }
972 break;
973 }
897 case OpCode::Type::Bfe: { 974 case OpCode::Type::Bfe: {
898 ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); 975 ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented");
899 976
@@ -912,56 +989,13 @@ private:
912 break; 989 break;
913 } 990 }
914 default: { 991 default: {
915 NGLOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName()); 992 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName());
916 UNREACHABLE(); 993 UNREACHABLE();
917 } 994 }
918 } 995 }
919 996
920 break; 997 break;
921 } 998 }
922 case OpCode::Type::Logic: {
923 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
924
925 if (instr.alu.lop.invert_a)
926 op_a = "~(" + op_a + ')';
927
928 switch (opcode->GetId()) {
929 case OpCode::Id::LOP32I: {
930 u32 imm = static_cast<u32>(instr.alu.imm20_32.Value());
931
932 if (instr.alu.lop.invert_b)
933 imm = ~imm;
934
935 switch (instr.alu.lop.operation) {
936 case Tegra::Shader::LogicOperation::And: {
937 regs.SetRegisterToInteger(instr.gpr0, true, 0,
938 '(' + op_a + " & " + std::to_string(imm) + ')', 1, 1);
939 break;
940 }
941 case Tegra::Shader::LogicOperation::Or: {
942 regs.SetRegisterToInteger(instr.gpr0, true, 0,
943 '(' + op_a + " | " + std::to_string(imm) + ')', 1, 1);
944 break;
945 }
946 case Tegra::Shader::LogicOperation::Xor: {
947 regs.SetRegisterToInteger(instr.gpr0, true, 0,
948 '(' + op_a + " ^ " + std::to_string(imm) + ')', 1, 1);
949 break;
950 }
951 default:
952 NGLOG_CRITICAL(HW_GPU, "Unimplemented lop32i operation: {}",
953 static_cast<u32>(instr.alu.lop.operation.Value()));
954 UNREACHABLE();
955 }
956 break;
957 }
958 default: {
959 NGLOG_CRITICAL(HW_GPU, "Unhandled logic instruction: {}", opcode->GetName());
960 UNREACHABLE();
961 }
962 }
963 break;
964 }
965 999
966 case OpCode::Type::Shift: { 1000 case OpCode::Type::Shift: {
967 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1001 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
@@ -998,21 +1032,46 @@ private:
998 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); 1032 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
999 break; 1033 break;
1000 default: { 1034 default: {
1001 NGLOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); 1035 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName());
1002 UNREACHABLE(); 1036 UNREACHABLE();
1003 } 1037 }
1004 } 1038 }
1005 break; 1039 break;
1006 } 1040 }
1007 1041
1008 case OpCode::Type::ArithmeticInteger: { 1042 case OpCode::Type::ArithmeticIntegerImmediate: {
1009 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1043 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1044 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
1010 1045
1011 if (instr.alu_integer.negate_a) 1046 switch (opcode->GetId()) {
1012 op_a = '-' + op_a; 1047 case OpCode::Id::IADD32I:
1048 if (instr.iadd32i.negate_a)
1049 op_a = "-(" + op_a + ')';
1013 1050
1014 std::string op_b = instr.alu_integer.negate_b ? "-" : ""; 1051 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1052 instr.iadd32i.saturate != 0);
1053 break;
1054 case OpCode::Id::LOP32I: {
1055 if (instr.alu.lop32i.invert_a)
1056 op_a = "~(" + op_a + ')';
1015 1057
1058 if (instr.alu.lop32i.invert_b)
1059 op_b = "~(" + op_b + ')';
1060
1061 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b);
1062 break;
1063 }
1064 default: {
1065 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}",
1066 opcode->GetName());
1067 UNREACHABLE();
1068 }
1069 }
1070 break;
1071 }
1072 case OpCode::Type::ArithmeticInteger: {
1073 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1074 std::string op_b;
1016 if (instr.is_b_imm) { 1075 if (instr.is_b_imm) {
1017 op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')'; 1076 op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')';
1018 } else { 1077 } else {
@@ -1028,22 +1087,63 @@ private:
1028 case OpCode::Id::IADD_C: 1087 case OpCode::Id::IADD_C:
1029 case OpCode::Id::IADD_R: 1088 case OpCode::Id::IADD_R:
1030 case OpCode::Id::IADD_IMM: { 1089 case OpCode::Id::IADD_IMM: {
1031 ASSERT_MSG(!instr.saturate_a, "Unimplemented"); 1090 if (instr.alu_integer.negate_a)
1032 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1); 1091 op_a = "-(" + op_a + ')';
1092
1093 if (instr.alu_integer.negate_b)
1094 op_b = "-(" + op_b + ')';
1095
1096 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1097 instr.alu.saturate_d);
1033 break; 1098 break;
1034 } 1099 }
1035 case OpCode::Id::ISCADD_C: 1100 case OpCode::Id::ISCADD_C:
1036 case OpCode::Id::ISCADD_R: 1101 case OpCode::Id::ISCADD_R:
1037 case OpCode::Id::ISCADD_IMM: { 1102 case OpCode::Id::ISCADD_IMM: {
1103 if (instr.alu_integer.negate_a)
1104 op_a = "-(" + op_a + ')';
1105
1106 if (instr.alu_integer.negate_b)
1107 op_b = "-(" + op_b + ')';
1108
1038 std::string shift = std::to_string(instr.alu_integer.shift_amount.Value()); 1109 std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
1039 1110
1040 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1111 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1041 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1112 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
1042 break; 1113 break;
1043 } 1114 }
1115 case OpCode::Id::LOP_C:
1116 case OpCode::Id::LOP_R:
1117 case OpCode::Id::LOP_IMM: {
1118 ASSERT_MSG(!instr.alu.lop.unk44, "Unimplemented");
1119 ASSERT_MSG(instr.alu.lop.pred48 == Pred::UnusedIndex, "Unimplemented");
1120
1121 if (instr.alu.lop.invert_a)
1122 op_a = "~(" + op_a + ')';
1123
1124 if (instr.alu.lop.invert_b)
1125 op_b = "~(" + op_b + ')';
1126
1127 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b);
1128 break;
1129 }
1130 case OpCode::Id::IMNMX_C:
1131 case OpCode::Id::IMNMX_R:
1132 case OpCode::Id::IMNMX_IMM: {
1133 ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None,
1134 "Unimplemented");
1135 std::string condition =
1136 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
1137 std::string parameters = op_a + ',' + op_b;
1138 regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
1139 '(' + condition + ") ? min(" + parameters + ") : max(" +
1140 parameters + ')',
1141 1, 1);
1142 break;
1143 }
1044 default: { 1144 default: {
1045 NGLOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", 1145 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
1046 opcode->GetName()); 1146 opcode->GetName());
1047 UNREACHABLE(); 1147 UNREACHABLE();
1048 } 1148 }
1049 } 1149 }
@@ -1051,8 +1151,6 @@ private:
1051 break; 1151 break;
1052 } 1152 }
1053 case OpCode::Type::Ffma: { 1153 case OpCode::Type::Ffma: {
1054 ASSERT_MSG(!instr.saturate_a, "Unimplemented");
1055
1056 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1154 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1057 std::string op_b = instr.ffma.negate_b ? "-" : ""; 1155 std::string op_b = instr.ffma.negate_b ? "-" : "";
1058 std::string op_c = instr.ffma.negate_c ? "-" : ""; 1156 std::string op_c = instr.ffma.negate_c ? "-" : "";
@@ -1081,38 +1179,38 @@ private:
1081 break; 1179 break;
1082 } 1180 }
1083 default: { 1181 default: {
1084 NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); 1182 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName());
1085 UNREACHABLE(); 1183 UNREACHABLE();
1086 } 1184 }
1087 } 1185 }
1088 1186
1089 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1); 1187 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1,
1188 instr.alu.saturate_d);
1090 break; 1189 break;
1091 } 1190 }
1092 case OpCode::Type::Conversion: { 1191 case OpCode::Type::Conversion: {
1093 ASSERT_MSG(instr.conversion.size == Register::Size::Word, "Unimplemented");
1094 ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented"); 1192 ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented");
1095 ASSERT_MSG(!instr.saturate_a, "Unimplemented");
1096 1193
1097 switch (opcode->GetId()) { 1194 switch (opcode->GetId()) {
1098 case OpCode::Id::I2I_R: { 1195 case OpCode::Id::I2I_R: {
1099 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 1196 ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
1100 1197
1101 std::string op_a = 1198 std::string op_a = regs.GetRegisterAsInteger(
1102 regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); 1199 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
1103 1200
1104 if (instr.conversion.abs_a) { 1201 if (instr.conversion.abs_a) {
1105 op_a = "abs(" + op_a + ')'; 1202 op_a = "abs(" + op_a + ')';
1106 } 1203 }
1107 1204
1108 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 1205 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1109 1); 1206 1, instr.alu.saturate_d, 0, instr.conversion.dest_size);
1110 break; 1207 break;
1111 } 1208 }
1112 case OpCode::Id::I2F_R: { 1209 case OpCode::Id::I2F_R: {
1210 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented");
1113 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 1211 ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
1114 std::string op_a = 1212 std::string op_a = regs.GetRegisterAsInteger(
1115 regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); 1213 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
1116 1214
1117 if (instr.conversion.abs_a) { 1215 if (instr.conversion.abs_a) {
1118 op_a = "abs(" + op_a + ')'; 1216 op_a = "abs(" + op_a + ')';
@@ -1122,13 +1220,16 @@ private:
1122 break; 1220 break;
1123 } 1221 }
1124 case OpCode::Id::F2F_R: { 1222 case OpCode::Id::F2F_R: {
1125 ASSERT_MSG(!instr.saturate_a, "Unimplemented"); 1223 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented");
1126 1224 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented");
1127 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); 1225 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
1128 1226
1129 switch (instr.conversion.f2f.rounding) { 1227 switch (instr.conversion.f2f.rounding) {
1130 case Tegra::Shader::F2fRoundingOp::None: 1228 case Tegra::Shader::F2fRoundingOp::None:
1131 break; 1229 break;
1230 case Tegra::Shader::F2fRoundingOp::Round:
1231 op_a = "roundEven(" + op_a + ')';
1232 break;
1132 case Tegra::Shader::F2fRoundingOp::Floor: 1233 case Tegra::Shader::F2fRoundingOp::Floor:
1133 op_a = "floor(" + op_a + ')'; 1234 op_a = "floor(" + op_a + ')';
1134 break; 1235 break;
@@ -1139,8 +1240,8 @@ private:
1139 op_a = "trunc(" + op_a + ')'; 1240 op_a = "trunc(" + op_a + ')';
1140 break; 1241 break;
1141 default: 1242 default:
1142 NGLOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", 1243 LOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}",
1143 static_cast<u32>(instr.conversion.f2f.rounding.Value())); 1244 static_cast<u32>(instr.conversion.f2f.rounding.Value()));
1144 UNREACHABLE(); 1245 UNREACHABLE();
1145 break; 1246 break;
1146 } 1247 }
@@ -1149,10 +1250,11 @@ private:
1149 op_a = "abs(" + op_a + ')'; 1250 op_a = "abs(" + op_a + ')';
1150 } 1251 }
1151 1252
1152 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 1253 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
1153 break; 1254 break;
1154 } 1255 }
1155 case OpCode::Id::F2I_R: { 1256 case OpCode::Id::F2I_R: {
1257 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented");
1156 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); 1258 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
1157 1259
1158 if (instr.conversion.abs_a) { 1260 if (instr.conversion.abs_a) {
@@ -1172,8 +1274,8 @@ private:
1172 op_a = "trunc(" + op_a + ')'; 1274 op_a = "trunc(" + op_a + ')';
1173 break; 1275 break;
1174 default: 1276 default:
1175 NGLOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", 1277 LOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}",
1176 static_cast<u32>(instr.conversion.f2i.rounding.Value())); 1278 static_cast<u32>(instr.conversion.f2i.rounding.Value()));
1177 UNREACHABLE(); 1279 UNREACHABLE();
1178 break; 1280 break;
1179 } 1281 }
@@ -1185,11 +1287,11 @@ private:
1185 } 1287 }
1186 1288
1187 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 1289 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1188 1); 1290 1, false, 0, instr.conversion.dest_size);
1189 break; 1291 break;
1190 } 1292 }
1191 default: { 1293 default: {
1192 NGLOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); 1294 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName());
1193 UNREACHABLE(); 1295 UNREACHABLE();
1194 } 1296 }
1195 } 1297 }
@@ -1224,8 +1326,8 @@ private:
1224 break; 1326 break;
1225 1327
1226 default: 1328 default:
1227 NGLOG_CRITICAL(HW_GPU, "Unhandled type: {}", 1329 LOG_CRITICAL(HW_GPU, "Unhandled type: {}",
1228 static_cast<unsigned>(instr.ld_c.type.Value())); 1330 static_cast<unsigned>(instr.ld_c.type.Value()));
1229 UNREACHABLE(); 1331 UNREACHABLE();
1230 } 1332 }
1231 break; 1333 break;
@@ -1298,7 +1400,7 @@ private:
1298 break; 1400 break;
1299 } 1401 }
1300 default: { 1402 default: {
1301 NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); 1403 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName());
1302 UNREACHABLE(); 1404 UNREACHABLE();
1303 } 1405 }
1304 } 1406 }
@@ -1340,10 +1442,9 @@ private:
1340 std::string second_pred = 1442 std::string second_pred =
1341 GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0); 1443 GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0);
1342 1444
1343 std::string comparator = GetPredicateComparison(instr.fsetp.cond);
1344 std::string combiner = GetPredicateCombiner(instr.fsetp.op); 1445 std::string combiner = GetPredicateCombiner(instr.fsetp.op);
1345 1446
1346 std::string predicate = '(' + op_a + ") " + comparator + " (" + op_b + ')'; 1447 std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b);
1347 // Set the primary predicate to the result of Predicate OP SecondPredicate 1448 // Set the primary predicate to the result of Predicate OP SecondPredicate
1348 SetPredicate(instr.fsetp.pred3, 1449 SetPredicate(instr.fsetp.pred3,
1349 '(' + predicate + ") " + combiner + " (" + second_pred + ')'); 1450 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
@@ -1378,10 +1479,9 @@ private:
1378 std::string second_pred = 1479 std::string second_pred =
1379 GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0); 1480 GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0);
1380 1481
1381 std::string comparator = GetPredicateComparison(instr.isetp.cond);
1382 std::string combiner = GetPredicateCombiner(instr.isetp.op); 1482 std::string combiner = GetPredicateCombiner(instr.isetp.op);
1383 1483
1384 std::string predicate = '(' + op_a + ") " + comparator + " (" + op_b + ')'; 1484 std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b);
1385 // Set the primary predicate to the result of Predicate OP SecondPredicate 1485 // Set the primary predicate to the result of Predicate OP SecondPredicate
1386 SetPredicate(instr.isetp.pred3, 1486 SetPredicate(instr.isetp.pred3,
1387 '(' + predicate + ") " + combiner + " (" + second_pred + ')'); 1487 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
@@ -1394,6 +1494,36 @@ private:
1394 } 1494 }
1395 break; 1495 break;
1396 } 1496 }
1497 case OpCode::Type::PredicateSetPredicate: {
1498 std::string op_a =
1499 GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0);
1500 std::string op_b =
1501 GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0);
1502
1503 using Tegra::Shader::Pred;
1504 // We can't use the constant predicate as destination.
1505 ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
1506
1507 std::string second_pred =
1508 GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0);
1509
1510 std::string combiner = GetPredicateCombiner(instr.psetp.op);
1511
1512 std::string predicate =
1513 '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')';
1514
1515 // Set the primary predicate to the result of Predicate OP SecondPredicate
1516 SetPredicate(instr.psetp.pred3,
1517 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
1518
1519 if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
1520 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
1521 // if enabled
1522 SetPredicate(instr.psetp.pred0,
1523 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
1524 }
1525 break;
1526 }
1397 case OpCode::Type::FloatSet: { 1527 case OpCode::Type::FloatSet: {
1398 std::string op_a = instr.fset.neg_a ? "-" : ""; 1528 std::string op_a = instr.fset.neg_a ? "-" : "";
1399 op_a += regs.GetRegisterAsFloat(instr.gpr8); 1529 op_a += regs.GetRegisterAsFloat(instr.gpr8);
@@ -1428,11 +1558,10 @@ private:
1428 std::string second_pred = 1558 std::string second_pred =
1429 GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0); 1559 GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0);
1430 1560
1431 std::string comparator = GetPredicateComparison(instr.fset.cond);
1432 std::string combiner = GetPredicateCombiner(instr.fset.op); 1561 std::string combiner = GetPredicateCombiner(instr.fset.op);
1433 1562
1434 std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " + 1563 std::string predicate = "((" + GetPredicateComparison(instr.fset.cond, op_a, op_b) +
1435 combiner + " (" + second_pred + "))"; 1564 ") " + combiner + " (" + second_pred + "))";
1436 1565
1437 if (instr.fset.bf) { 1566 if (instr.fset.bf) {
1438 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); 1567 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
@@ -1463,11 +1592,10 @@ private:
1463 std::string second_pred = 1592 std::string second_pred =
1464 GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0); 1593 GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0);
1465 1594
1466 std::string comparator = GetPredicateComparison(instr.iset.cond);
1467 std::string combiner = GetPredicateCombiner(instr.iset.op); 1595 std::string combiner = GetPredicateCombiner(instr.iset.op);
1468 1596
1469 std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " + 1597 std::string predicate = "((" + GetPredicateComparison(instr.iset.cond, op_a, op_b) +
1470 combiner + " (" + second_pred + "))"; 1598 ") " + combiner + " (" + second_pred + "))";
1471 1599
1472 if (instr.iset.bf) { 1600 if (instr.iset.bf) {
1473 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); 1601 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
@@ -1518,8 +1646,15 @@ private:
1518 // can ignore this when generating GLSL code. 1646 // can ignore this when generating GLSL code.
1519 break; 1647 break;
1520 } 1648 }
1649 case OpCode::Id::DEPBAR:
1650 case OpCode::Id::SYNC: {
1651 // TODO(Subv): Find out if we actually have to care about these instructions or if
1652 // the GLSL compiler takes care of that for us.
1653 LOG_WARNING(HW_GPU, "DEPBAR/SYNC instruction is stubbed");
1654 break;
1655 }
1521 default: { 1656 default: {
1522 NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 1657 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
1523 UNREACHABLE(); 1658 UNREACHABLE();
1524 } 1659 }
1525 } 1660 }
@@ -1646,7 +1781,10 @@ private:
1646}; // namespace Decompiler 1781}; // namespace Decompiler
1647 1782
1648std::string GetCommonDeclarations() { 1783std::string GetCommonDeclarations() {
1649 return "bool exec_shader();"; 1784 std::string declarations = "bool exec_shader();\n";
1785 declarations += "#define MAX_CONSTBUFFER_ELEMENTS " +
1786 std::to_string(RasterizerOpenGL::MaxConstbufferSize / (sizeof(GLvec4)));
1787 return declarations;
1650} 1788}
1651 1789
1652boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, 1790boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
@@ -1656,7 +1794,7 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
1656 GLSLGenerator generator(subroutines, program_code, main_offset, stage); 1794 GLSLGenerator generator(subroutines, program_code, main_offset, stage);
1657 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; 1795 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
1658 } catch (const DecompileFail& exception) { 1796 } catch (const DecompileFail& exception) {
1659 NGLOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); 1797 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());
1660 } 1798 }
1661 return boost::none; 1799 return boost::none;
1662} 1800}
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index b88d592b7..c1e6fac9f 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -39,6 +39,10 @@ void main() {
39 // Viewport can be flipped, which is unsupported by glViewport 39 // Viewport can be flipped, which is unsupported by glViewport
40 position.xy *= viewport_flip.xy; 40 position.xy *= viewport_flip.xy;
41 gl_Position = position; 41 gl_Position = position;
42
43 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
44 // For now, this is here to bring order in lieu of proper emulation
45 position.w = 1.0;
42} 46}
43)"; 47)";
44 out += program.first; 48 out += program.first;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 7c00beb33..d7167b298 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -38,8 +38,8 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
38 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 38 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
39 39
40 // TODO(bunnei): Support more than one viewport 40 // TODO(bunnei): Support more than one viewport
41 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0 : 1.0; 41 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
42 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0 : 1.0; 42 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
43} 43}
44 44
45} // namespace GLShader 45} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 8568fface..3c087d638 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -27,7 +27,7 @@ GLuint LoadShader(const char* source, GLenum type) {
27 } 27 }
28 GLuint shader_id = glCreateShader(type); 28 GLuint shader_id = glCreateShader(type);
29 glShaderSource(shader_id, 1, &source, nullptr); 29 glShaderSource(shader_id, 1, &source, nullptr);
30 NGLOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); 30 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
31 glCompileShader(shader_id); 31 glCompileShader(shader_id);
32 32
33 GLint result = GL_FALSE; 33 GLint result = GL_FALSE;
@@ -39,9 +39,9 @@ GLuint LoadShader(const char* source, GLenum type) {
39 std::string shader_error(info_log_length, ' '); 39 std::string shader_error(info_log_length, ' ');
40 glGetShaderInfoLog(shader_id, info_log_length, nullptr, &shader_error[0]); 40 glGetShaderInfoLog(shader_id, info_log_length, nullptr, &shader_error[0]);
41 if (result == GL_TRUE) { 41 if (result == GL_TRUE) {
42 NGLOG_DEBUG(Render_OpenGL, "{}", shader_error); 42 LOG_DEBUG(Render_OpenGL, "{}", shader_error);
43 } else { 43 } else {
44 NGLOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, shader_error); 44 LOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, shader_error);
45 } 45 }
46 } 46 }
47 return shader_id; 47 return shader_id;
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 2036a06a9..0e4d782e2 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -29,7 +29,7 @@ void LogShaderSource(T... shaders) {
29 29
30 std::string source(source_length, ' '); 30 std::string source(source_length, ' ');
31 glGetShaderSource(shader, source_length, nullptr, &source[0]); 31 glGetShaderSource(shader, source_length, nullptr, &source[0]);
32 NGLOG_INFO(Render_OpenGL, "Shader source {}", source); 32 LOG_INFO(Render_OpenGL, "Shader source {}", source);
33 } 33 }
34} 34}
35 35
@@ -49,7 +49,7 @@ GLuint LoadShader(const char* source, GLenum type);
49template <typename... T> 49template <typename... T>
50GLuint LoadProgram(bool separable_program, T... shaders) { 50GLuint LoadProgram(bool separable_program, T... shaders) {
51 // Link the program 51 // Link the program
52 NGLOG_DEBUG(Render_OpenGL, "Linking program..."); 52 LOG_DEBUG(Render_OpenGL, "Linking program...");
53 53
54 GLuint program_id = glCreateProgram(); 54 GLuint program_id = glCreateProgram();
55 55
@@ -71,9 +71,9 @@ GLuint LoadProgram(bool separable_program, T... shaders) {
71 std::string program_error(info_log_length, ' '); 71 std::string program_error(info_log_length, ' ');
72 glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]); 72 glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]);
73 if (result == GL_TRUE) { 73 if (result == GL_TRUE) {
74 NGLOG_DEBUG(Render_OpenGL, "{}", program_error); 74 LOG_DEBUG(Render_OpenGL, "{}", program_error);
75 } else { 75 } else {
76 NGLOG_ERROR(Render_OpenGL, "Error linking shader:\n{}", program_error); 76 LOG_ERROR(Render_OpenGL, "Error linking shader:\n{}", program_error);
77 } 77 }
78 } 78 }
79 79
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 44f0c8a01..2e8a422a8 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -48,24 +48,9 @@ OpenGLState::OpenGLState() {
48 logic_op = GL_COPY; 48 logic_op = GL_COPY;
49 49
50 for (auto& texture_unit : texture_units) { 50 for (auto& texture_unit : texture_units) {
51 texture_unit.texture_2d = 0; 51 texture_unit.Reset();
52 texture_unit.sampler = 0;
53 texture_unit.swizzle.r = GL_RED;
54 texture_unit.swizzle.g = GL_GREEN;
55 texture_unit.swizzle.b = GL_BLUE;
56 texture_unit.swizzle.a = GL_ALPHA;
57 } 52 }
58 53
59 lighting_lut.texture_buffer = 0;
60
61 fog_lut.texture_buffer = 0;
62
63 proctex_lut.texture_buffer = 0;
64 proctex_diff_lut.texture_buffer = 0;
65 proctex_color_map.texture_buffer = 0;
66 proctex_alpha_map.texture_buffer = 0;
67 proctex_noise_lut.texture_buffer = 0;
68
69 draw.read_framebuffer = 0; 54 draw.read_framebuffer = 0;
70 draw.draw_framebuffer = 0; 55 draw.draw_framebuffer = 0;
71 draw.vertex_array = 0; 56 draw.vertex_array = 0;
@@ -196,13 +181,13 @@ void OpenGLState::Apply() const {
196 } 181 }
197 182
198 // Textures 183 // Textures
199 for (size_t i = 0; i < std::size(texture_units); ++i) { 184 for (int i = 0; i < std::size(texture_units); ++i) {
200 if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { 185 if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
201 glActiveTexture(TextureUnits::MaxwellTexture(i).Enum()); 186 glActiveTexture(TextureUnits::MaxwellTexture(i).Enum());
202 glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); 187 glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
203 } 188 }
204 if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { 189 if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
205 glBindSampler(i, texture_units[i].sampler); 190 glBindSampler(static_cast<GLuint>(i), texture_units[i].sampler);
206 } 191 }
207 // Update the texture swizzle 192 // Update the texture swizzle
208 if (texture_units[i].swizzle.r != cur_state.texture_units[i].swizzle.r || 193 if (texture_units[i].swizzle.r != cur_state.texture_units[i].swizzle.r ||
@@ -223,54 +208,12 @@ void OpenGLState::Apply() const {
223 if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint || 208 if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint ||
224 current.ssbo != new_state.ssbo) { 209 current.ssbo != new_state.ssbo) {
225 if (new_state.enabled) { 210 if (new_state.enabled) {
226 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, new_state.bindpoint, new_state.ssbo); 211 glBindBufferBase(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo);
227 } 212 }
228 } 213 }
229 } 214 }
230 } 215 }
231 216
232 // Lighting LUTs
233 if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
234 glActiveTexture(TextureUnits::LightingLUT.Enum());
235 glBindTexture(GL_TEXTURE_BUFFER, lighting_lut.texture_buffer);
236 }
237
238 // Fog LUT
239 if (fog_lut.texture_buffer != cur_state.fog_lut.texture_buffer) {
240 glActiveTexture(TextureUnits::FogLUT.Enum());
241 glBindTexture(GL_TEXTURE_BUFFER, fog_lut.texture_buffer);
242 }
243
244 // ProcTex Noise LUT
245 if (proctex_noise_lut.texture_buffer != cur_state.proctex_noise_lut.texture_buffer) {
246 glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum());
247 glBindTexture(GL_TEXTURE_BUFFER, proctex_noise_lut.texture_buffer);
248 }
249
250 // ProcTex Color Map
251 if (proctex_color_map.texture_buffer != cur_state.proctex_color_map.texture_buffer) {
252 glActiveTexture(TextureUnits::ProcTexColorMap.Enum());
253 glBindTexture(GL_TEXTURE_BUFFER, proctex_color_map.texture_buffer);
254 }
255
256 // ProcTex Alpha Map
257 if (proctex_alpha_map.texture_buffer != cur_state.proctex_alpha_map.texture_buffer) {
258 glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum());
259 glBindTexture(GL_TEXTURE_BUFFER, proctex_alpha_map.texture_buffer);
260 }
261
262 // ProcTex LUT
263 if (proctex_lut.texture_buffer != cur_state.proctex_lut.texture_buffer) {
264 glActiveTexture(TextureUnits::ProcTexLUT.Enum());
265 glBindTexture(GL_TEXTURE_BUFFER, proctex_lut.texture_buffer);
266 }
267
268 // ProcTex Diff LUT
269 if (proctex_diff_lut.texture_buffer != cur_state.proctex_diff_lut.texture_buffer) {
270 glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());
271 glBindTexture(GL_TEXTURE_BUFFER, proctex_diff_lut.texture_buffer);
272 }
273
274 // Framebuffer 217 // Framebuffer
275 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 218 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
276 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 219 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -338,26 +281,12 @@ void OpenGLState::Apply() const {
338 cur_state = *this; 281 cur_state = *this;
339} 282}
340 283
341OpenGLState& OpenGLState::ResetTexture(GLuint handle) { 284OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
342 for (auto& unit : texture_units) { 285 for (auto& unit : texture_units) {
343 if (unit.texture_2d == handle) { 286 if (unit.texture_2d == handle) {
344 unit.texture_2d = 0; 287 unit.Unbind();
345 } 288 }
346 } 289 }
347 if (lighting_lut.texture_buffer == handle)
348 lighting_lut.texture_buffer = 0;
349 if (fog_lut.texture_buffer == handle)
350 fog_lut.texture_buffer = 0;
351 if (proctex_noise_lut.texture_buffer == handle)
352 proctex_noise_lut.texture_buffer = 0;
353 if (proctex_color_map.texture_buffer == handle)
354 proctex_color_map.texture_buffer = 0;
355 if (proctex_alpha_map.texture_buffer == handle)
356 proctex_alpha_map.texture_buffer = 0;
357 if (proctex_lut.texture_buffer == handle)
358 proctex_lut.texture_buffer = 0;
359 if (proctex_diff_lut.texture_buffer == handle)
360 proctex_diff_lut.texture_buffer = 0;
361 return *this; 290 return *this;
362} 291}
363 292
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 839e50e93..3398d7c04 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -91,35 +91,20 @@ public:
91 GLint b; // GL_TEXTURE_SWIZZLE_B 91 GLint b; // GL_TEXTURE_SWIZZLE_B
92 GLint a; // GL_TEXTURE_SWIZZLE_A 92 GLint a; // GL_TEXTURE_SWIZZLE_A
93 } swizzle; 93 } swizzle;
94 } texture_units[32];
95
96 struct {
97 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
98 } lighting_lut;
99
100 struct {
101 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
102 } fog_lut;
103
104 struct {
105 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
106 } proctex_noise_lut;
107 94
108 struct { 95 void Unbind() {
109 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER 96 texture_2d = 0;
110 } proctex_color_map; 97 swizzle.r = GL_RED;
111 98 swizzle.g = GL_GREEN;
112 struct { 99 swizzle.b = GL_BLUE;
113 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER 100 swizzle.a = GL_ALPHA;
114 } proctex_alpha_map; 101 }
115 102
116 struct { 103 void Reset() {
117 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER 104 Unbind();
118 } proctex_lut; 105 sampler = 0;
119 106 }
120 struct { 107 } texture_units[32];
121 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
122 } proctex_diff_lut;
123 108
124 struct { 109 struct {
125 GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING 110 GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
@@ -165,7 +150,7 @@ public:
165 void Apply() const; 150 void Apply() const;
166 151
167 /// Resets any references to the given resource 152 /// Resets any references to the given resource
168 OpenGLState& ResetTexture(GLuint handle); 153 OpenGLState& UnbindTexture(GLuint handle);
169 OpenGLState& ResetSampler(GLuint handle); 154 OpenGLState& ResetSampler(GLuint handle);
170 OpenGLState& ResetProgram(GLuint handle); 155 OpenGLState& ResetProgram(GLuint handle);
171 OpenGLState& ResetPipeline(GLuint handle); 156 OpenGLState& ResetPipeline(GLuint handle);
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 2155fb019..e19c3b280 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -29,9 +29,13 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
29 switch (attrib.size) { 29 switch (attrib.size) {
30 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 30 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
31 return GL_UNSIGNED_BYTE; 31 return GL_UNSIGNED_BYTE;
32 case Maxwell::VertexAttribute::Size::Size_16_16:
33 return GL_UNSIGNED_SHORT;
34 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
35 return GL_UNSIGNED_INT_2_10_10_10_REV;
32 } 36 }
33 37
34 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); 38 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
35 UNREACHABLE(); 39 UNREACHABLE();
36 return {}; 40 return {};
37 } 41 }
@@ -41,9 +45,13 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
41 switch (attrib.size) { 45 switch (attrib.size) {
42 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 46 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
43 return GL_BYTE; 47 return GL_BYTE;
48 case Maxwell::VertexAttribute::Size::Size_16_16:
49 return GL_SHORT;
50 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
51 return GL_INT_2_10_10_10_REV;
44 } 52 }
45 53
46 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); 54 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
47 UNREACHABLE(); 55 UNREACHABLE();
48 return {}; 56 return {};
49 } 57 }
@@ -52,7 +60,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
52 return GL_FLOAT; 60 return GL_FLOAT;
53 } 61 }
54 62
55 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); 63 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
56 UNREACHABLE(); 64 UNREACHABLE();
57 return {}; 65 return {};
58} 66}
@@ -66,7 +74,7 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
66 case Maxwell::IndexFormat::UnsignedInt: 74 case Maxwell::IndexFormat::UnsignedInt:
67 return GL_UNSIGNED_INT; 75 return GL_UNSIGNED_INT;
68 } 76 }
69 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented index_format={}", static_cast<u32>(index_format)); 77 LOG_CRITICAL(Render_OpenGL, "Unimplemented index_format={}", static_cast<u32>(index_format));
70 UNREACHABLE(); 78 UNREACHABLE();
71 return {}; 79 return {};
72} 80}
@@ -78,7 +86,7 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
78 case Maxwell::PrimitiveTopology::TriangleStrip: 86 case Maxwell::PrimitiveTopology::TriangleStrip:
79 return GL_TRIANGLE_STRIP; 87 return GL_TRIANGLE_STRIP;
80 } 88 }
81 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); 89 LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology));
82 UNREACHABLE(); 90 UNREACHABLE();
83 return {}; 91 return {};
84} 92}
@@ -90,8 +98,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) {
90 case Tegra::Texture::TextureFilter::Nearest: 98 case Tegra::Texture::TextureFilter::Nearest:
91 return GL_NEAREST; 99 return GL_NEAREST;
92 } 100 }
93 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", 101 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}",
94 static_cast<u32>(filter_mode)); 102 static_cast<u32>(filter_mode));
95 UNREACHABLE(); 103 UNREACHABLE();
96 return {}; 104 return {};
97} 105}
@@ -110,8 +118,7 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
110 // manually mix them. However the shader part of this is not yet implemented. 118 // manually mix them. However the shader part of this is not yet implemented.
111 return GL_CLAMP_TO_BORDER; 119 return GL_CLAMP_TO_BORDER;
112 } 120 }
113 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", 121 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
114 static_cast<u32>(wrap_mode));
115 UNREACHABLE(); 122 UNREACHABLE();
116 return {}; 123 return {};
117} 124}
@@ -129,7 +136,7 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
129 case Maxwell::Blend::Equation::Max: 136 case Maxwell::Blend::Equation::Max:
130 return GL_MAX; 137 return GL_MAX;
131 } 138 }
132 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); 139 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
133 UNREACHABLE(); 140 UNREACHABLE();
134 return {}; 141 return {};
135} 142}
@@ -175,7 +182,7 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
175 case Maxwell::Blend::Factor::OneMinusConstantAlpha: 182 case Maxwell::Blend::Factor::OneMinusConstantAlpha:
176 return GL_ONE_MINUS_CONSTANT_ALPHA; 183 return GL_ONE_MINUS_CONSTANT_ALPHA;
177 } 184 }
178 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); 185 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
179 UNREACHABLE(); 186 UNREACHABLE();
180 return {}; 187 return {};
181} 188}
@@ -196,7 +203,65 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
196 case Tegra::Texture::SwizzleSource::OneFloat: 203 case Tegra::Texture::SwizzleSource::OneFloat:
197 return GL_ONE; 204 return GL_ONE;
198 } 205 }
199 NGLOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); 206 LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
207 UNREACHABLE();
208 return {};
209}
210
211inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
212 switch (comparison) {
213 case Maxwell::ComparisonOp::Never:
214 case Maxwell::ComparisonOp::NeverOld:
215 return GL_NEVER;
216 case Maxwell::ComparisonOp::Less:
217 case Maxwell::ComparisonOp::LessOld:
218 return GL_LESS;
219 case Maxwell::ComparisonOp::Equal:
220 case Maxwell::ComparisonOp::EqualOld:
221 return GL_EQUAL;
222 case Maxwell::ComparisonOp::LessEqual:
223 case Maxwell::ComparisonOp::LessEqualOld:
224 return GL_LEQUAL;
225 case Maxwell::ComparisonOp::Greater:
226 case Maxwell::ComparisonOp::GreaterOld:
227 return GL_GREATER;
228 case Maxwell::ComparisonOp::NotEqual:
229 case Maxwell::ComparisonOp::NotEqualOld:
230 return GL_NOTEQUAL;
231 case Maxwell::ComparisonOp::GreaterEqual:
232 case Maxwell::ComparisonOp::GreaterEqualOld:
233 return GL_GEQUAL;
234 case Maxwell::ComparisonOp::Always:
235 case Maxwell::ComparisonOp::AlwaysOld:
236 return GL_ALWAYS;
237 }
238 LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
239 UNREACHABLE();
240 return {};
241}
242
243inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
244 switch (front_face) {
245 case Maxwell::Cull::FrontFace::ClockWise:
246 return GL_CW;
247 case Maxwell::Cull::FrontFace::CounterClockWise:
248 return GL_CCW;
249 }
250 LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
251 UNREACHABLE();
252 return {};
253}
254
255inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
256 switch (cull_face) {
257 case Maxwell::Cull::CullFace::Front:
258 return GL_FRONT;
259 case Maxwell::Cull::CullFace::Back:
260 return GL_BACK;
261 case Maxwell::Cull::CullFace::FrontAndBack:
262 return GL_FRONT_AND_BACK;
263 }
264 LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
200 UNREACHABLE(); 265 UNREACHABLE();
201 return {}; 266 return {};
202} 267}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f33766bfd..00841e937 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -150,7 +150,6 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
150 screen_info)) { 150 screen_info)) {
151 // Reset the screen info's display texture to its own permanent texture 151 // Reset the screen info's display texture to its own permanent texture
152 screen_info.display_texture = screen_info.texture.resource.handle; 152 screen_info.display_texture = screen_info.texture.resource.handle;
153 screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
154 153
155 Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes, 154 Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes,
156 Memory::FlushMode::Flush); 155 Memory::FlushMode::Flush);
@@ -302,8 +301,8 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
302 right = texcoords.left; 301 right = texcoords.left;
303 } else { 302 } else {
304 // Other transformations are unsupported 303 // Other transformations are unsupported
305 NGLOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}", 304 LOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}",
306 static_cast<u32>(framebuffer_transform_flags)); 305 static_cast<u32>(framebuffer_transform_flags));
307 UNIMPLEMENTED(); 306 UNIMPLEMENTED();
308 } 307 }
309 } 308 }
@@ -405,14 +404,14 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
405 404
406 switch (severity) { 405 switch (severity) {
407 case GL_DEBUG_SEVERITY_HIGH: 406 case GL_DEBUG_SEVERITY_HIGH:
408 NGLOG_ERROR(Render_OpenGL, format, str_source, str_type, id, message); 407 LOG_ERROR(Render_OpenGL, format, str_source, str_type, id, message);
409 break; 408 break;
410 case GL_DEBUG_SEVERITY_MEDIUM: 409 case GL_DEBUG_SEVERITY_MEDIUM:
411 NGLOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message); 410 LOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message);
412 break; 411 break;
413 case GL_DEBUG_SEVERITY_NOTIFICATION: 412 case GL_DEBUG_SEVERITY_NOTIFICATION:
414 case GL_DEBUG_SEVERITY_LOW: 413 case GL_DEBUG_SEVERITY_LOW:
415 NGLOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message); 414 LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
416 break; 415 break;
417 } 416 }
418} 417}
@@ -430,9 +429,9 @@ bool RendererOpenGL::Init() {
430 const char* gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; 429 const char* gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
431 const char* gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))}; 430 const char* gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))};
432 431
433 NGLOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version); 432 LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
434 NGLOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); 433 LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
435 NGLOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); 434 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
436 435
437 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor); 436 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor);
438 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model); 437 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 2cc6d9a00..21f0d298c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -27,7 +27,7 @@ struct TextureInfo {
27/// Structure used for storing information about the display target for the Switch screen 27/// Structure used for storing information about the display target for the Switch screen
28struct ScreenInfo { 28struct ScreenInfo {
29 GLuint display_texture; 29 GLuint display_texture;
30 MathUtil::Rectangle<float> display_texcoords; 30 const MathUtil::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
31 TextureInfo texture; 31 TextureInfo texture;
32}; 32};
33 33
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
new file mode 100644
index 000000000..3c4ad1c9d
--- /dev/null
+++ b/src/video_core/textures/astc.cpp
@@ -0,0 +1,1646 @@
1// Copyright 2016 The University of North Carolina at Chapel Hill
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Please send all BUG REPORTS to <pavel@cs.unc.edu>.
16// <http://gamma.cs.unc.edu/FasTC/>
17
18#include <algorithm>
19#include <cassert>
20#include <cstdint>
21#include <cstring>
22#include <vector>
23
24#include "video_core/textures/astc.h"
25
26class BitStream {
27public:
28 BitStream(unsigned char* ptr, int nBits = 0, int start_offset = 0)
29 : m_BitsWritten(0), m_BitsRead(0), m_NumBits(nBits), m_CurByte(ptr),
30 m_NextBit(start_offset % 8), done(false) {}
31
32 int GetBitsWritten() const {
33 return m_BitsWritten;
34 }
35
36 ~BitStream() {}
37
38 void WriteBitsR(unsigned int val, unsigned int nBits) {
39 for (unsigned int i = 0; i < nBits; i++) {
40 WriteBit((val >> (nBits - i - 1)) & 1);
41 }
42 }
43
44 void WriteBits(unsigned int val, unsigned int nBits) {
45 for (unsigned int i = 0; i < nBits; i++) {
46 WriteBit((val >> i) & 1);
47 }
48 }
49
50 int GetBitsRead() const {
51 return m_BitsRead;
52 }
53
54 int ReadBit() {
55
56 int bit = *m_CurByte >> m_NextBit++;
57 while (m_NextBit >= 8) {
58 m_NextBit -= 8;
59 m_CurByte++;
60 }
61
62 m_BitsRead++;
63 return bit & 1;
64 }
65
66 unsigned int ReadBits(unsigned int nBits) {
67 unsigned int ret = 0;
68 for (unsigned int i = 0; i < nBits; i++) {
69 ret |= (ReadBit() & 1) << i;
70 }
71 return ret;
72 }
73
74private:
75 void WriteBit(int b) {
76
77 if (done)
78 return;
79
80 const unsigned int mask = 1 << m_NextBit++;
81
82 // clear the bit
83 *m_CurByte &= ~mask;
84
85 // Write the bit, if necessary
86 if (b)
87 *m_CurByte |= mask;
88
89 // Next byte?
90 if (m_NextBit >= 8) {
91 m_CurByte += 1;
92 m_NextBit = 0;
93 }
94
95 done = done || ++m_BitsWritten >= m_NumBits;
96 }
97
98 int m_BitsWritten;
99 const int m_NumBits;
100 unsigned char* m_CurByte;
101 int m_NextBit;
102 int m_BitsRead;
103
104 bool done;
105};
106
107template <typename IntType>
108class Bits {
109private:
110 const IntType& m_Bits;
111
112 // Don't copy
113 Bits() {}
114 Bits(const Bits&) {}
115 Bits& operator=(const Bits&) {}
116
117public:
118 explicit Bits(IntType& v) : m_Bits(v) {}
119
120 uint8_t operator[](uint32_t bitPos) {
121 return static_cast<uint8_t>((m_Bits >> bitPos) & 1);
122 }
123
124 IntType operator()(uint32_t start, uint32_t end) {
125 if (start == end) {
126 return (*this)[start];
127 } else if (start > end) {
128 uint32_t t = start;
129 start = end;
130 end = t;
131 }
132
133 uint64_t mask = (1 << (end - start + 1)) - 1;
134 return (m_Bits >> start) & mask;
135 }
136};
137
138enum EIntegerEncoding { eIntegerEncoding_JustBits, eIntegerEncoding_Quint, eIntegerEncoding_Trit };
139
140class IntegerEncodedValue {
141private:
142 const EIntegerEncoding m_Encoding;
143 const uint32_t m_NumBits;
144 uint32_t m_BitValue;
145 union {
146 uint32_t m_QuintValue;
147 uint32_t m_TritValue;
148 };
149
150public:
151 // Jank, but we're not doing any heavy lifting in this class, so it's
152 // probably OK. It allows us to use these in std::vectors...
153 IntegerEncodedValue& operator=(const IntegerEncodedValue& other) {
154 new (this) IntegerEncodedValue(other);
155 return *this;
156 }
157
158 IntegerEncodedValue(EIntegerEncoding encoding, uint32_t numBits)
159 : m_Encoding(encoding), m_NumBits(numBits) {}
160
161 EIntegerEncoding GetEncoding() const {
162 return m_Encoding;
163 }
164 uint32_t BaseBitLength() const {
165 return m_NumBits;
166 }
167
168 uint32_t GetBitValue() const {
169 return m_BitValue;
170 }
171 void SetBitValue(uint32_t val) {
172 m_BitValue = val;
173 }
174
175 uint32_t GetTritValue() const {
176 return m_TritValue;
177 }
178 void SetTritValue(uint32_t val) {
179 m_TritValue = val;
180 }
181
182 uint32_t GetQuintValue() const {
183 return m_QuintValue;
184 }
185 void SetQuintValue(uint32_t val) {
186 m_QuintValue = val;
187 }
188
189 bool MatchesEncoding(const IntegerEncodedValue& other) {
190 return m_Encoding == other.m_Encoding && m_NumBits == other.m_NumBits;
191 }
192
193 // Returns the number of bits required to encode nVals values.
194 uint32_t GetBitLength(uint32_t nVals) {
195 uint32_t totalBits = m_NumBits * nVals;
196 if (m_Encoding == eIntegerEncoding_Trit) {
197 totalBits += (nVals * 8 + 4) / 5;
198 } else if (m_Encoding == eIntegerEncoding_Quint) {
199 totalBits += (nVals * 7 + 2) / 3;
200 }
201 return totalBits;
202 }
203
204 // Count the number of bits set in a number.
205 static inline uint32_t Popcnt(uint32_t n) {
206 uint32_t c;
207 for (c = 0; n; c++) {
208 n &= n - 1;
209 }
210 return c;
211 }
212
213 // Returns a new instance of this struct that corresponds to the
214 // can take no more than maxval values
215 static IntegerEncodedValue CreateEncoding(uint32_t maxVal) {
216 while (maxVal > 0) {
217 uint32_t check = maxVal + 1;
218
219 // Is maxVal a power of two?
220 if (!(check & (check - 1))) {
221 return IntegerEncodedValue(eIntegerEncoding_JustBits, Popcnt(maxVal));
222 }
223
224 // Is maxVal of the type 3*2^n - 1?
225 if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
226 return IntegerEncodedValue(eIntegerEncoding_Trit, Popcnt(check / 3 - 1));
227 }
228
229 // Is maxVal of the type 5*2^n - 1?
230 if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
231 return IntegerEncodedValue(eIntegerEncoding_Quint, Popcnt(check / 5 - 1));
232 }
233
234 // Apparently it can't be represented with a bounded integer sequence...
235 // just iterate.
236 maxVal--;
237 }
238 return IntegerEncodedValue(eIntegerEncoding_JustBits, 0);
239 }
240
241 // Fills result with the values that are encoded in the given
242 // bitstream. We must know beforehand what the maximum possible
243 // value is, and how many values we're decoding.
244 static void DecodeIntegerSequence(std::vector<IntegerEncodedValue>& result, BitStream& bits,
245 uint32_t maxRange, uint32_t nValues) {
246 // Determine encoding parameters
247 IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(maxRange);
248
249 // Start decoding
250 uint32_t nValsDecoded = 0;
251 while (nValsDecoded < nValues) {
252 switch (val.GetEncoding()) {
253 case eIntegerEncoding_Quint:
254 DecodeQuintBlock(bits, result, val.BaseBitLength());
255 nValsDecoded += 3;
256 break;
257
258 case eIntegerEncoding_Trit:
259 DecodeTritBlock(bits, result, val.BaseBitLength());
260 nValsDecoded += 5;
261 break;
262
263 case eIntegerEncoding_JustBits:
264 val.SetBitValue(bits.ReadBits(val.BaseBitLength()));
265 result.push_back(val);
266 nValsDecoded++;
267 break;
268 }
269 }
270 }
271
272private:
273 static void DecodeTritBlock(BitStream& bits, std::vector<IntegerEncodedValue>& result,
274 uint32_t nBitsPerValue) {
275 // Implement the algorithm in section C.2.12
276 uint32_t m[5];
277 uint32_t t[5];
278 uint32_t T;
279
280 // Read the trit encoded block according to
281 // table C.2.14
282 m[0] = bits.ReadBits(nBitsPerValue);
283 T = bits.ReadBits(2);
284 m[1] = bits.ReadBits(nBitsPerValue);
285 T |= bits.ReadBits(2) << 2;
286 m[2] = bits.ReadBits(nBitsPerValue);
287 T |= bits.ReadBit() << 4;
288 m[3] = bits.ReadBits(nBitsPerValue);
289 T |= bits.ReadBits(2) << 5;
290 m[4] = bits.ReadBits(nBitsPerValue);
291 T |= bits.ReadBit() << 7;
292
293 uint32_t C = 0;
294
295 Bits<uint32_t> Tb(T);
296 if (Tb(2, 4) == 7) {
297 C = (Tb(5, 7) << 2) | Tb(0, 1);
298 t[4] = t[3] = 2;
299 } else {
300 C = Tb(0, 4);
301 if (Tb(5, 6) == 3) {
302 t[4] = 2;
303 t[3] = Tb[7];
304 } else {
305 t[4] = Tb[7];
306 t[3] = Tb(5, 6);
307 }
308 }
309
310 Bits<uint32_t> Cb(C);
311 if (Cb(0, 1) == 3) {
312 t[2] = 2;
313 t[1] = Cb[4];
314 t[0] = (Cb[3] << 1) | (Cb[2] & ~Cb[3]);
315 } else if (Cb(2, 3) == 3) {
316 t[2] = 2;
317 t[1] = 2;
318 t[0] = Cb(0, 1);
319 } else {
320 t[2] = Cb[4];
321 t[1] = Cb(2, 3);
322 t[0] = (Cb[1] << 1) | (Cb[0] & ~Cb[1]);
323 }
324
325 for (uint32_t i = 0; i < 5; i++) {
326 IntegerEncodedValue val(eIntegerEncoding_Trit, nBitsPerValue);
327 val.SetBitValue(m[i]);
328 val.SetTritValue(t[i]);
329 result.push_back(val);
330 }
331 }
332
333 static void DecodeQuintBlock(BitStream& bits, std::vector<IntegerEncodedValue>& result,
334 uint32_t nBitsPerValue) {
335 // Implement the algorithm in section C.2.12
336 uint32_t m[3];
337 uint32_t q[3];
338 uint32_t Q;
339
340 // Read the trit encoded block according to
341 // table C.2.15
342 m[0] = bits.ReadBits(nBitsPerValue);
343 Q = bits.ReadBits(3);
344 m[1] = bits.ReadBits(nBitsPerValue);
345 Q |= bits.ReadBits(2) << 3;
346 m[2] = bits.ReadBits(nBitsPerValue);
347 Q |= bits.ReadBits(2) << 5;
348
349 Bits<uint32_t> Qb(Q);
350 if (Qb(1, 2) == 3 && Qb(5, 6) == 0) {
351 q[0] = q[1] = 4;
352 q[2] = (Qb[0] << 2) | ((Qb[4] & ~Qb[0]) << 1) | (Qb[3] & ~Qb[0]);
353 } else {
354 uint32_t C = 0;
355 if (Qb(1, 2) == 3) {
356 q[2] = 4;
357 C = (Qb(3, 4) << 3) | ((~Qb(5, 6) & 3) << 1) | Qb[0];
358 } else {
359 q[2] = Qb(5, 6);
360 C = Qb(0, 4);
361 }
362
363 Bits<uint32_t> Cb(C);
364 if (Cb(0, 2) == 5) {
365 q[1] = 4;
366 q[0] = Cb(3, 4);
367 } else {
368 q[1] = Cb(3, 4);
369 q[0] = Cb(0, 2);
370 }
371 }
372
373 for (uint32_t i = 0; i < 3; i++) {
374 IntegerEncodedValue val(eIntegerEncoding_Quint, nBitsPerValue);
375 val.m_BitValue = m[i];
376 val.m_QuintValue = q[i];
377 result.push_back(val);
378 }
379 }
380};
381
382namespace ASTCC {
383
384struct TexelWeightParams {
385 uint32_t m_Width;
386 uint32_t m_Height;
387 bool m_bDualPlane;
388 uint32_t m_MaxWeight;
389 bool m_bError;
390 bool m_bVoidExtentLDR;
391 bool m_bVoidExtentHDR;
392
393 TexelWeightParams() {
394 memset(this, 0, sizeof(*this));
395 }
396
397 uint32_t GetPackedBitSize() {
398 // How many indices do we have?
399 uint32_t nIdxs = m_Height * m_Width;
400 if (m_bDualPlane) {
401 nIdxs *= 2;
402 }
403
404 return IntegerEncodedValue::CreateEncoding(m_MaxWeight).GetBitLength(nIdxs);
405 }
406
407 uint32_t GetNumWeightValues() const {
408 uint32_t ret = m_Width * m_Height;
409 if (m_bDualPlane) {
410 ret *= 2;
411 }
412 return ret;
413 }
414};
415
416TexelWeightParams DecodeBlockInfo(BitStream& strm) {
417 TexelWeightParams params;
418
419 // Read the entire block mode all at once
420 uint16_t modeBits = strm.ReadBits(11);
421
422 // Does this match the void extent block mode?
423 if ((modeBits & 0x01FF) == 0x1FC) {
424 if (modeBits & 0x200) {
425 params.m_bVoidExtentHDR = true;
426 } else {
427 params.m_bVoidExtentLDR = true;
428 }
429
430 // Next two bits must be one.
431 if (!(modeBits & 0x400) || !strm.ReadBit()) {
432 params.m_bError = true;
433 }
434
435 return params;
436 }
437
438 // First check if the last four bits are zero
439 if ((modeBits & 0xF) == 0) {
440 params.m_bError = true;
441 return params;
442 }
443
444 // If the last two bits are zero, then if bits
445 // [6-8] are all ones, this is also reserved.
446 if ((modeBits & 0x3) == 0 && (modeBits & 0x1C0) == 0x1C0) {
447 params.m_bError = true;
448 return params;
449 }
450
451 // Otherwise, there is no error... Figure out the layout
452 // of the block mode. Layout is determined by a number
453 // between 0 and 9 corresponding to table C.2.8 of the
454 // ASTC spec.
455 uint32_t layout = 0;
456
457 if ((modeBits & 0x1) || (modeBits & 0x2)) {
458 // layout is in [0-4]
459 if (modeBits & 0x8) {
460 // layout is in [2-4]
461 if (modeBits & 0x4) {
462 // layout is in [3-4]
463 if (modeBits & 0x100) {
464 layout = 4;
465 } else {
466 layout = 3;
467 }
468 } else {
469 layout = 2;
470 }
471 } else {
472 // layout is in [0-1]
473 if (modeBits & 0x4) {
474 layout = 1;
475 } else {
476 layout = 0;
477 }
478 }
479 } else {
480 // layout is in [5-9]
481 if (modeBits & 0x100) {
482 // layout is in [7-9]
483 if (modeBits & 0x80) {
484 // layout is in [7-8]
485 assert((modeBits & 0x40) == 0U);
486 if (modeBits & 0x20) {
487 layout = 8;
488 } else {
489 layout = 7;
490 }
491 } else {
492 layout = 9;
493 }
494 } else {
495 // layout is in [5-6]
496 if (modeBits & 0x80) {
497 layout = 6;
498 } else {
499 layout = 5;
500 }
501 }
502 }
503
504 assert(layout < 10);
505
506 // Determine R
507 uint32_t R = !!(modeBits & 0x10);
508 if (layout < 5) {
509 R |= (modeBits & 0x3) << 1;
510 } else {
511 R |= (modeBits & 0xC) >> 1;
512 }
513 assert(2 <= R && R <= 7);
514
515 // Determine width & height
516 switch (layout) {
517 case 0: {
518 uint32_t A = (modeBits >> 5) & 0x3;
519 uint32_t B = (modeBits >> 7) & 0x3;
520 params.m_Width = B + 4;
521 params.m_Height = A + 2;
522 break;
523 }
524
525 case 1: {
526 uint32_t A = (modeBits >> 5) & 0x3;
527 uint32_t B = (modeBits >> 7) & 0x3;
528 params.m_Width = B + 8;
529 params.m_Height = A + 2;
530 break;
531 }
532
533 case 2: {
534 uint32_t A = (modeBits >> 5) & 0x3;
535 uint32_t B = (modeBits >> 7) & 0x3;
536 params.m_Width = A + 2;
537 params.m_Height = B + 8;
538 break;
539 }
540
541 case 3: {
542 uint32_t A = (modeBits >> 5) & 0x3;
543 uint32_t B = (modeBits >> 7) & 0x1;
544 params.m_Width = A + 2;
545 params.m_Height = B + 6;
546 break;
547 }
548
549 case 4: {
550 uint32_t A = (modeBits >> 5) & 0x3;
551 uint32_t B = (modeBits >> 7) & 0x1;
552 params.m_Width = B + 2;
553 params.m_Height = A + 2;
554 break;
555 }
556
557 case 5: {
558 uint32_t A = (modeBits >> 5) & 0x3;
559 params.m_Width = 12;
560 params.m_Height = A + 2;
561 break;
562 }
563
564 case 6: {
565 uint32_t A = (modeBits >> 5) & 0x3;
566 params.m_Width = A + 2;
567 params.m_Height = 12;
568 break;
569 }
570
571 case 7: {
572 params.m_Width = 6;
573 params.m_Height = 10;
574 break;
575 }
576
577 case 8: {
578 params.m_Width = 10;
579 params.m_Height = 6;
580 break;
581 }
582
583 case 9: {
584 uint32_t A = (modeBits >> 5) & 0x3;
585 uint32_t B = (modeBits >> 9) & 0x3;
586 params.m_Width = A + 6;
587 params.m_Height = B + 6;
588 break;
589 }
590
591 default:
592 assert(!"Don't know this layout...");
593 params.m_bError = true;
594 break;
595 }
596
597 // Determine whether or not we're using dual planes
598 // and/or high precision layouts.
599 bool D = (layout != 9) && (modeBits & 0x400);
600 bool H = (layout != 9) && (modeBits & 0x200);
601
602 if (H) {
603 const uint32_t maxWeights[6] = {9, 11, 15, 19, 23, 31};
604 params.m_MaxWeight = maxWeights[R - 2];
605 } else {
606 const uint32_t maxWeights[6] = {1, 2, 3, 4, 5, 7};
607 params.m_MaxWeight = maxWeights[R - 2];
608 }
609
610 params.m_bDualPlane = D;
611
612 return params;
613}
614
615void FillVoidExtentLDR(BitStream& strm, uint32_t* const outBuf, uint32_t blockWidth,
616 uint32_t blockHeight) {
617 // Don't actually care about the void extent, just read the bits...
618 for (int i = 0; i < 4; ++i) {
619 strm.ReadBits(13);
620 }
621
622 // Decode the RGBA components and renormalize them to the range [0, 255]
623 uint16_t r = strm.ReadBits(16);
624 uint16_t g = strm.ReadBits(16);
625 uint16_t b = strm.ReadBits(16);
626 uint16_t a = strm.ReadBits(16);
627
628 uint32_t rgba = (r >> 8) | (g & 0xFF00) | (static_cast<uint32_t>(b) & 0xFF00) << 8 |
629 (static_cast<uint32_t>(a) & 0xFF00) << 16;
630
631 for (uint32_t j = 0; j < blockHeight; j++)
632 for (uint32_t i = 0; i < blockWidth; i++) {
633 outBuf[j * blockWidth + i] = rgba;
634 }
635}
636
637void FillError(uint32_t* outBuf, uint32_t blockWidth, uint32_t blockHeight) {
638 for (uint32_t j = 0; j < blockHeight; j++)
639 for (uint32_t i = 0; i < blockWidth; i++) {
640 outBuf[j * blockWidth + i] = 0xFFFF00FF;
641 }
642}
643
644// Replicates low numBits such that [(toBit - 1):(toBit - 1 - fromBit)]
645// is the same as [(numBits - 1):0] and repeats all the way down.
646template <typename IntType>
647IntType Replicate(const IntType& val, uint32_t numBits, uint32_t toBit) {
648 if (numBits == 0)
649 return 0;
650 if (toBit == 0)
651 return 0;
652 IntType v = val & ((1 << numBits) - 1);
653 IntType res = v;
654 uint32_t reslen = numBits;
655 while (reslen < toBit) {
656 uint32_t comp = 0;
657 if (numBits > toBit - reslen) {
658 uint32_t newshift = toBit - reslen;
659 comp = numBits - newshift;
660 numBits = newshift;
661 }
662 res <<= numBits;
663 res |= v >> comp;
664 reslen += numBits;
665 }
666 return res;
667}
668
669class Pixel {
670protected:
671 typedef int16_t ChannelType;
672 uint8_t m_BitDepth[4];
673 int16_t color[4];
674
675public:
676 Pixel() {
677 for (int i = 0; i < 4; i++) {
678 m_BitDepth[i] = 8;
679 color[i] = 0;
680 }
681 }
682
683 Pixel(ChannelType a, ChannelType r, ChannelType g, ChannelType b, unsigned bitDepth = 8) {
684 for (int i = 0; i < 4; i++)
685 m_BitDepth[i] = bitDepth;
686
687 color[0] = a;
688 color[1] = r;
689 color[2] = g;
690 color[3] = b;
691 }
692
693 // Changes the depth of each pixel. This scales the values to
694 // the appropriate bit depth by either truncating the least
695 // significant bits when going from larger to smaller bit depth
696 // or by repeating the most significant bits when going from
697 // smaller to larger bit depths.
698 void ChangeBitDepth(const uint8_t (&depth)[4]) {
699 for (uint32_t i = 0; i < 4; i++) {
700 Component(i) = ChangeBitDepth(Component(i), m_BitDepth[i], depth[i]);
701 m_BitDepth[i] = depth[i];
702 }
703 }
704
705 template <typename IntType>
706 static float ConvertChannelToFloat(IntType channel, uint8_t bitDepth) {
707 float denominator = static_cast<float>((1 << bitDepth) - 1);
708 return static_cast<float>(channel) / denominator;
709 }
710
711 // Changes the bit depth of a single component. See the comment
712 // above for how we do this.
713 static ChannelType ChangeBitDepth(Pixel::ChannelType val, uint8_t oldDepth, uint8_t newDepth) {
714 assert(newDepth <= 8);
715 assert(oldDepth <= 8);
716
717 if (oldDepth == newDepth) {
718 // Do nothing
719 return val;
720 } else if (oldDepth == 0 && newDepth != 0) {
721 return (1 << newDepth) - 1;
722 } else if (newDepth > oldDepth) {
723 return Replicate(val, oldDepth, newDepth);
724 } else {
725 // oldDepth > newDepth
726 if (newDepth == 0) {
727 return 0xFF;
728 } else {
729 uint8_t bitsWasted = oldDepth - newDepth;
730 uint16_t v = static_cast<uint16_t>(val);
731 v = (v + (1 << (bitsWasted - 1))) >> bitsWasted;
732 v = ::std::min<uint16_t>(::std::max<uint16_t>(0, v), (1 << newDepth) - 1);
733 return static_cast<uint8_t>(v);
734 }
735 }
736
737 assert(!"We shouldn't get here.");
738 return 0;
739 }
740
741 const ChannelType& A() const {
742 return color[0];
743 }
744 ChannelType& A() {
745 return color[0];
746 }
747 const ChannelType& R() const {
748 return color[1];
749 }
750 ChannelType& R() {
751 return color[1];
752 }
753 const ChannelType& G() const {
754 return color[2];
755 }
756 ChannelType& G() {
757 return color[2];
758 }
759 const ChannelType& B() const {
760 return color[3];
761 }
762 ChannelType& B() {
763 return color[3];
764 }
765 const ChannelType& Component(uint32_t idx) const {
766 return color[idx];
767 }
768 ChannelType& Component(uint32_t idx) {
769 return color[idx];
770 }
771
772 void GetBitDepth(uint8_t (&outDepth)[4]) const {
773 for (int i = 0; i < 4; i++) {
774 outDepth[i] = m_BitDepth[i];
775 }
776 }
777
778 // Take all of the components, transform them to their 8-bit variants,
779 // and then pack each channel into an R8G8B8A8 32-bit integer. We assume
780 // that the architecture is little-endian, so the alpha channel will end
781 // up in the most-significant byte.
782 uint32_t Pack() const {
783 Pixel eightBit(*this);
784 const uint8_t eightBitDepth[4] = {8, 8, 8, 8};
785 eightBit.ChangeBitDepth(eightBitDepth);
786
787 uint32_t r = 0;
788 r |= eightBit.A();
789 r <<= 8;
790 r |= eightBit.B();
791 r <<= 8;
792 r |= eightBit.G();
793 r <<= 8;
794 r |= eightBit.R();
795 return r;
796 }
797
798 // Clamps the pixel to the range [0,255]
799 void ClampByte() {
800 for (uint32_t i = 0; i < 4; i++) {
801 color[i] = (color[i] < 0) ? 0 : ((color[i] > 255) ? 255 : color[i]);
802 }
803 }
804
805 void MakeOpaque() {
806 A() = 255;
807 }
808};
809
810void DecodeColorValues(uint32_t* out, uint8_t* data, uint32_t* modes, const uint32_t nPartitions,
811 const uint32_t nBitsForColorData) {
812 // First figure out how many color values we have
813 uint32_t nValues = 0;
814 for (uint32_t i = 0; i < nPartitions; i++) {
815 nValues += ((modes[i] >> 2) + 1) << 1;
816 }
817
818 // Then based on the number of values and the remaining number of bits,
819 // figure out the max value for each of them...
820 uint32_t range = 256;
821 while (--range > 0) {
822 IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(range);
823 uint32_t bitLength = val.GetBitLength(nValues);
824 if (bitLength <= nBitsForColorData) {
825 // Find the smallest possible range that matches the given encoding
826 while (--range > 0) {
827 IntegerEncodedValue newval = IntegerEncodedValue::CreateEncoding(range);
828 if (!newval.MatchesEncoding(val)) {
829 break;
830 }
831 }
832
833 // Return to last matching range.
834 range++;
835 break;
836 }
837 }
838
839 // We now have enough to decode our integer sequence.
840 std::vector<IntegerEncodedValue> decodedColorValues;
841 BitStream colorStream(data);
842 IntegerEncodedValue::DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues);
843
844 // Once we have the decoded values, we need to dequantize them to the 0-255 range
845 // This procedure is outlined in ASTC spec C.2.13
846 uint32_t outIdx = 0;
847 std::vector<IntegerEncodedValue>::const_iterator itr;
848 for (itr = decodedColorValues.begin(); itr != decodedColorValues.end(); itr++) {
849 // Have we already decoded all that we need?
850 if (outIdx >= nValues) {
851 break;
852 }
853
854 const IntegerEncodedValue& val = *itr;
855 uint32_t bitlen = val.BaseBitLength();
856 uint32_t bitval = val.GetBitValue();
857
858 assert(bitlen >= 1);
859
860 uint32_t A = 0, B = 0, C = 0, D = 0;
861 // A is just the lsb replicated 9 times.
862 A = Replicate(bitval & 1, 1, 9);
863
864 switch (val.GetEncoding()) {
865 // Replicate bits
866 case eIntegerEncoding_JustBits:
867 out[outIdx++] = Replicate(bitval, bitlen, 8);
868 break;
869
870 // Use algorithm in C.2.13
871 case eIntegerEncoding_Trit: {
872
873 D = val.GetTritValue();
874
875 switch (bitlen) {
876 case 1: {
877 C = 204;
878 } break;
879
880 case 2: {
881 C = 93;
882 // B = b000b0bb0
883 uint32_t b = (bitval >> 1) & 1;
884 B = (b << 8) | (b << 4) | (b << 2) | (b << 1);
885 } break;
886
887 case 3: {
888 C = 44;
889 // B = cb000cbcb
890 uint32_t cb = (bitval >> 1) & 3;
891 B = (cb << 7) | (cb << 2) | cb;
892 } break;
893
894 case 4: {
895 C = 22;
896 // B = dcb000dcb
897 uint32_t dcb = (bitval >> 1) & 7;
898 B = (dcb << 6) | dcb;
899 } break;
900
901 case 5: {
902 C = 11;
903 // B = edcb000ed
904 uint32_t edcb = (bitval >> 1) & 0xF;
905 B = (edcb << 5) | (edcb >> 2);
906 } break;
907
908 case 6: {
909 C = 5;
910 // B = fedcb000f
911 uint32_t fedcb = (bitval >> 1) & 0x1F;
912 B = (fedcb << 4) | (fedcb >> 4);
913 } break;
914
915 default:
916 assert(!"Unsupported trit encoding for color values!");
917 break;
918 } // switch(bitlen)
919 } // case eIntegerEncoding_Trit
920 break;
921
922 case eIntegerEncoding_Quint: {
923
924 D = val.GetQuintValue();
925
926 switch (bitlen) {
927 case 1: {
928 C = 113;
929 } break;
930
931 case 2: {
932 C = 54;
933 // B = b0000bb00
934 uint32_t b = (bitval >> 1) & 1;
935 B = (b << 8) | (b << 3) | (b << 2);
936 } break;
937
938 case 3: {
939 C = 26;
940 // B = cb0000cbc
941 uint32_t cb = (bitval >> 1) & 3;
942 B = (cb << 7) | (cb << 1) | (cb >> 1);
943 } break;
944
945 case 4: {
946 C = 13;
947 // B = dcb0000dc
948 uint32_t dcb = (bitval >> 1) & 7;
949 B = (dcb << 6) | (dcb >> 1);
950 } break;
951
952 case 5: {
953 C = 6;
954 // B = edcb0000e
955 uint32_t edcb = (bitval >> 1) & 0xF;
956 B = (edcb << 5) | (edcb >> 3);
957 } break;
958
959 default:
960 assert(!"Unsupported quint encoding for color values!");
961 break;
962 } // switch(bitlen)
963 } // case eIntegerEncoding_Quint
964 break;
965 } // switch(val.GetEncoding())
966
967 if (val.GetEncoding() != eIntegerEncoding_JustBits) {
968 uint32_t T = D * C + B;
969 T ^= A;
970 T = (A & 0x80) | (T >> 2);
971 out[outIdx++] = T;
972 }
973 }
974
975 // Make sure that each of our values is in the proper range...
976 for (uint32_t i = 0; i < nValues; i++) {
977 assert(out[i] <= 255);
978 }
979}
980
981uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
982 uint32_t bitval = val.GetBitValue();
983 uint32_t bitlen = val.BaseBitLength();
984
985 uint32_t A = Replicate(bitval & 1, 1, 7);
986 uint32_t B = 0, C = 0, D = 0;
987
988 uint32_t result = 0;
989 switch (val.GetEncoding()) {
990 case eIntegerEncoding_JustBits:
991 result = Replicate(bitval, bitlen, 6);
992 break;
993
994 case eIntegerEncoding_Trit: {
995 D = val.GetTritValue();
996 assert(D < 3);
997
998 switch (bitlen) {
999 case 0: {
1000 uint32_t results[3] = {0, 32, 63};
1001 result = results[D];
1002 } break;
1003
1004 case 1: {
1005 C = 50;
1006 } break;
1007
1008 case 2: {
1009 C = 23;
1010 uint32_t b = (bitval >> 1) & 1;
1011 B = (b << 6) | (b << 2) | b;
1012 } break;
1013
1014 case 3: {
1015 C = 11;
1016 uint32_t cb = (bitval >> 1) & 3;
1017 B = (cb << 5) | cb;
1018 } break;
1019
1020 default:
1021 assert(!"Invalid trit encoding for texel weight");
1022 break;
1023 }
1024 } break;
1025
1026 case eIntegerEncoding_Quint: {
1027 D = val.GetQuintValue();
1028 assert(D < 5);
1029
1030 switch (bitlen) {
1031 case 0: {
1032 uint32_t results[5] = {0, 16, 32, 47, 63};
1033 result = results[D];
1034 } break;
1035
1036 case 1: {
1037 C = 28;
1038 } break;
1039
1040 case 2: {
1041 C = 13;
1042 uint32_t b = (bitval >> 1) & 1;
1043 B = (b << 6) | (b << 1);
1044 } break;
1045
1046 default:
1047 assert(!"Invalid quint encoding for texel weight");
1048 break;
1049 }
1050 } break;
1051 }
1052
1053 if (val.GetEncoding() != eIntegerEncoding_JustBits && bitlen > 0) {
1054 // Decode the value...
1055 result = D * C + B;
1056 result ^= A;
1057 result = (A & 0x20) | (result >> 2);
1058 }
1059
1060 assert(result < 64);
1061
1062 // Change from [0,63] to [0,64]
1063 if (result > 32) {
1064 result += 1;
1065 }
1066
1067 return result;
1068}
1069
1070void UnquantizeTexelWeights(uint32_t out[2][144], std::vector<IntegerEncodedValue>& weights,
1071 const TexelWeightParams& params, const uint32_t blockWidth,
1072 const uint32_t blockHeight) {
1073 uint32_t weightIdx = 0;
1074 uint32_t unquantized[2][144];
1075 std::vector<IntegerEncodedValue>::const_iterator itr;
1076 for (itr = weights.begin(); itr != weights.end(); itr++) {
1077 unquantized[0][weightIdx] = UnquantizeTexelWeight(*itr);
1078
1079 if (params.m_bDualPlane) {
1080 itr++;
1081 unquantized[1][weightIdx] = UnquantizeTexelWeight(*itr);
1082 if (itr == weights.end()) {
1083 break;
1084 }
1085 }
1086
1087 if (++weightIdx >= (params.m_Width * params.m_Height))
1088 break;
1089 }
1090
1091 // Do infill if necessary (Section C.2.18) ...
1092 uint32_t Ds = (1024 + (blockWidth / 2)) / (blockWidth - 1);
1093 uint32_t Dt = (1024 + (blockHeight / 2)) / (blockHeight - 1);
1094
1095 const uint32_t kPlaneScale = params.m_bDualPlane ? 2U : 1U;
1096 for (uint32_t plane = 0; plane < kPlaneScale; plane++)
1097 for (uint32_t t = 0; t < blockHeight; t++)
1098 for (uint32_t s = 0; s < blockWidth; s++) {
1099 uint32_t cs = Ds * s;
1100 uint32_t ct = Dt * t;
1101
1102 uint32_t gs = (cs * (params.m_Width - 1) + 32) >> 6;
1103 uint32_t gt = (ct * (params.m_Height - 1) + 32) >> 6;
1104
1105 uint32_t js = gs >> 4;
1106 uint32_t fs = gs & 0xF;
1107
1108 uint32_t jt = gt >> 4;
1109 uint32_t ft = gt & 0x0F;
1110
1111 uint32_t w11 = (fs * ft + 8) >> 4;
1112 uint32_t w10 = ft - w11;
1113 uint32_t w01 = fs - w11;
1114 uint32_t w00 = 16 - fs - ft + w11;
1115
1116 uint32_t v0 = js + jt * params.m_Width;
1117
1118#define FIND_TEXEL(tidx, bidx) \
1119 uint32_t p##bidx = 0; \
1120 do { \
1121 if ((tidx) < (params.m_Width * params.m_Height)) { \
1122 p##bidx = unquantized[plane][(tidx)]; \
1123 } \
1124 } while (0)
1125
1126 FIND_TEXEL(v0, 00);
1127 FIND_TEXEL(v0 + 1, 01);
1128 FIND_TEXEL(v0 + params.m_Width, 10);
1129 FIND_TEXEL(v0 + params.m_Width + 1, 11);
1130
1131#undef FIND_TEXEL
1132
1133 out[plane][t * blockWidth + s] =
1134 (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4;
1135 }
1136}
1137
1138// Transfers a bit as described in C.2.14
1139static inline void BitTransferSigned(int32_t& a, int32_t& b) {
1140 b >>= 1;
1141 b |= a & 0x80;
1142 a >>= 1;
1143 a &= 0x3F;
1144 if (a & 0x20)
1145 a -= 0x40;
1146}
1147
1148// Adds more precision to the blue channel as described
1149// in C.2.14
1150static inline Pixel BlueContract(int32_t a, int32_t r, int32_t g, int32_t b) {
1151 return Pixel(static_cast<int16_t>(a), static_cast<int16_t>((r + b) >> 1),
1152 static_cast<int16_t>((g + b) >> 1), static_cast<int16_t>(b));
1153}
1154
1155// Partition selection functions as specified in
1156// C.2.21
1157static inline uint32_t hash52(uint32_t p) {
1158 p ^= p >> 15;
1159 p -= p << 17;
1160 p += p << 7;
1161 p += p << 4;
1162 p ^= p >> 5;
1163 p += p << 16;
1164 p ^= p >> 7;
1165 p ^= p >> 3;
1166 p ^= p << 6;
1167 p ^= p >> 17;
1168 return p;
1169}
1170
1171static uint32_t SelectPartition(int32_t seed, int32_t x, int32_t y, int32_t z,
1172 int32_t partitionCount, int32_t smallBlock) {
1173 if (1 == partitionCount)
1174 return 0;
1175
1176 if (smallBlock) {
1177 x <<= 1;
1178 y <<= 1;
1179 z <<= 1;
1180 }
1181
1182 seed += (partitionCount - 1) * 1024;
1183
1184 uint32_t rnum = hash52(static_cast<uint32_t>(seed));
1185 uint8_t seed1 = static_cast<uint8_t>(rnum & 0xF);
1186 uint8_t seed2 = static_cast<uint8_t>((rnum >> 4) & 0xF);
1187 uint8_t seed3 = static_cast<uint8_t>((rnum >> 8) & 0xF);
1188 uint8_t seed4 = static_cast<uint8_t>((rnum >> 12) & 0xF);
1189 uint8_t seed5 = static_cast<uint8_t>((rnum >> 16) & 0xF);
1190 uint8_t seed6 = static_cast<uint8_t>((rnum >> 20) & 0xF);
1191 uint8_t seed7 = static_cast<uint8_t>((rnum >> 24) & 0xF);
1192 uint8_t seed8 = static_cast<uint8_t>((rnum >> 28) & 0xF);
1193 uint8_t seed9 = static_cast<uint8_t>((rnum >> 18) & 0xF);
1194 uint8_t seed10 = static_cast<uint8_t>((rnum >> 22) & 0xF);
1195 uint8_t seed11 = static_cast<uint8_t>((rnum >> 26) & 0xF);
1196 uint8_t seed12 = static_cast<uint8_t>(((rnum >> 30) | (rnum << 2)) & 0xF);
1197
1198 seed1 *= seed1;
1199 seed2 *= seed2;
1200 seed3 *= seed3;
1201 seed4 *= seed4;
1202 seed5 *= seed5;
1203 seed6 *= seed6;
1204 seed7 *= seed7;
1205 seed8 *= seed8;
1206 seed9 *= seed9;
1207 seed10 *= seed10;
1208 seed11 *= seed11;
1209 seed12 *= seed12;
1210
1211 int32_t sh1, sh2, sh3;
1212 if (seed & 1) {
1213 sh1 = (seed & 2) ? 4 : 5;
1214 sh2 = (partitionCount == 3) ? 6 : 5;
1215 } else {
1216 sh1 = (partitionCount == 3) ? 6 : 5;
1217 sh2 = (seed & 2) ? 4 : 5;
1218 }
1219 sh3 = (seed & 0x10) ? sh1 : sh2;
1220
1221 seed1 >>= sh1;
1222 seed2 >>= sh2;
1223 seed3 >>= sh1;
1224 seed4 >>= sh2;
1225 seed5 >>= sh1;
1226 seed6 >>= sh2;
1227 seed7 >>= sh1;
1228 seed8 >>= sh2;
1229 seed9 >>= sh3;
1230 seed10 >>= sh3;
1231 seed11 >>= sh3;
1232 seed12 >>= sh3;
1233
1234 int32_t a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
1235 int32_t b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
1236 int32_t c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
1237 int32_t d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
1238
1239 a &= 0x3F;
1240 b &= 0x3F;
1241 c &= 0x3F;
1242 d &= 0x3F;
1243
1244 if (partitionCount < 4)
1245 d = 0;
1246 if (partitionCount < 3)
1247 c = 0;
1248
1249 if (a >= b && a >= c && a >= d)
1250 return 0;
1251 else if (b >= c && b >= d)
1252 return 1;
1253 else if (c >= d)
1254 return 2;
1255 return 3;
1256}
1257
1258static inline uint32_t Select2DPartition(int32_t seed, int32_t x, int32_t y, int32_t partitionCount,
1259 int32_t smallBlock) {
1260 return SelectPartition(seed, x, y, 0, partitionCount, smallBlock);
1261}
1262
1263// Section C.2.14
1264void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValues,
1265 uint32_t colorEndpointMode) {
1266#define READ_UINT_VALUES(N) \
1267 uint32_t v[N]; \
1268 for (uint32_t i = 0; i < N; i++) { \
1269 v[i] = *(colorValues++); \
1270 }
1271
1272#define READ_INT_VALUES(N) \
1273 int32_t v[N]; \
1274 for (uint32_t i = 0; i < N; i++) { \
1275 v[i] = static_cast<int32_t>(*(colorValues++)); \
1276 }
1277
1278 switch (colorEndpointMode) {
1279 case 0: {
1280 READ_UINT_VALUES(2)
1281 ep1 = Pixel(0xFF, v[0], v[0], v[0]);
1282 ep2 = Pixel(0xFF, v[1], v[1], v[1]);
1283 } break;
1284
1285 case 1: {
1286 READ_UINT_VALUES(2)
1287 uint32_t L0 = (v[0] >> 2) | (v[1] & 0xC0);
1288 uint32_t L1 = std::max(L0 + (v[1] & 0x3F), 0xFFU);
1289 ep1 = Pixel(0xFF, L0, L0, L0);
1290 ep2 = Pixel(0xFF, L1, L1, L1);
1291 } break;
1292
1293 case 4: {
1294 READ_UINT_VALUES(4)
1295 ep1 = Pixel(v[2], v[0], v[0], v[0]);
1296 ep2 = Pixel(v[3], v[1], v[1], v[1]);
1297 } break;
1298
1299 case 5: {
1300 READ_INT_VALUES(4)
1301 BitTransferSigned(v[1], v[0]);
1302 BitTransferSigned(v[3], v[2]);
1303 ep1 = Pixel(v[2], v[0], v[0], v[0]);
1304 ep2 = Pixel(v[2] + v[3], v[0] + v[1], v[0] + v[1], v[0] + v[1]);
1305 ep1.ClampByte();
1306 ep2.ClampByte();
1307 } break;
1308
1309 case 6: {
1310 READ_UINT_VALUES(4)
1311 ep1 = Pixel(0xFF, v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8);
1312 ep2 = Pixel(0xFF, v[0], v[1], v[2]);
1313 } break;
1314
1315 case 8: {
1316 READ_UINT_VALUES(6)
1317 if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) {
1318 ep1 = Pixel(0xFF, v[0], v[2], v[4]);
1319 ep2 = Pixel(0xFF, v[1], v[3], v[5]);
1320 } else {
1321 ep1 = BlueContract(0xFF, v[1], v[3], v[5]);
1322 ep2 = BlueContract(0xFF, v[0], v[2], v[4]);
1323 }
1324 } break;
1325
1326 case 9: {
1327 READ_INT_VALUES(6)
1328 BitTransferSigned(v[1], v[0]);
1329 BitTransferSigned(v[3], v[2]);
1330 BitTransferSigned(v[5], v[4]);
1331 if (v[1] + v[3] + v[5] >= 0) {
1332 ep1 = Pixel(0xFF, v[0], v[2], v[4]);
1333 ep2 = Pixel(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1334 } else {
1335 ep1 = BlueContract(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1336 ep2 = BlueContract(0xFF, v[0], v[2], v[4]);
1337 }
1338 ep1.ClampByte();
1339 ep2.ClampByte();
1340 } break;
1341
1342 case 10: {
1343 READ_UINT_VALUES(6)
1344 ep1 = Pixel(v[4], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8);
1345 ep2 = Pixel(v[5], v[0], v[1], v[2]);
1346 } break;
1347
1348 case 12: {
1349 READ_UINT_VALUES(8)
1350 if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) {
1351 ep1 = Pixel(v[6], v[0], v[2], v[4]);
1352 ep2 = Pixel(v[7], v[1], v[3], v[5]);
1353 } else {
1354 ep1 = BlueContract(v[7], v[1], v[3], v[5]);
1355 ep2 = BlueContract(v[6], v[0], v[2], v[4]);
1356 }
1357 } break;
1358
1359 case 13: {
1360 READ_INT_VALUES(8)
1361 BitTransferSigned(v[1], v[0]);
1362 BitTransferSigned(v[3], v[2]);
1363 BitTransferSigned(v[5], v[4]);
1364 BitTransferSigned(v[7], v[6]);
1365 if (v[1] + v[3] + v[5] >= 0) {
1366 ep1 = Pixel(v[6], v[0], v[2], v[4]);
1367 ep2 = Pixel(v[7] + v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1368 } else {
1369 ep1 = BlueContract(v[6] + v[7], v[0] + v[1], v[2] + v[3], v[4] + v[5]);
1370 ep2 = BlueContract(v[6], v[0], v[2], v[4]);
1371 }
1372 ep1.ClampByte();
1373 ep2.ClampByte();
1374 } break;
1375
1376 default:
1377 assert(!"Unsupported color endpoint mode (is it HDR?)");
1378 break;
1379 }
1380
1381#undef READ_UINT_VALUES
1382#undef READ_INT_VALUES
1383}
1384
1385void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth, const uint32_t blockHeight,
1386 uint32_t* outBuf) {
1387 BitStream strm(inBuf);
1388 TexelWeightParams weightParams = DecodeBlockInfo(strm);
1389
1390 // Was there an error?
1391 if (weightParams.m_bError) {
1392 assert(!"Invalid block mode");
1393 FillError(outBuf, blockWidth, blockHeight);
1394 return;
1395 }
1396
1397 if (weightParams.m_bVoidExtentLDR) {
1398 FillVoidExtentLDR(strm, outBuf, blockWidth, blockHeight);
1399 return;
1400 }
1401
1402 if (weightParams.m_bVoidExtentHDR) {
1403 assert(!"HDR void extent blocks are unsupported!");
1404 FillError(outBuf, blockWidth, blockHeight);
1405 return;
1406 }
1407
1408 if (weightParams.m_Width > blockWidth) {
1409 assert(!"Texel weight grid width should be smaller than block width");
1410 FillError(outBuf, blockWidth, blockHeight);
1411 return;
1412 }
1413
1414 if (weightParams.m_Height > blockHeight) {
1415 assert(!"Texel weight grid height should be smaller than block height");
1416 FillError(outBuf, blockWidth, blockHeight);
1417 return;
1418 }
1419
1420 // Read num partitions
1421 uint32_t nPartitions = strm.ReadBits(2) + 1;
1422 assert(nPartitions <= 4);
1423
1424 if (nPartitions == 4 && weightParams.m_bDualPlane) {
1425 assert(!"Dual plane mode is incompatible with four partition blocks");
1426 FillError(outBuf, blockWidth, blockHeight);
1427 return;
1428 }
1429
1430 // Based on the number of partitions, read the color endpoint mode for
1431 // each partition.
1432
1433 // Determine partitions, partition index, and color endpoint modes
1434 int32_t planeIdx = -1;
1435 uint32_t partitionIndex;
1436 uint32_t colorEndpointMode[4] = {0, 0, 0, 0};
1437
1438 // Define color data.
1439 uint8_t colorEndpointData[16];
1440 memset(colorEndpointData, 0, sizeof(colorEndpointData));
1441 BitStream colorEndpointStream(colorEndpointData, 16 * 8, 0);
1442
1443 // Read extra config data...
1444 uint32_t baseCEM = 0;
1445 if (nPartitions == 1) {
1446 colorEndpointMode[0] = strm.ReadBits(4);
1447 partitionIndex = 0;
1448 } else {
1449 partitionIndex = strm.ReadBits(10);
1450 baseCEM = strm.ReadBits(6);
1451 }
1452 uint32_t baseMode = (baseCEM & 3);
1453
1454 // Remaining bits are color endpoint data...
1455 uint32_t nWeightBits = weightParams.GetPackedBitSize();
1456 int32_t remainingBits = 128 - nWeightBits - strm.GetBitsRead();
1457
1458 // Consider extra bits prior to texel data...
1459 uint32_t extraCEMbits = 0;
1460 if (baseMode) {
1461 switch (nPartitions) {
1462 case 2:
1463 extraCEMbits += 2;
1464 break;
1465 case 3:
1466 extraCEMbits += 5;
1467 break;
1468 case 4:
1469 extraCEMbits += 8;
1470 break;
1471 default:
1472 assert(false);
1473 break;
1474 }
1475 }
1476 remainingBits -= extraCEMbits;
1477
1478 // Do we have a dual plane situation?
1479 uint32_t planeSelectorBits = 0;
1480 if (weightParams.m_bDualPlane) {
1481 planeSelectorBits = 2;
1482 }
1483 remainingBits -= planeSelectorBits;
1484
1485 // Read color data...
1486 uint32_t colorDataBits = remainingBits;
1487 while (remainingBits > 0) {
1488 uint32_t nb = std::min(remainingBits, 8);
1489 uint32_t b = strm.ReadBits(nb);
1490 colorEndpointStream.WriteBits(b, nb);
1491 remainingBits -= 8;
1492 }
1493
1494 // Read the plane selection bits
1495 planeIdx = strm.ReadBits(planeSelectorBits);
1496
1497 // Read the rest of the CEM
1498 if (baseMode) {
1499 uint32_t extraCEM = strm.ReadBits(extraCEMbits);
1500 uint32_t CEM = (extraCEM << 6) | baseCEM;
1501 CEM >>= 2;
1502
1503 bool C[4] = {0};
1504 for (uint32_t i = 0; i < nPartitions; i++) {
1505 C[i] = CEM & 1;
1506 CEM >>= 1;
1507 }
1508
1509 uint8_t M[4] = {0};
1510 for (uint32_t i = 0; i < nPartitions; i++) {
1511 M[i] = CEM & 3;
1512 CEM >>= 2;
1513 assert(M[i] <= 3);
1514 }
1515
1516 for (uint32_t i = 0; i < nPartitions; i++) {
1517 colorEndpointMode[i] = baseMode;
1518 if (!(C[i]))
1519 colorEndpointMode[i] -= 1;
1520 colorEndpointMode[i] <<= 2;
1521 colorEndpointMode[i] |= M[i];
1522 }
1523 } else if (nPartitions > 1) {
1524 uint32_t CEM = baseCEM >> 2;
1525 for (uint32_t i = 0; i < nPartitions; i++) {
1526 colorEndpointMode[i] = CEM;
1527 }
1528 }
1529
1530 // Make sure everything up till here is sane.
1531 for (uint32_t i = 0; i < nPartitions; i++) {
1532 assert(colorEndpointMode[i] < 16);
1533 }
1534 assert(strm.GetBitsRead() + weightParams.GetPackedBitSize() == 128);
1535
1536 // Decode both color data and texel weight data
1537 uint32_t colorValues[32]; // Four values, two endpoints, four maximum paritions
1538 DecodeColorValues(colorValues, colorEndpointData, colorEndpointMode, nPartitions,
1539 colorDataBits);
1540
1541 Pixel endpoints[4][2];
1542 const uint32_t* colorValuesPtr = colorValues;
1543 for (uint32_t i = 0; i < nPartitions; i++) {
1544 ComputeEndpoints(endpoints[i][0], endpoints[i][1], colorValuesPtr, colorEndpointMode[i]);
1545 }
1546
1547 // Read the texel weight data..
1548 uint8_t texelWeightData[16];
1549 memcpy(texelWeightData, inBuf, sizeof(texelWeightData));
1550
1551 // Reverse everything
1552 for (uint32_t i = 0; i < 8; i++) {
1553// Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
1554#define REVERSE_BYTE(b) (((b)*0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32
1555 unsigned char a = static_cast<unsigned char>(REVERSE_BYTE(texelWeightData[i]));
1556 unsigned char b = static_cast<unsigned char>(REVERSE_BYTE(texelWeightData[15 - i]));
1557#undef REVERSE_BYTE
1558
1559 texelWeightData[i] = b;
1560 texelWeightData[15 - i] = a;
1561 }
1562
1563 // Make sure that higher non-texel bits are set to zero
1564 const uint32_t clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1;
1565 texelWeightData[clearByteStart - 1] &= (1 << (weightParams.GetPackedBitSize() % 8)) - 1;
1566 memset(texelWeightData + clearByteStart, 0, 16 - clearByteStart);
1567
1568 std::vector<IntegerEncodedValue> texelWeightValues;
1569 BitStream weightStream(texelWeightData);
1570
1571 IntegerEncodedValue::DecodeIntegerSequence(texelWeightValues, weightStream,
1572 weightParams.m_MaxWeight,
1573 weightParams.GetNumWeightValues());
1574
1575 // Blocks can be at most 12x12, so we can have as many as 144 weights
1576 uint32_t weights[2][144];
1577 UnquantizeTexelWeights(weights, texelWeightValues, weightParams, blockWidth, blockHeight);
1578
1579 // Now that we have endpoints and weights, we can interpolate and generate
1580 // the proper decoding...
1581 for (uint32_t j = 0; j < blockHeight; j++)
1582 for (uint32_t i = 0; i < blockWidth; i++) {
1583 uint32_t partition = Select2DPartition(partitionIndex, i, j, nPartitions,
1584 (blockHeight * blockWidth) < 32);
1585 assert(partition < nPartitions);
1586
1587 Pixel p;
1588 for (uint32_t c = 0; c < 4; c++) {
1589 uint32_t C0 = endpoints[partition][0].Component(c);
1590 C0 = Replicate(C0, 8, 16);
1591 uint32_t C1 = endpoints[partition][1].Component(c);
1592 C1 = Replicate(C1, 8, 16);
1593
1594 uint32_t plane = 0;
1595 if (weightParams.m_bDualPlane && (((planeIdx + 1) & 3) == c)) {
1596 plane = 1;
1597 }
1598
1599 uint32_t weight = weights[plane][j * blockWidth + i];
1600 uint32_t C = (C0 * (64 - weight) + C1 * weight + 32) / 64;
1601 if (C == 65535) {
1602 p.Component(c) = 255;
1603 } else {
1604 double Cf = static_cast<double>(C);
1605 p.Component(c) = static_cast<uint16_t>(255.0 * (Cf / 65536.0) + 0.5);
1606 }
1607 }
1608
1609 outBuf[j * blockWidth + i] = p.Pack();
1610 }
1611}
1612
1613} // namespace ASTCC
1614
1615namespace Tegra::Texture::ASTC {
1616
1617std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
1618 uint32_t block_width, uint32_t block_height) {
1619 uint32_t blockIdx = 0;
1620 std::vector<uint8_t> outData;
1621 outData.resize(height * width * 4);
1622 for (uint32_t j = 0; j < height; j += block_height) {
1623 for (uint32_t i = 0; i < width; i += block_width) {
1624
1625 uint8_t* blockPtr = data.data() + blockIdx * 16;
1626
1627 // Blocks can be at most 12x12
1628 uint32_t uncompData[144];
1629 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
1630
1631 uint32_t decompWidth = std::min(block_width, width - i);
1632 uint32_t decompHeight = std::min(block_height, height - j);
1633
1634 uint8_t* outRow = outData.data() + (j * width + i) * 4;
1635 for (uint32_t jj = 0; jj < decompHeight; jj++) {
1636 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
1637 }
1638
1639 blockIdx++;
1640 }
1641 }
1642
1643 return outData;
1644}
1645
1646} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
new file mode 100644
index 000000000..f0d7c0e56
--- /dev/null
+++ b/src/video_core/textures/astc.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstdint>
8#include <vector>
9
10namespace Tegra::Texture::ASTC {
11
12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
13 uint32_t block_width, uint32_t block_height);
14
15} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 7bf9c4c4b..b3937b2fe 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/memory.h" 7#include "core/memory.h"
8#include "video_core/gpu.h"
8#include "video_core/textures/decoders.h" 9#include "video_core/textures/decoders.h"
9#include "video_core/textures/texture.h" 10#include "video_core/textures/texture.h"
10 11
@@ -51,8 +52,10 @@ u32 BytesPerPixel(TextureFormat format) {
51 return 8; 52 return 8;
52 case TextureFormat::DXT23: 53 case TextureFormat::DXT23:
53 case TextureFormat::DXT45: 54 case TextureFormat::DXT45:
55 case TextureFormat::BC7U:
54 // In this case a 'pixel' actually refers to a 4x4 tile. 56 // In this case a 'pixel' actually refers to a 4x4 tile.
55 return 16; 57 return 16;
58 case TextureFormat::ASTC_2D_4X4:
56 case TextureFormat::A8R8G8B8: 59 case TextureFormat::A8R8G8B8:
57 case TextureFormat::A2B10G10R10: 60 case TextureFormat::A2B10G10R10:
58 case TextureFormat::BF10GF11RF11: 61 case TextureFormat::BF10GF11RF11:
@@ -64,6 +67,20 @@ u32 BytesPerPixel(TextureFormat format) {
64 return 1; 67 return 1;
65 case TextureFormat::R16_G16_B16_A16: 68 case TextureFormat::R16_G16_B16_A16:
66 return 8; 69 return 8;
70 case TextureFormat::R32_G32_B32_A32:
71 return 16;
72 default:
73 UNIMPLEMENTED_MSG("Format not implemented");
74 break;
75 }
76}
77
78static u32 DepthBytesPerPixel(DepthFormat format) {
79 switch (format) {
80 case DepthFormat::S8_Z24_UNORM:
81 case DepthFormat::Z24_S8_UNORM:
82 case DepthFormat::Z32_FLOAT:
83 return 4;
67 default: 84 default:
68 UNIMPLEMENTED_MSG("Format not implemented"); 85 UNIMPLEMENTED_MSG("Format not implemented");
69 break; 86 break;
@@ -82,6 +99,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
82 case TextureFormat::DXT23: 99 case TextureFormat::DXT23:
83 case TextureFormat::DXT45: 100 case TextureFormat::DXT45:
84 case TextureFormat::DXN1: 101 case TextureFormat::DXN1:
102 case TextureFormat::BC7U:
85 // In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel 103 // In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
86 // values. 104 // values.
87 CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, 105 CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
@@ -93,7 +111,31 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
93 case TextureFormat::B5G6R5: 111 case TextureFormat::B5G6R5:
94 case TextureFormat::R8: 112 case TextureFormat::R8:
95 case TextureFormat::R16_G16_B16_A16: 113 case TextureFormat::R16_G16_B16_A16:
114 case TextureFormat::R32_G32_B32_A32:
96 case TextureFormat::BF10GF11RF11: 115 case TextureFormat::BF10GF11RF11:
116 case TextureFormat::ASTC_2D_4X4:
117 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
118 unswizzled_data.data(), true, block_height);
119 break;
120 default:
121 UNIMPLEMENTED_MSG("Format not implemented");
122 break;
123 }
124
125 return unswizzled_data;
126}
127
128std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
129 u32 block_height) {
130 u8* data = Memory::GetPointer(address);
131 u32 bytes_per_pixel = DepthBytesPerPixel(format);
132
133 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
134
135 switch (format) {
136 case DepthFormat::S8_Z24_UNORM:
137 case DepthFormat::Z24_S8_UNORM:
138 case DepthFormat::Z32_FLOAT:
97 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, 139 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
98 unswizzled_data.data(), true, block_height); 140 unswizzled_data.data(), true, block_height);
99 break; 141 break;
@@ -115,12 +157,15 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
115 case TextureFormat::DXT23: 157 case TextureFormat::DXT23:
116 case TextureFormat::DXT45: 158 case TextureFormat::DXT45:
117 case TextureFormat::DXN1: 159 case TextureFormat::DXN1:
160 case TextureFormat::BC7U:
161 case TextureFormat::ASTC_2D_4X4:
118 case TextureFormat::A8R8G8B8: 162 case TextureFormat::A8R8G8B8:
119 case TextureFormat::A2B10G10R10: 163 case TextureFormat::A2B10G10R10:
120 case TextureFormat::A1B5G5R5: 164 case TextureFormat::A1B5G5R5:
121 case TextureFormat::B5G6R5: 165 case TextureFormat::B5G6R5:
122 case TextureFormat::R8: 166 case TextureFormat::R8:
123 case TextureFormat::BF10GF11RF11: 167 case TextureFormat::BF10GF11RF11:
168 case TextureFormat::R32_G32_B32_A32:
124 // TODO(Subv): For the time being just forward the same data without any decoding. 169 // TODO(Subv): For the time being just forward the same data without any decoding.
125 rgba_data = texture_data; 170 rgba_data = texture_data;
126 break; 171 break;
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 2562c4b06..2b088c077 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -17,6 +17,12 @@ namespace Texture {
17std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height, 17std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
18 u32 block_height = TICEntry::DefaultBlockHeight); 18 u32 block_height = TICEntry::DefaultBlockHeight);
19 19
20/**
21 * Unswizzles a swizzled depth texture without changing its format.
22 */
23std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
24 u32 block_height = TICEntry::DefaultBlockHeight);
25
20/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary. 26/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary.
21void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 27void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
22 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height); 28 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height);
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 89dc8ed1e..289140f31 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -24,9 +24,9 @@ bool Init(EmuWindow* emu_window) {
24 g_renderer = std::make_unique<RendererOpenGL>(); 24 g_renderer = std::make_unique<RendererOpenGL>();
25 g_renderer->SetWindow(g_emu_window); 25 g_renderer->SetWindow(g_emu_window);
26 if (g_renderer->Init()) { 26 if (g_renderer->Init()) {
27 NGLOG_DEBUG(Render, "initialized OK"); 27 LOG_DEBUG(Render, "initialized OK");
28 } else { 28 } else {
29 NGLOG_CRITICAL(Render, "initialization failed !"); 29 LOG_CRITICAL(Render, "initialization failed !");
30 return false; 30 return false;
31 } 31 }
32 return true; 32 return true;
@@ -36,7 +36,7 @@ bool Init(EmuWindow* emu_window) {
36void Shutdown() { 36void Shutdown() {
37 g_renderer.reset(); 37 g_renderer.reset();
38 38
39 NGLOG_DEBUG(Render, "shutdown OK"); 39 LOG_DEBUG(Render, "shutdown OK");
40} 40}
41 41
42} // namespace VideoCore 42} // namespace VideoCore
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 5af3154d7..7de919a8e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,10 +30,10 @@ add_executable(yuzu
30 debugger/graphics/graphics_breakpoints_p.h 30 debugger/graphics/graphics_breakpoints_p.h
31 debugger/graphics/graphics_surface.cpp 31 debugger/graphics/graphics_surface.cpp
32 debugger/graphics/graphics_surface.h 32 debugger/graphics/graphics_surface.h
33 debugger/console.cpp
34 debugger/console.h
33 debugger/profiler.cpp 35 debugger/profiler.cpp
34 debugger/profiler.h 36 debugger/profiler.h
35 debugger/registers.cpp
36 debugger/registers.h
37 debugger/wait_tree.cpp 37 debugger/wait_tree.cpp
38 debugger/wait_tree.h 38 debugger/wait_tree.h
39 game_list.cpp 39 game_list.cpp
@@ -60,7 +60,6 @@ set(UIS
60 configuration/configure_graphics.ui 60 configuration/configure_graphics.ui
61 configuration/configure_input.ui 61 configuration/configure_input.ui
62 configuration/configure_system.ui 62 configuration/configure_system.ui
63 debugger/registers.ui
64 hotkeys.ui 63 hotkeys.ui
65 main.ui 64 main.ui
66) 65)
@@ -84,6 +83,14 @@ if (APPLE)
84 target_sources(yuzu PRIVATE ${MACOSX_ICON}) 83 target_sources(yuzu PRIVATE ${MACOSX_ICON})
85 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE) 84 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
86 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) 85 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
86elseif(WIN32)
87 # compile as a win32 gui application instead of a console application
88 target_link_libraries(yuzu PRIVATE Qt5::WinMain)
89 if(MSVC)
90 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
91 elseif(MINGW)
92 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
93 endif()
87endif() 94endif()
88 95
89create_target_directory_groups(yuzu) 96create_target_directory_groups(yuzu)
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 5c17cd0d9..833085559 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -127,13 +127,14 @@ void GRenderWindow::moveContext() {
127} 127}
128 128
129void GRenderWindow::SwapBuffers() { 129void GRenderWindow::SwapBuffers() {
130#if !defined(QT_NO_DEBUG) 130 // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`,
131 // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent 131 // since we never call `doneCurrent` in this thread.
132 // since the last time you called swapBuffers. This presumably means something if you're using 132 // However:
133 // QGLWidget the "regular" way, but in our multi-threaded use case is harmless since we never 133 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
134 // call doneCurrent in this thread. 134 // since the last time `swapBuffers` was executed;
135 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
135 child->makeCurrent(); 136 child->makeCurrent();
136#endif 137
137 child->swapBuffers(); 138 child->swapBuffers();
138} 139}
139 140
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 8316db708..a32134fbe 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -84,6 +84,8 @@ void Config::ReadValues() {
84 qt_config->beginGroup("Renderer"); 84 qt_config->beginGroup("Renderer");
85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
86 Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); 86 Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool();
87 Settings::values.use_accurate_framebuffers =
88 qt_config->value("use_accurate_framebuffers", false).toBool();
87 89
88 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 90 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
89 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 91 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -158,6 +160,7 @@ void Config::ReadValues() {
158 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); 160 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
159 UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); 161 UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
160 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); 162 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
163 UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
161 164
162 qt_config->endGroup(); 165 qt_config->endGroup();
163} 166}
@@ -184,6 +187,7 @@ void Config::SaveValues() {
184 qt_config->beginGroup("Renderer"); 187 qt_config->beginGroup("Renderer");
185 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 188 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
186 qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit); 189 qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit);
190 qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers);
187 191
188 // Cast to double because Qt's written float values are not human-readable 192 // Cast to double because Qt's written float values are not human-readable
189 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 193 qt_config->setValue("bg_red", (double)Settings::values.bg_red);
@@ -243,7 +247,7 @@ void Config::SaveValues() {
243 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); 247 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
244 qt_config->setValue("firstStart", UISettings::values.first_start); 248 qt_config->setValue("firstStart", UISettings::values.first_start);
245 qt_config->setValue("calloutFlags", UISettings::values.callout_flags); 249 qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
246 250 qt_config->setValue("showConsole", UISettings::values.show_console);
247 qt_config->endGroup(); 251 qt_config->endGroup();
248} 252}
249 253
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index a45edd510..241db4ae3 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -2,13 +2,26 @@
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 <QDesktopServices>
6#include <QUrl>
7#include "common/file_util.h"
8#include "common/logging/backend.h"
9#include "common/logging/filter.h"
10#include "common/logging/log.h"
11#include "core/core.h"
5#include "core/settings.h" 12#include "core/settings.h"
6#include "ui_configure_debug.h" 13#include "ui_configure_debug.h"
7#include "yuzu/configuration/configure_debug.h" 14#include "yuzu/configuration/configure_debug.h"
15#include "yuzu/debugger/console.h"
16#include "yuzu/ui_settings.h"
8 17
9ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) { 18ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
10 ui->setupUi(this); 19 ui->setupUi(this);
11 this->setConfiguration(); 20 this->setConfiguration();
21 connect(ui->open_log_button, &QPushButton::pressed, []() {
22 QString path = QString::fromStdString(FileUtil::GetUserPath(D_LOGS_IDX));
23 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
24 });
12} 25}
13 26
14ConfigureDebug::~ConfigureDebug() {} 27ConfigureDebug::~ConfigureDebug() {}
@@ -17,10 +30,19 @@ void ConfigureDebug::setConfiguration() {
17 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); 30 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
18 ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub); 31 ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
19 ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port); 32 ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
33 ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
20} 36}
21 37
22void ConfigureDebug::applyConfiguration() { 38void ConfigureDebug::applyConfiguration() {
23 Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked(); 39 Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
24 Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); 40 Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
41 UISettings::values.show_console = ui->toggle_console->isChecked();
42 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
43 Debugger::ToggleConsole();
44 Log::Filter filter;
45 filter.ParseFilterString(Settings::values.log_filter);
46 Log::SetGlobalFilter(filter);
25 Settings::Apply(); 47 Settings::Apply();
26} 48}
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index a10bea2f4..118e91cf1 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -73,6 +73,47 @@
73 </layout> 73 </layout>
74 </item> 74 </item>
75 <item> 75 <item>
76 <widget class="QGroupBox" name="groupBox_2">
77 <property name="title">
78 <string>Logging</string>
79 </property>
80 <layout class="QVBoxLayout" name="verticalLayout">
81 <item>
82 <layout class="QHBoxLayout" name="horizontalLayout">
83 <item>
84 <widget class="QLabel" name="label">
85 <property name="text">
86 <string>Global Log Filter</string>
87 </property>
88 </widget>
89 </item>
90 <item>
91 <widget class="QLineEdit" name="log_filter_edit"/>
92 </item>
93 </layout>
94 </item>
95 <item>
96 <layout class="QHBoxLayout" name="horizontalLayout_2">
97 <item>
98 <widget class="QCheckBox" name="toggle_console">
99 <property name="text">
100 <string>Show Log Console (Windows Only)</string>
101 </property>
102 </widget>
103 </item>
104 <item>
105 <widget class="QPushButton" name="open_log_button">
106 <property name="text">
107 <string>Open Log Location</string>
108 </property>
109 </widget>
110 </item>
111 </layout>
112 </item>
113 </layout>
114 </widget>
115 </item>
116 <item>
76 <spacer name="verticalSpacer"> 117 <spacer name="verticalSpacer">
77 <property name="orientation"> 118 <property name="orientation">
78 <enum>Qt::Vertical</enum> 119 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 47b9b6e95..7664880d5 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -59,11 +59,13 @@ void ConfigureGraphics::setConfiguration() {
59 ui->resolution_factor_combobox->setCurrentIndex( 59 ui->resolution_factor_combobox->setCurrentIndex(
60 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 60 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
61 ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit); 61 ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit);
62 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers);
62} 63}
63 64
64void ConfigureGraphics::applyConfiguration() { 65void ConfigureGraphics::applyConfiguration() {
65 Settings::values.resolution_factor = 66 Settings::values.resolution_factor =
66 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 67 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
67 Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); 68 Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked();
69 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked();
68 Settings::Apply(); 70 Settings::Apply();
69} 71}
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 366931a9a..7d092df03 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -30,6 +30,13 @@
30 </widget> 30 </widget>
31 </item> 31 </item>
32 <item> 32 <item>
33 <widget class="QCheckBox" name="use_accurate_framebuffers">
34 <property name="text">
35 <string>Use accurate framebuffers (slow)</string>
36 </property>
37 </widget>
38 </item>
39 <item>
33 <layout class="QHBoxLayout" name="horizontalLayout"> 40 <layout class="QHBoxLayout" name="horizontalLayout">
34 <item> 41 <item>
35 <widget class="QLabel" name="label"> 42 <widget class="QLabel" name="label">
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp
new file mode 100644
index 000000000..e3d2d975f
--- /dev/null
+++ b/src/yuzu/debugger/console.cpp
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _WIN32
6#include <windows.h>
7
8#include <wincon.h>
9#endif
10
11#include "common/logging/backend.h"
12#include "yuzu/debugger/console.h"
13#include "yuzu/ui_settings.h"
14
15namespace Debugger {
16void ToggleConsole() {
17#if defined(_WIN32) && !defined(_DEBUG)
18 FILE* temp;
19 if (UISettings::values.show_console) {
20 if (AllocConsole()) {
21 // The first parameter for freopen_s is a out parameter, so we can just ignore it
22 freopen_s(&temp, "CONIN$", "r", stdin);
23 freopen_s(&temp, "CONOUT$", "w", stdout);
24 freopen_s(&temp, "CONOUT$", "w", stderr);
25 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
26 }
27 } else {
28 if (FreeConsole()) {
29 // In order to close the console, we have to also detach the streams on it.
30 // Just redirect them to NUL if there is no console window
31 Log::RemoveBackend(Log::ColorConsoleBackend::Name());
32 freopen_s(&temp, "NUL", "r", stdin);
33 freopen_s(&temp, "NUL", "w", stdout);
34 freopen_s(&temp, "NUL", "w", stderr);
35 }
36 }
37#else
38 if (UISettings::values.show_console) {
39 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
40 } else {
41 Log::RemoveBackend(Log::ColorConsoleBackend::Name());
42 }
43#endif
44}
45} // namespace Debugger
diff --git a/src/yuzu/debugger/console.h b/src/yuzu/debugger/console.h
new file mode 100644
index 000000000..d1990c496
--- /dev/null
+++ b/src/yuzu/debugger/console.h
@@ -0,0 +1,14 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Debugger {
8
9/**
10 * Uses the WINAPI to hide or show the stderr console. This function is a placeholder until we can
11 * get a real qt logging window which would work for all platforms.
12 */
13void ToggleConsole();
14} // namespace Debugger \ No newline at end of file
diff --git a/src/yuzu/debugger/registers.cpp b/src/yuzu/debugger/registers.cpp
deleted file mode 100644
index 178cc65a7..000000000
--- a/src/yuzu/debugger/registers.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QTreeWidgetItem>
6#include "core/arm/arm_interface.h"
7#include "core/core.h"
8#include "yuzu/debugger/registers.h"
9#include "yuzu/util/util.h"
10
11RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
12 cpu_regs_ui.setupUi(this);
13
14 tree = cpu_regs_ui.treeWidget;
15 tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers"))));
16 tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers"))));
17 tree->addTopLevelItem(vfp_system_registers =
18 new QTreeWidgetItem(QStringList(tr("VFP System Registers"))));
19 tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR")));
20
21 for (int i = 0; i < 16; ++i) {
22 QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i)));
23 core_registers->addChild(child);
24 }
25
26 for (int i = 0; i < 32; ++i) {
27 QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("S[%1]").arg(i)));
28 vfp_registers->addChild(child);
29 }
30
31 QFont font = GetMonospaceFont();
32
33 CreateCPSRChildren();
34 CreateVFPSystemRegisterChildren();
35
36 // Set Registers to display in monospace font
37 for (int i = 0; i < core_registers->childCount(); ++i)
38 core_registers->child(i)->setFont(1, font);
39
40 for (int i = 0; i < vfp_registers->childCount(); ++i)
41 vfp_registers->child(i)->setFont(1, font);
42
43 for (int i = 0; i < vfp_system_registers->childCount(); ++i) {
44 vfp_system_registers->child(i)->setFont(1, font);
45 for (int x = 0; x < vfp_system_registers->child(i)->childCount(); ++x) {
46 vfp_system_registers->child(i)->child(x)->setFont(1, font);
47 }
48 }
49 // Set CSPR to display in monospace font
50 cpsr->setFont(1, font);
51 for (int i = 0; i < cpsr->childCount(); ++i) {
52 cpsr->child(i)->setFont(1, font);
53 for (int x = 0; x < cpsr->child(i)->childCount(); ++x) {
54 cpsr->child(i)->child(x)->setFont(1, font);
55 }
56 }
57 setEnabled(false);
58}
59
60void RegistersWidget::OnDebugModeEntered() {
61 if (!Core::System::GetInstance().IsPoweredOn())
62 return;
63
64 for (int i = 0; i < core_registers->childCount(); ++i)
65 core_registers->child(i)->setText(
66 1, QString("0x%1").arg(Core::CurrentArmInterface().GetReg(i), 8, 16, QLatin1Char('0')));
67
68 UpdateCPSRValues();
69}
70
71void RegistersWidget::OnDebugModeLeft() {}
72
73void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
74 setEnabled(true);
75}
76
77void RegistersWidget::OnEmulationStopping() {
78 // Reset widget text
79 for (int i = 0; i < core_registers->childCount(); ++i)
80 core_registers->child(i)->setText(1, QString(""));
81
82 for (int i = 0; i < vfp_registers->childCount(); ++i)
83 vfp_registers->child(i)->setText(1, QString(""));
84
85 for (int i = 0; i < cpsr->childCount(); ++i)
86 cpsr->child(i)->setText(1, QString(""));
87
88 cpsr->setText(1, QString(""));
89
90 // FPSCR
91 for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i)
92 vfp_system_registers->child(0)->child(i)->setText(1, QString(""));
93
94 // FPEXC
95 for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i)
96 vfp_system_registers->child(1)->child(i)->setText(1, QString(""));
97
98 vfp_system_registers->child(0)->setText(1, QString(""));
99 vfp_system_registers->child(1)->setText(1, QString(""));
100 vfp_system_registers->child(2)->setText(1, QString(""));
101 vfp_system_registers->child(3)->setText(1, QString(""));
102
103 setEnabled(false);
104}
105
106void RegistersWidget::CreateCPSRChildren() {
107 cpsr->addChild(new QTreeWidgetItem(QStringList("M")));
108 cpsr->addChild(new QTreeWidgetItem(QStringList("T")));
109 cpsr->addChild(new QTreeWidgetItem(QStringList("F")));
110 cpsr->addChild(new QTreeWidgetItem(QStringList("I")));
111 cpsr->addChild(new QTreeWidgetItem(QStringList("A")));
112 cpsr->addChild(new QTreeWidgetItem(QStringList("E")));
113 cpsr->addChild(new QTreeWidgetItem(QStringList("IT")));
114 cpsr->addChild(new QTreeWidgetItem(QStringList("GE")));
115 cpsr->addChild(new QTreeWidgetItem(QStringList("DNM")));
116 cpsr->addChild(new QTreeWidgetItem(QStringList("J")));
117 cpsr->addChild(new QTreeWidgetItem(QStringList("Q")));
118 cpsr->addChild(new QTreeWidgetItem(QStringList("V")));
119 cpsr->addChild(new QTreeWidgetItem(QStringList("C")));
120 cpsr->addChild(new QTreeWidgetItem(QStringList("Z")));
121 cpsr->addChild(new QTreeWidgetItem(QStringList("N")));
122}
123
124void RegistersWidget::UpdateCPSRValues() {
125 const u32 cpsr_val = Core::CurrentArmInterface().GetCPSR();
126
127 cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
128 cpsr->child(0)->setText(
129 1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
130 cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State
131 cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable
132 cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable
133 cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort
134 cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianness
135 cpsr->child(6)->setText(1,
136 QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
137 cpsr->child(7)->setText(1,
138 QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal
139 cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify
140 cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle
141 cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation
142 cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow
143 cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend
144 cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero
145 cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than
146}
147
148void RegistersWidget::CreateVFPSystemRegisterChildren() {
149 QTreeWidgetItem* const fpscr = new QTreeWidgetItem(QStringList("FPSCR"));
150 fpscr->addChild(new QTreeWidgetItem(QStringList("IOC")));
151 fpscr->addChild(new QTreeWidgetItem(QStringList("DZC")));
152 fpscr->addChild(new QTreeWidgetItem(QStringList("OFC")));
153 fpscr->addChild(new QTreeWidgetItem(QStringList("UFC")));
154 fpscr->addChild(new QTreeWidgetItem(QStringList("IXC")));
155 fpscr->addChild(new QTreeWidgetItem(QStringList("IDC")));
156 fpscr->addChild(new QTreeWidgetItem(QStringList("IOE")));
157 fpscr->addChild(new QTreeWidgetItem(QStringList("DZE")));
158 fpscr->addChild(new QTreeWidgetItem(QStringList("OFE")));
159 fpscr->addChild(new QTreeWidgetItem(QStringList("UFE")));
160 fpscr->addChild(new QTreeWidgetItem(QStringList("IXE")));
161 fpscr->addChild(new QTreeWidgetItem(QStringList("IDE")));
162 fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Length"))));
163 fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Stride"))));
164 fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Rounding Mode"))));
165 fpscr->addChild(new QTreeWidgetItem(QStringList("FZ")));
166 fpscr->addChild(new QTreeWidgetItem(QStringList("DN")));
167 fpscr->addChild(new QTreeWidgetItem(QStringList("V")));
168 fpscr->addChild(new QTreeWidgetItem(QStringList("C")));
169 fpscr->addChild(new QTreeWidgetItem(QStringList("Z")));
170 fpscr->addChild(new QTreeWidgetItem(QStringList("N")));
171
172 QTreeWidgetItem* const fpexc = new QTreeWidgetItem(QStringList("FPEXC"));
173 fpexc->addChild(new QTreeWidgetItem(QStringList("IOC")));
174 fpexc->addChild(new QTreeWidgetItem(QStringList("OFC")));
175 fpexc->addChild(new QTreeWidgetItem(QStringList("UFC")));
176 fpexc->addChild(new QTreeWidgetItem(QStringList("INV")));
177 fpexc->addChild(new QTreeWidgetItem(QStringList(tr("Vector Iteration Count"))));
178 fpexc->addChild(new QTreeWidgetItem(QStringList("FP2V")));
179 fpexc->addChild(new QTreeWidgetItem(QStringList("EN")));
180 fpexc->addChild(new QTreeWidgetItem(QStringList("EX")));
181
182 vfp_system_registers->addChild(fpscr);
183 vfp_system_registers->addChild(fpexc);
184 vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST")));
185 vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST2")));
186}
187
188void RegistersWidget::UpdateVFPSystemRegisterValues() {
189 UNIMPLEMENTED();
190}
diff --git a/src/yuzu/debugger/registers.h b/src/yuzu/debugger/registers.h
deleted file mode 100644
index 55bda5b59..000000000
--- a/src/yuzu/debugger/registers.h
+++ /dev/null
@@ -1,42 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDockWidget>
8#include "ui_registers.h"
9
10class QTreeWidget;
11class QTreeWidgetItem;
12class EmuThread;
13
14class RegistersWidget : public QDockWidget {
15 Q_OBJECT
16
17public:
18 explicit RegistersWidget(QWidget* parent = nullptr);
19
20public slots:
21 void OnDebugModeEntered();
22 void OnDebugModeLeft();
23
24 void OnEmulationStarting(EmuThread* emu_thread);
25 void OnEmulationStopping();
26
27private:
28 void CreateCPSRChildren();
29 void UpdateCPSRValues();
30
31 void CreateVFPSystemRegisterChildren();
32 void UpdateVFPSystemRegisterValues();
33
34 Ui::ARMRegisters cpu_regs_ui;
35
36 QTreeWidget* tree;
37
38 QTreeWidgetItem* core_registers;
39 QTreeWidgetItem* vfp_registers;
40 QTreeWidgetItem* vfp_system_registers;
41 QTreeWidgetItem* cpsr;
42};
diff --git a/src/yuzu/debugger/registers.ui b/src/yuzu/debugger/registers.ui
deleted file mode 100644
index c81ae03f9..000000000
--- a/src/yuzu/debugger/registers.ui
+++ /dev/null
@@ -1,40 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ARMRegisters</class>
4 <widget class="QDockWidget" name="ARMRegisters">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>300</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ARM Registers</string>
15 </property>
16 <widget class="QWidget" name="dockWidgetContents">
17 <layout class="QVBoxLayout" name="verticalLayout">
18 <item>
19 <widget class="QTreeWidget" name="treeWidget">
20 <property name="alternatingRowColors">
21 <bool>true</bool>
22 </property>
23 <column>
24 <property name="text">
25 <string>Register</string>
26 </property>
27 </column>
28 <column>
29 <property name="text">
30 <string>Value</string>
31 </property>
32 </column>
33 </widget>
34 </item>
35 </layout>
36 </widget>
37 </widget>
38 <resources/>
39 <connections/>
40</ui>
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 017bef13c..7101b381e 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -213,6 +213,9 @@ QString WaitTreeThread::GetText() const {
213 case THREADSTATUS_WAIT_MUTEX: 213 case THREADSTATUS_WAIT_MUTEX:
214 status = tr("waiting for mutex"); 214 status = tr("waiting for mutex");
215 break; 215 break;
216 case THREADSTATUS_WAIT_ARB:
217 status = tr("waiting for address arbiter");
218 break;
216 case THREADSTATUS_DORMANT: 219 case THREADSTATUS_DORMANT:
217 status = tr("dormant"); 220 status = tr("dormant");
218 break; 221 break;
@@ -240,6 +243,7 @@ QColor WaitTreeThread::GetColor() const {
240 case THREADSTATUS_WAIT_SYNCH_ALL: 243 case THREADSTATUS_WAIT_SYNCH_ALL:
241 case THREADSTATUS_WAIT_SYNCH_ANY: 244 case THREADSTATUS_WAIT_SYNCH_ANY:
242 case THREADSTATUS_WAIT_MUTEX: 245 case THREADSTATUS_WAIT_MUTEX:
246 case THREADSTATUS_WAIT_ARB:
243 return QColor(Qt::GlobalColor::red); 247 return QColor(Qt::GlobalColor::red);
244 case THREADSTATUS_DORMANT: 248 case THREADSTATUS_DORMANT:
245 return QColor(Qt::GlobalColor::darkCyan); 249 return QColor(Qt::GlobalColor::darkCyan);
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index bbd681eae..5a708dc73 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QApplication> 5#include <QApplication>
6#include <QDir>
6#include <QFileInfo> 7#include <QFileInfo>
7#include <QHeaderView> 8#include <QHeaderView>
8#include <QKeyEvent> 9#include <QKeyEvent>
@@ -264,8 +265,17 @@ void GameList::ValidateEntry(const QModelIndex& item) {
264 if (file_path.isEmpty()) 265 if (file_path.isEmpty())
265 return; 266 return;
266 std::string std_file_path(file_path.toStdString()); 267 std::string std_file_path(file_path.toStdString());
267 if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path)) 268 if (!FileUtil::Exists(std_file_path))
268 return; 269 return;
270 if (FileUtil::IsDirectory(std_file_path)) {
271 QDir dir(std_file_path.c_str());
272 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
273 if (matching_main.size() == 1) {
274 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
275 }
276 return;
277 }
278
269 // Users usually want to run a diffrent game after closing one 279 // Users usually want to run a diffrent game after closing one
270 search_field->clear(); 280 search_field->clear();
271 emit GameChosen(file_path); 281 emit GameChosen(file_path);
@@ -315,8 +325,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
315void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { 325void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
316 if (!FileUtil::Exists(dir_path.toStdString()) || 326 if (!FileUtil::Exists(dir_path.toStdString()) ||
317 !FileUtil::IsDirectory(dir_path.toStdString())) { 327 !FileUtil::IsDirectory(dir_path.toStdString())) {
318 NGLOG_ERROR(Frontend, "Could not find game list folder at {}", 328 LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data());
319 dir_path.toLocal8Bit().data());
320 search_field->setFilterResult(0, 0); 329 search_field->setFilterResult(0, 0);
321 return; 330 return;
322 } 331 }
@@ -356,16 +365,29 @@ void GameList::LoadInterfaceLayout() {
356 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); 365 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
357} 366}
358 367
359const QStringList GameList::supported_file_extensions = {"nso", "nro"}; 368const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca"};
360 369
361static bool HasSupportedFileExtension(const std::string& file_name) { 370static bool HasSupportedFileExtension(const std::string& file_name) {
362 QFileInfo file = QFileInfo(file_name.c_str()); 371 QFileInfo file = QFileInfo(file_name.c_str());
363 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); 372 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
364} 373}
365 374
375static bool IsExtractedNCAMain(const std::string& file_name) {
376 return QFileInfo(file_name.c_str()).fileName() == "main";
377}
378
379static QString FormatGameName(const std::string& physical_name) {
380 QFileInfo file_info(physical_name.c_str());
381 if (IsExtractedNCAMain(physical_name)) {
382 return file_info.dir().path();
383 } else {
384 return QString::fromStdString(physical_name);
385 }
386}
387
366void GameList::RefreshGameDirectory() { 388void GameList::RefreshGameDirectory() {
367 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { 389 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
368 NGLOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); 390 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
369 search_field->clear(); 391 search_field->clear();
370 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 392 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
371 } 393 }
@@ -380,7 +402,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
380 return false; // Breaks the callback loop. 402 return false; // Breaks the callback loop.
381 403
382 bool is_dir = FileUtil::IsDirectory(physical_name); 404 bool is_dir = FileUtil::IsDirectory(physical_name);
383 if (!is_dir && HasSupportedFileExtension(physical_name)) { 405 if (!is_dir &&
406 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
384 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); 407 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
385 if (!loader) 408 if (!loader)
386 return true; 409 return true;
@@ -392,7 +415,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
392 loader->ReadProgramId(program_id); 415 loader->ReadProgramId(program_id);
393 416
394 emit EntryReady({ 417 emit EntryReady({
395 new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id), 418 new GameListItemPath(FormatGameName(physical_name), smdh, program_id),
396 new GameListItem( 419 new GameListItem(
397 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 420 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
398 new GameListItemSize(FileUtil::GetSize(physical_name)), 421 new GameListItemSize(FileUtil::GetSize(physical_name)),
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 3038bd6da..05a8ae6d2 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -13,6 +13,7 @@
13#include <QMessageBox> 13#include <QMessageBox>
14#include <QtGui> 14#include <QtGui>
15#include <QtWidgets> 15#include <QtWidgets>
16#include "common/common_paths.h"
16#include "common/logging/backend.h" 17#include "common/logging/backend.h"
17#include "common/logging/filter.h" 18#include "common/logging/filter.h"
18#include "common/logging/log.h" 19#include "common/logging/log.h"
@@ -30,10 +31,10 @@
30#include "yuzu/bootmanager.h" 31#include "yuzu/bootmanager.h"
31#include "yuzu/configuration/config.h" 32#include "yuzu/configuration/config.h"
32#include "yuzu/configuration/configure_dialog.h" 33#include "yuzu/configuration/configure_dialog.h"
34#include "yuzu/debugger/console.h"
33#include "yuzu/debugger/graphics/graphics_breakpoints.h" 35#include "yuzu/debugger/graphics/graphics_breakpoints.h"
34#include "yuzu/debugger/graphics/graphics_surface.h" 36#include "yuzu/debugger/graphics/graphics_surface.h"
35#include "yuzu/debugger/profiler.h" 37#include "yuzu/debugger/profiler.h"
36#include "yuzu/debugger/registers.h"
37#include "yuzu/debugger/wait_tree.h" 38#include "yuzu/debugger/wait_tree.h"
38#include "yuzu/game_list.h" 39#include "yuzu/game_list.h"
39#include "yuzu/hotkeys.h" 40#include "yuzu/hotkeys.h"
@@ -169,15 +170,6 @@ void GMainWindow::InitializeDebugWidgets() {
169 debug_menu->addAction(microProfileDialog->toggleViewAction()); 170 debug_menu->addAction(microProfileDialog->toggleViewAction());
170#endif 171#endif
171 172
172 registersWidget = new RegistersWidget(this);
173 addDockWidget(Qt::RightDockWidgetArea, registersWidget);
174 registersWidget->hide();
175 debug_menu->addAction(registersWidget->toggleViewAction());
176 connect(this, &GMainWindow::EmulationStarting, registersWidget,
177 &RegistersWidget::OnEmulationStarting);
178 connect(this, &GMainWindow::EmulationStopping, registersWidget,
179 &RegistersWidget::OnEmulationStopping);
180
181 graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this); 173 graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this);
182 addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); 174 addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
183 graphicsBreakpointsWidget->hide(); 175 graphicsBreakpointsWidget->hide();
@@ -270,6 +262,7 @@ void GMainWindow::RestoreUIState() {
270 262
271 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); 263 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
272 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); 264 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
265 Debugger::ToggleConsole();
273} 266}
274 267
275void GMainWindow::ConnectWidgetEvents() { 268void GMainWindow::ConnectWidgetEvents() {
@@ -288,6 +281,7 @@ void GMainWindow::ConnectWidgetEvents() {
288void GMainWindow::ConnectMenuEvents() { 281void GMainWindow::ConnectMenuEvents() {
289 // File 282 // File
290 connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); 283 connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
284 connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder);
291 connect(ui.action_Select_Game_List_Root, &QAction::triggered, this, 285 connect(ui.action_Select_Game_List_Root, &QAction::triggered, this,
292 &GMainWindow::OnMenuSelectGameListRoot); 286 &GMainWindow::OnMenuSelectGameListRoot);
293 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 287 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
@@ -342,13 +336,11 @@ bool GMainWindow::SupportsRequiredGLExtensions() {
342 unsupported_ext.append("ARB_program_interface_query"); 336 unsupported_ext.append("ARB_program_interface_query");
343 if (!GLAD_GL_ARB_separate_shader_objects) 337 if (!GLAD_GL_ARB_separate_shader_objects)
344 unsupported_ext.append("ARB_separate_shader_objects"); 338 unsupported_ext.append("ARB_separate_shader_objects");
345 if (!GLAD_GL_ARB_shader_storage_buffer_object)
346 unsupported_ext.append("ARB_shader_storage_buffer_object");
347 if (!GLAD_GL_ARB_vertex_attrib_binding) 339 if (!GLAD_GL_ARB_vertex_attrib_binding)
348 unsupported_ext.append("ARB_vertex_attrib_binding"); 340 unsupported_ext.append("ARB_vertex_attrib_binding");
349 341
350 for (const QString& ext : unsupported_ext) 342 for (const QString& ext : unsupported_ext)
351 NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); 343 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
352 344
353 return unsupported_ext.empty(); 345 return unsupported_ext.empty();
354} 346}
@@ -385,17 +377,17 @@ bool GMainWindow::LoadROM(const QString& filename) {
385 if (result != Core::System::ResultStatus::Success) { 377 if (result != Core::System::ResultStatus::Success) {
386 switch (result) { 378 switch (result) {
387 case Core::System::ResultStatus::ErrorGetLoader: 379 case Core::System::ResultStatus::ErrorGetLoader:
388 NGLOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filename.toStdString()); 380 LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filename.toStdString());
389 QMessageBox::critical(this, tr("Error while loading ROM!"), 381 QMessageBox::critical(this, tr("Error while loading ROM!"),
390 tr("The ROM format is not supported.")); 382 tr("The ROM format is not supported."));
391 break; 383 break;
392 case Core::System::ResultStatus::ErrorUnsupportedArch: 384 case Core::System::ResultStatus::ErrorUnsupportedArch:
393 NGLOG_CRITICAL(Frontend, "Unsupported architecture detected!", filename.toStdString()); 385 LOG_CRITICAL(Frontend, "Unsupported architecture detected!", filename.toStdString());
394 QMessageBox::critical(this, tr("Error while loading ROM!"), 386 QMessageBox::critical(this, tr("Error while loading ROM!"),
395 tr("The ROM uses currently unusable 32-bit architecture")); 387 tr("The ROM uses currently unusable 32-bit architecture"));
396 break; 388 break;
397 case Core::System::ResultStatus::ErrorSystemMode: 389 case Core::System::ResultStatus::ErrorSystemMode:
398 NGLOG_CRITICAL(Frontend, "Failed to load ROM!"); 390 LOG_CRITICAL(Frontend, "Failed to load ROM!");
399 QMessageBox::critical(this, tr("Error while loading ROM!"), 391 QMessageBox::critical(this, tr("Error while loading ROM!"),
400 tr("Could not determine the system mode.")); 392 tr("Could not determine the system mode."));
401 break; 393 break;
@@ -445,7 +437,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
445} 437}
446 438
447void GMainWindow::BootGame(const QString& filename) { 439void GMainWindow::BootGame(const QString& filename) {
448 NGLOG_INFO(Frontend, "yuzu starting..."); 440 LOG_INFO(Frontend, "yuzu starting...");
449 StoreRecentFile(filename); // Put the filename on top of the list 441 StoreRecentFile(filename); // Put the filename on top of the list
450 442
451 if (!LoadROM(filename)) 443 if (!LoadROM(filename))
@@ -460,17 +452,12 @@ void GMainWindow::BootGame(const QString& filename) {
460 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 452 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
461 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views 453 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
462 // before the CPU continues 454 // before the CPU continues
463 connect(emu_thread.get(), &EmuThread::DebugModeEntered, registersWidget,
464 &RegistersWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
465 connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget, 455 connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget,
466 &WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection); 456 &WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
467 connect(emu_thread.get(), &EmuThread::DebugModeLeft, registersWidget,
468 &RegistersWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection);
469 connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, 457 connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget,
470 &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); 458 &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection);
471 459
472 // Update the GUI 460 // Update the GUI
473 registersWidget->OnDebugModeEntered();
474 if (ui.action_Single_Window_Mode->isChecked()) { 461 if (ui.action_Single_Window_Mode->isChecked()) {
475 game_list->hide(); 462 game_list->hide();
476 } 463 }
@@ -565,6 +552,8 @@ void GMainWindow::OnMenuLoadFile() {
565 for (const auto& piece : game_list->supported_file_extensions) 552 for (const auto& piece : game_list->supported_file_extensions)
566 extensions += "*." + piece + " "; 553 extensions += "*." + piece + " ";
567 554
555 extensions += "main ";
556
568 QString file_filter = tr("Switch Executable") + " (" + extensions + ")"; 557 QString file_filter = tr("Switch Executable") + " (" + extensions + ")";
569 file_filter += ";;" + tr("All Files (*.*)"); 558 file_filter += ";;" + tr("All Files (*.*)");
570 559
@@ -577,6 +566,18 @@ void GMainWindow::OnMenuLoadFile() {
577 } 566 }
578} 567}
579 568
569void GMainWindow::OnMenuLoadFolder() {
570 QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
571
572 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
573 if (matching_main.size() == 1) {
574 BootGame(dir.path() + DIR_SEP + matching_main[0]);
575 } else {
576 QMessageBox::warning(this, tr("Invalid Directory Selected"),
577 tr("The directory you have selected does not contain a 'main' file."));
578 }
579}
580
580void GMainWindow::OnMenuSelectGameListRoot() { 581void GMainWindow::OnMenuSelectGameListRoot() {
581 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 582 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
582 if (!dir_path.isEmpty()) { 583 if (!dir_path.isEmpty()) {
@@ -883,7 +884,7 @@ void GMainWindow::UpdateUITheme() {
883 QString theme_uri(":" + UISettings::values.theme + "/style.qss"); 884 QString theme_uri(":" + UISettings::values.theme + "/style.qss");
884 QFile f(theme_uri); 885 QFile f(theme_uri);
885 if (!f.exists()) { 886 if (!f.exists()) {
886 NGLOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); 887 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
887 } else { 888 } else {
888 f.open(QFile::ReadOnly | QFile::Text); 889 f.open(QFile::ReadOnly | QFile::Text);
889 QTextStream ts(&f); 890 QTextStream ts(&f);
@@ -907,8 +908,7 @@ void GMainWindow::UpdateUITheme() {
907#endif 908#endif
908 909
909int main(int argc, char* argv[]) { 910int main(int argc, char* argv[]) {
910 Log::Filter log_filter(Log::Level::Info); 911 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
911 Log::SetFilter(&log_filter);
912 912
913 MicroProfileOnThreadCreate("Frontend"); 913 MicroProfileOnThreadCreate("Frontend");
914 SCOPE_EXIT({ MicroProfileShutdown(); }); 914 SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -926,7 +926,12 @@ int main(int argc, char* argv[]) {
926 926
927 GMainWindow main_window; 927 GMainWindow main_window;
928 // After settings have been loaded by GMainWindow, apply the filter 928 // After settings have been loaded by GMainWindow, apply the filter
929 Log::Filter log_filter;
929 log_filter.ParseFilterString(Settings::values.log_filter); 930 log_filter.ParseFilterString(Settings::values.log_filter);
931 Log::SetGlobalFilter(log_filter);
932 FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
933 Log::AddBackend(
934 std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
930 935
931 main_window.show(); 936 main_window.show();
932 return app.exec(); 937 return app.exec();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index ac3024d8a..074bba3f9 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -19,7 +19,6 @@ class GraphicsSurfaceWidget;
19class GRenderWindow; 19class GRenderWindow;
20class MicroProfileDialog; 20class MicroProfileDialog;
21class ProfilerWidget; 21class ProfilerWidget;
22class RegistersWidget;
23class WaitTreeWidget; 22class WaitTreeWidget;
24 23
25namespace Tegra { 24namespace Tegra {
@@ -124,6 +123,7 @@ private slots:
124 void OnGameListLoadFile(QString game_path); 123 void OnGameListLoadFile(QString game_path);
125 void OnGameListOpenSaveFolder(u64 program_id); 124 void OnGameListOpenSaveFolder(u64 program_id);
126 void OnMenuLoadFile(); 125 void OnMenuLoadFile();
126 void OnMenuLoadFolder();
127 /// Called whenever a user selects the "File->Select Game List Root" menu item 127 /// Called whenever a user selects the "File->Select Game List Root" menu item
128 void OnMenuSelectGameListRoot(); 128 void OnMenuSelectGameListRoot();
129 void OnMenuRecentFile(); 129 void OnMenuRecentFile();
@@ -163,7 +163,6 @@ private:
163 // Debugger panes 163 // Debugger panes
164 ProfilerWidget* profilerWidget; 164 ProfilerWidget* profilerWidget;
165 MicroProfileDialog* microProfileDialog; 165 MicroProfileDialog* microProfileDialog;
166 RegistersWidget* registersWidget;
167 GraphicsBreakPointsWidget* graphicsBreakpointsWidget; 166 GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
168 GraphicsSurfaceWidget* graphicsSurfaceWidget; 167 GraphicsSurfaceWidget* graphicsSurfaceWidget;
169 WaitTreeWidget* waitTreeWidget; 168 WaitTreeWidget* waitTreeWidget;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 0fcd93cc2..22c4cad08 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -58,6 +58,7 @@
58 </property> 58 </property>
59 </widget> 59 </widget>
60 <addaction name="action_Load_File"/> 60 <addaction name="action_Load_File"/>
61 <addaction name="action_Load_Folder"/>
61 <addaction name="separator"/> 62 <addaction name="separator"/>
62 <addaction name="action_Select_Game_List_Root"/> 63 <addaction name="action_Select_Game_List_Root"/>
63 <addaction name="menu_recent_files"/> 64 <addaction name="menu_recent_files"/>
@@ -106,6 +107,11 @@
106 <string>Load File...</string> 107 <string>Load File...</string>
107 </property> 108 </property>
108 </action> 109 </action>
110 <action name="action_Load_Folder">
111 <property name="text">
112 <string>Load Folder...</string>
113 </property>
114 </action>
109 <action name="action_Load_Symbol_Map"> 115 <action name="action_Load_Symbol_Map">
110 <property name="text"> 116 <property name="text">
111 <string>Load Symbol Map...</string> 117 <string>Load Symbol Map...</string>
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 8e215a002..2286c2559 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -51,6 +51,9 @@ struct Values {
51 std::vector<Shortcut> shortcuts; 51 std::vector<Shortcut> shortcuts;
52 52
53 uint32_t callout_flags; 53 uint32_t callout_flags;
54
55 // logging
56 bool show_console;
54}; 57};
55 58
56extern Values values; 59extern Values values;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index ee6e4d658..3a311b69f 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -27,17 +27,17 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
27 const char* location = this->sdl2_config_loc.c_str(); 27 const char* location = this->sdl2_config_loc.c_str();
28 if (sdl2_config->ParseError() < 0) { 28 if (sdl2_config->ParseError() < 0) {
29 if (retry) { 29 if (retry) {
30 NGLOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); 30 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
31 FileUtil::CreateFullPath(location); 31 FileUtil::CreateFullPath(location);
32 FileUtil::WriteStringToFile(true, default_contents, location); 32 FileUtil::WriteStringToFile(true, default_contents, location);
33 sdl2_config = std::make_unique<INIReader>(location); // Reopen file 33 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
34 34
35 return LoadINI(default_contents, false); 35 return LoadINI(default_contents, false);
36 } 36 }
37 NGLOG_ERROR(Config, "Failed."); 37 LOG_ERROR(Config, "Failed.");
38 return false; 38 return false;
39 } 39 }
40 NGLOG_INFO(Config, "Successfully loaded {}", location); 40 LOG_INFO(Config, "Successfully loaded {}", location);
41 return true; 41 return true;
42} 42}
43 43
@@ -98,6 +98,8 @@ void Config::ReadValues() {
98 (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0); 98 (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
99 Settings::values.toggle_framelimit = 99 Settings::values.toggle_framelimit =
100 sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); 100 sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true);
101 Settings::values.use_accurate_framebuffers =
102 sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false);
101 103
102 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); 104 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
103 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 105 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 1c438c3f5..71d2e040f 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -102,6 +102,10 @@ resolution_factor =
102# 0 (default): Off, 1: On 102# 0 (default): Off, 1: On
103use_vsync = 103use_vsync =
104 104
105# Whether to use accurate framebuffers
106# 0 (default): Off (fast), 1 : On (slow)
107use_accurate_framebuffers =
108
105# The clear color for the renderer. What shows up on the sides of the bottom screen. 109# The clear color for the renderer. What shows up on the sides of the bottom screen.
106# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 110# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
107bg_red = 111bg_red =
@@ -162,7 +166,7 @@ use_virtual_sd =
162# 1 (default): Yes, 0: No 166# 1 (default): Yes, 0: No
163use_docked_mode = 167use_docked_mode =
164 168
165# The system region that Citra will use during emulation 169# The system region that yuzu will use during emulation
166# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan 170# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
167region_value = 171region_value =
168 172
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index cfd8eb7e6..e6f0bbe8f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -62,19 +62,19 @@ void EmuWindow_SDL2::Fullscreen() {
62 return; 62 return;
63 } 63 }
64 64
65 NGLOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); 65 LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
66 66
67 // Try a different fullscreening method 67 // Try a different fullscreening method
68 NGLOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); 68 LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
69 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { 69 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
70 return; 70 return;
71 } 71 }
72 72
73 NGLOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError()); 73 LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
74 74
75 // Fallback algorithm: Maximise window. 75 // Fallback algorithm: Maximise window.
76 // Works on all systems (unless something is seriously wrong), so no fallback for this one. 76 // Works on all systems (unless something is seriously wrong), so no fallback for this one.
77 NGLOG_INFO(Frontend, "Falling back on a maximised window..."); 77 LOG_INFO(Frontend, "Falling back on a maximised window...");
78 SDL_MaximizeWindow(render_window); 78 SDL_MaximizeWindow(render_window);
79} 79}
80 80
@@ -91,7 +91,7 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
91 unsupported_ext.push_back("ARB_vertex_attrib_binding"); 91 unsupported_ext.push_back("ARB_vertex_attrib_binding");
92 92
93 for (const std::string& ext : unsupported_ext) 93 for (const std::string& ext : unsupported_ext)
94 NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); 94 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
95 95
96 return unsupported_ext.empty(); 96 return unsupported_ext.empty();
97} 97}
@@ -103,7 +103,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
103 103
104 // Initialize the window 104 // Initialize the window
105 if (SDL_Init(SDL_INIT_VIDEO) < 0) { 105 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
106 NGLOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 106 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
107 exit(1); 107 exit(1);
108 } 108 }
109 109
@@ -126,7 +126,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
126 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); 126 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
127 127
128 if (render_window == nullptr) { 128 if (render_window == nullptr) {
129 NGLOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); 129 LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting...");
130 exit(1); 130 exit(1);
131 } 131 }
132 132
@@ -137,17 +137,17 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
137 gl_context = SDL_GL_CreateContext(render_window); 137 gl_context = SDL_GL_CreateContext(render_window);
138 138
139 if (gl_context == nullptr) { 139 if (gl_context == nullptr) {
140 NGLOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting..."); 140 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting...");
141 exit(1); 141 exit(1);
142 } 142 }
143 143
144 if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { 144 if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
145 NGLOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting..."); 145 LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting...");
146 exit(1); 146 exit(1);
147 } 147 }
148 148
149 if (!SupportsRequiredGLExtensions()) { 149 if (!SupportsRequiredGLExtensions()) {
150 NGLOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); 150 LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
151 exit(1); 151 exit(1);
152 } 152 }
153 153
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 95e568b7b..8ddd202d8 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -7,6 +7,7 @@
7#include <string> 7#include <string>
8#include <thread> 8#include <thread>
9 9
10#include "common/common_paths.h"
10#include "common/logging/backend.h" 11#include "common/logging/backend.h"
11#include "common/logging/filter.h" 12#include "common/logging/filter.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
@@ -69,7 +70,7 @@ int main(int argc, char** argv) {
69 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); 70 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
70 71
71 if (argv_w == nullptr) { 72 if (argv_w == nullptr) {
72 NGLOG_CRITICAL(Frontend, "Failed to get command line arguments"); 73 LOG_CRITICAL(Frontend, "Failed to get command line arguments");
73 return -1; 74 return -1;
74 } 75 }
75#endif 76#endif
@@ -102,7 +103,7 @@ int main(int argc, char** argv) {
102 break; 103 break;
103 case 'f': 104 case 'f':
104 fullscreen = true; 105 fullscreen = true;
105 NGLOG_INFO(Frontend, "Starting in fullscreen mode..."); 106 LOG_INFO(Frontend, "Starting in fullscreen mode...");
106 break; 107 break;
107 case 'h': 108 case 'h':
108 PrintHelp(argv[0]); 109 PrintHelp(argv[0]);
@@ -126,13 +127,18 @@ int main(int argc, char** argv) {
126#endif 127#endif
127 128
128 Log::Filter log_filter(Log::Level::Debug); 129 Log::Filter log_filter(Log::Level::Debug);
129 Log::SetFilter(&log_filter); 130 Log::SetGlobalFilter(log_filter);
131
132 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
133 FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
134 Log::AddBackend(
135 std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
130 136
131 MicroProfileOnThreadCreate("EmuThread"); 137 MicroProfileOnThreadCreate("EmuThread");
132 SCOPE_EXIT({ MicroProfileShutdown(); }); 138 SCOPE_EXIT({ MicroProfileShutdown(); });
133 139
134 if (filepath.empty()) { 140 if (filepath.empty()) {
135 NGLOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); 141 LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
136 return -1; 142 return -1;
137 } 143 }
138 144
@@ -153,28 +159,28 @@ int main(int argc, char** argv) {
153 159
154 switch (load_result) { 160 switch (load_result) {
155 case Core::System::ResultStatus::ErrorGetLoader: 161 case Core::System::ResultStatus::ErrorGetLoader:
156 NGLOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str()); 162 LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str());
157 return -1; 163 return -1;
158 case Core::System::ResultStatus::ErrorLoader: 164 case Core::System::ResultStatus::ErrorLoader:
159 NGLOG_CRITICAL(Frontend, "Failed to load ROM!"); 165 LOG_CRITICAL(Frontend, "Failed to load ROM!");
160 return -1; 166 return -1;
161 case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: 167 case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted:
162 NGLOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " 168 LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
163 "being used with yuzu. \n\n For more information on dumping and " 169 "being used with yuzu. \n\n For more information on dumping and "
164 "decrypting games, please refer to: " 170 "decrypting games, please refer to: "
165 "https://yuzu-emu.org/wiki/dumping-game-cartridges/"); 171 "https://yuzu-emu.org/wiki/dumping-game-cartridges/");
166 return -1; 172 return -1;
167 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: 173 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
168 NGLOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); 174 LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
169 return -1; 175 return -1;
170 case Core::System::ResultStatus::ErrorNotInitialized: 176 case Core::System::ResultStatus::ErrorNotInitialized:
171 NGLOG_CRITICAL(Frontend, "CPUCore not initialized"); 177 LOG_CRITICAL(Frontend, "CPUCore not initialized");
172 return -1; 178 return -1;
173 case Core::System::ResultStatus::ErrorSystemMode: 179 case Core::System::ResultStatus::ErrorSystemMode:
174 NGLOG_CRITICAL(Frontend, "Failed to determine system mode!"); 180 LOG_CRITICAL(Frontend, "Failed to determine system mode!");
175 return -1; 181 return -1;
176 case Core::System::ResultStatus::ErrorVideoCore: 182 case Core::System::ResultStatus::ErrorVideoCore:
177 NGLOG_CRITICAL(Frontend, "VideoCore not initialized"); 183 LOG_CRITICAL(Frontend, "VideoCore not initialized");
178 return -1; 184 return -1;
179 case Core::System::ResultStatus::Success: 185 case Core::System::ResultStatus::Success:
180 break; // Expected case 186 break; // Expected case