Compare commits

...

70 Commits

Author SHA1 Message Date
bunnei
03c2d049d4 settings: Turn docked mode off by default. 2018-07-17 22:52:25 -04:00
bunnei
49e5de9f03 vi: Change TransactionId::CancelBuffer to LOG_CRITICAL. 2018-07-17 22:47:35 -04:00
bunnei
0d1a99edf6 vi: Fix size for ListDisplays default display. 2018-07-17 22:18:14 -04:00
bunnei
c3dd456d51 vi: Partially implement buffer crop parameters. 2018-07-17 20:13:17 -04:00
bunnei
8e28af6f89 Merge pull request #675 from Subv/stencil
GPU: Added register definitions for the stencil parameters.
2018-07-17 15:53:00 -07:00
Subv
3d3b10adc7 GPU: Added register definitions for the stencil parameters. 2018-07-17 15:00:21 -05:00
Zach Hilman
69bfe075b5 General Filesystem and Save Data Fixes (#670) 2018-07-17 12:42:15 -07:00
bunnei
88a3140c9b Merge pull request #671 from MerryMage/clear-exclusive-state
scheduler: Clear exclusive state when switching contexts
2018-07-17 07:33:32 -07:00
bunnei
519035db3d Merge pull request #672 from SciresM/to_address_fix
svc:: Fix bug in svcWaitForAddress
2018-07-17 07:32:42 -07:00
bunnei
5f119bed56 Merge pull request #673 from bunnei/fix-buffer-queue-evt
nvflinger: Fix for BufferQueue event handling.
2018-07-17 07:07:57 -07:00
bunnei
170e19d4ea nvflinger: Fix for BufferQueue event handling. 2018-07-17 00:26:23 -04:00
Michael Scire
3b885691a1 Kernel/Arbiter: Fix bug in WaitIfLessThan 2018-07-16 20:55:53 -06:00
MerryMage
56cc1c11ec scheduler: Clear exclusive state when switching contexts 2018-07-16 11:24:00 +01:00
bunnei
f00ca69a81 Merge pull request #669 from lioncash/dynarmic
externals: Update dynarmic to dfdec79
2018-07-15 18:36:32 -07:00
Lioncash
0722adb471 externals: Update dynarmic to dfdec79 2018-07-15 16:11:45 -04:00
bunnei
068668780c Merge pull request #668 from jroweboy/controller-lag
HID: Update controllers less often
2018-07-15 13:06:28 -07:00
bunnei
04b9cde4f5 Merge pull request #664 from jroweboy/logging-stuff
Minor logging improvements
2018-07-15 12:58:52 -07:00
James Rowe
7d209b3c9f HID: Update controllers less often 2018-07-15 13:47:41 -06:00
James Rowe
497b81558e Logging: Dump all logs in the queue on close in debug mode 2018-07-15 13:02:09 -06:00
bunnei
98762e9601 Merge pull request #666 from bunnei/g8r8
gl_rasterizer_cache: Implement texture format G8R8.
2018-07-15 10:09:36 -07:00
bunnei
3a96670f2d gl_rasterizer_cache: Implement texture format G8R8. 2018-07-15 01:33:42 -04:00
bunnei
aaec0b7e70 Merge pull request #665 from bunnei/fix-z24-s8
gl_rasterizer_cache: Fix incorrect offset in ConvertS8Z24ToZ24S8.
2018-07-14 22:18:55 -07:00
bunnei
f8ab956189 Merge pull request #659 from bunnei/depth16
gl_rasterizer_cache: Implement depth format Z16_UNORM.
2018-07-14 21:39:23 -07:00
bunnei
3145114190 gl_rasterizer_cache: Fix incorrect offset in ConvertS8Z24ToZ24S8. 2018-07-15 00:02:05 -04:00
bunnei
e21190f47f gl_rasterizer_cache: Implement depth format Z16_UNORM. 2018-07-14 23:43:28 -04:00
bunnei
2cb3fdca86 Merge pull request #598 from bunnei/makedonecurrent
OpenGL: Use MakeCurrent/DoneCurrent for multithreaded rendering.
2018-07-14 20:18:11 -07:00
bunnei
c324a378ac Merge pull request #663 from Subv/bsd
Services/BSD: Corrected the return for StartMonitoring according to SwIPC
2018-07-14 19:40:34 -07:00
bunnei
fd1f5c5414 Merge pull request #662 from Subv/delete_file
FileSys: Append the requested path to the filesystem base path in DeleteFile
2018-07-14 13:11:58 -07:00
James Rowe
6daebaaa57 Logging: Don't lock the queue for the duration of the write 2018-07-14 11:57:13 -06:00
Subv
b07f4d6afb Services/BSD: Corrected the return for StartMonitoring according to SwIPC. 2018-07-14 12:34:07 -05:00
bunnei
ad0166a982 Merge pull request #661 from ogniK5377/assert-nit
No need to use ASSERT_MSG with an empty assert message
2018-07-14 09:12:21 -07:00
Subv
7e5e4f8d7a FileSys: Append the requested path to the filesystem base path in DeleteFile.
We were trying to delete things in the current directory instead of the actual filesystem directory. This may fix some savedata issues in some games.
2018-07-14 10:57:22 -05:00
David Marcec
a7d6c0d6ea No need to use ASSERT_MSG with an empty message 2018-07-14 23:13:16 +10:00
bunnei
81739a5448 Merge pull request #660 from Subv/depth_write
GPU: Always enable the depth write when clearing the depth buffer.
2018-07-14 00:38:12 -07:00
bunnei
05cb10530f OpenGL: Use MakeCurrent/DoneCurrent for multithreaded rendering. 2018-07-14 02:50:35 -04:00
Subv
b37354cca8 GPU: Always enable the depth write when clearing the depth buffer.
The GPU ignores that register when clearing, but OpenGL obeys the glDepthMask parameter, so we set the depth mask to GL_TRUE when clearing the depth buffer. It will be restored to the correct value automatically on the next draw call.
2018-07-14 00:52:23 -05:00
bunnei
9fc0d1d701 Merge pull request #657 from bunnei/dual-vs
gl_shader_gen: Implement dual vertex shader mode.
2018-07-13 07:08:54 -07:00
Hedges
e066bc75b9 More improvements to GDBStub (#653)
* More improvements to GDBStub
- Debugging of threads should work correctly with source and assembly level stepping and modifying registers and memory, meaning threads and callstacks are fully clickable in VS.
- List of modules is available to the client, with assumption that .nro and .nso are backed up by an .elf with symbols, while deconstructed ROMs keep N names.
- Initial support for floating point registers.

* Tidy up as requested in PR feedback

* Tidy up as requested in PR feedback
2018-07-12 20:22:59 -07:00
bunnei
8aeff9cf8e gl_rasterizer: Fix check for if a shader stage is enabled. 2018-07-12 22:57:57 -04:00
bunnei
c4015cd93a gl_shader_gen: Implement dual vertex shader mode.
- When VertexA shader stage is enabled, we combine with VertexB program to make a single Vertex Shader stage.
2018-07-12 22:25:36 -04:00
bunnei
ce23ae3ede Merge pull request #656 from ogniK5377/audren-mem-init
Initialized memory for RequestUpdateAudioRenderer and fixed MemoryPoolSection to be more accurate
2018-07-12 19:12:47 -07:00
bunnei
64b5e5d5d9 Merge pull request #655 from bunnei/pred-lt-nan
gl_shader_decompiler: Implement PredCondition::LessThanWithNan.
2018-07-12 18:59:15 -07:00
bunnei
52636f67cc Merge pull request #654 from bunnei/cond-exit
gl_shader_decompiler: Use FlowCondition field in EXIT instruction.
2018-07-12 18:59:05 -07:00
David Marcec
8bd8d1e3da We only need to alert for memory pool changes 2018-07-13 10:36:28 +10:00
David Marcec
6642011706 initialized voice status and unused sizes in the update data header 2018-07-13 10:35:44 +10:00
bunnei
49c0c081c4 gl_shader_decompiler: Implement PredCondition::LessThanWithNan. 2018-07-12 20:04:35 -04:00
bunnei
4757ffdcce gl_shader_decompiler: Use FlowCondition field in EXIT instruction. 2018-07-12 20:00:37 -04:00
Sebastian Valle
274d1fb0fc Merge pull request #652 from Subv/fadd32i
GPU: Implement the FADD32I shader instruction.
2018-07-12 17:36:51 -05:00
bunnei
3ff21345b4 Merge pull request #651 from Subv/ffma_decode
GPU: Corrected the decoding of FFMA for immediate operands.
2018-07-12 12:42:58 -07:00
Subv
c1ae841f47 GPU: Implement the FADD32I shader instruction. 2018-07-12 12:00:31 -05:00
Tobias
316b933a31 Port #3335 and #3373 from Citra: "Small SDL fixes" and "Print the actual error preventing SDL from working" (#637)
* Port #3335 and #3373 from Citra

* Fixup: Use the new logging placeholders
2018-07-12 09:26:27 -07:00
Subv
0cad310e12 GPU: Corrected the decoding of FFMA for immediate operands. 2018-07-12 10:15:48 -05:00
bunnei
4f41ffdd41 Merge pull request #648 from ogniK5377/no-net
Let games/application know that we're offline
2018-07-12 06:44:15 -07:00
bunnei
7c7b2b8285 Merge pull request #649 from ogniK5377/audout-auto
Audout "Auto" functions
2018-07-12 06:43:37 -07:00
bunnei
b89fb430c7 Merge pull request #650 from jroweboy/logging-stuff
Minor logging fixes
2018-07-12 06:42:46 -07:00
James Rowe
b30c5370b1 yuzu - Fix duplicate logs 2018-07-12 01:11:43 -06:00
James Rowe
020d005d8c yuzu-cmd Apply the filter string from settings 2018-07-12 01:09:03 -06:00
David Marcec
706892de7d Audout "Auto" functions
Audout autos are identical to their counterpart except for the buffer type which yuzu already handles for us.
2018-07-12 16:57:31 +10:00
David Marcec
3d68f6ba6c Added IsWirelessCommunicationEnabled, IsEthernetCommunicationEnabled, IsAnyInternetRequestAccepted
Since we have no socket implementation we should be returning 0 to indicate we're currently offline.
2018-07-12 16:40:17 +10:00
bunnei
7230ceb584 Merge pull request #559 from Subv/mount_savedata
Services/FS: Return the correct error code when trying to mount a nonexistent savedata.
2018-07-11 20:21:52 -07:00
bunnei
afb26b190f Merge pull request #585 from janisozaur/patch-11
Improve directory creation in WindowsCopyFiles.cmake
2018-07-11 20:21:17 -07:00
bunnei
e2037821b6 Merge pull request #646 from bunnei/fix-hid-smo
hid: Fix timestamps and controller type.
2018-07-11 06:30:56 -07:00
bunnei
12a6996262 hid: Fix timestamps and controller type.
- This fixes user input in SMO.
2018-07-10 22:55:13 -04:00
bunnei
379a935016 Merge pull request #644 from ogniK5377/getconfig-err
NvOsGetConfigU32 production impl
2018-07-10 15:44:23 -07:00
bunnei
04524e76c2 Merge pull request #633 from FearlessTobi/port-defines
Port #3579 from Citra: Clean up architecture-specific defines
2018-07-10 09:12:52 -07:00
bunnei
3e966be6fc Merge pull request #642 from bunnei/create-save-dir
savedata_factory: Always create a save directory for games.
2018-07-10 09:09:58 -07:00
bunnei
eb6cbfdbd8 savedata_factory: Always create a save directory for games. 2018-07-08 17:05:13 -04:00
fearlessTobi
70a6691e3b Port #3579 from Citra 2018-07-07 13:59:18 +02:00
Michał Janiszewski
23dc36ed71 Improve directory creation in WindowsCopyFiles.cmake 2018-06-24 21:27:00 +02:00
Subv
5f57a70a7d Services/FS: Return the correct error code when trying to mount a nonexistent savedata. 2018-06-18 19:26:01 -05:00
72 changed files with 983 additions and 500 deletions

View File

@@ -1,4 +1,4 @@
# Copyright 2016 Citra Emulator Project
# Copyright 2018 Yuzu Emulator Project
# Licensed under GPLv2 or any later version
# Refer to the license.txt file included.
@@ -22,7 +22,7 @@ function(windows_copy_files TARGET SOURCE_DIR DEST_DIR)
# cmake adds an extra check for command success which doesn't work too well with robocopy
# so trick it into thinking the command was successful with the || cmd /c "exit /b 0"
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND if not exist ${DEST_DIR} mkdir ${DEST_DIR} 2> nul
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR}
COMMAND robocopy ${SOURCE_DIR} ${DEST_DIR} ${ARGN} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
)
endfunction()
endfunction()

View File

@@ -4,7 +4,7 @@
#pragma once
#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM)
#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM)
#include <cstdlib> // for exit
#endif
#include "common/common_types.h"
@@ -30,7 +30,7 @@
#ifdef ARCHITECTURE_x86_64
#define Crash() __asm__ __volatile__("int $3")
#elif defined(_M_ARM)
#elif defined(ARCHITECTURE_ARM)
#define Crash() __asm__ __volatile__("trap")
#else
#define Crash() exit(1)

View File

@@ -5,6 +5,7 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <climits>
#include <condition_variable>
#include <memory>
#include <thread>
@@ -83,8 +84,10 @@ private:
}
};
while (true) {
std::unique_lock<std::mutex> lock(message_mutex);
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
{
std::unique_lock<std::mutex> lock(message_mutex);
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
}
if (!running) {
break;
}
@@ -92,7 +95,7 @@ private:
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
// where a system is repeatedly spamming logs even on close.
constexpr int MAX_LOGS_TO_WRITE = 100;
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
int logs_written = 0;
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
write_logs(entry);
@@ -282,4 +285,4 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
Impl::Instance().PushEntry(std::move(entry));
}
} // namespace Log
} // namespace Log

View File

@@ -94,4 +94,11 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
bool Filter::CheckMessage(Class log_class, Level level) const {
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
}
bool Filter::IsDebug() const {
return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) {
return static_cast<u8>(l) <= static_cast<u8>(Level::Debug);
});
}
} // namespace Log

View File

@@ -47,6 +47,9 @@ public:
/// Matches class/level combination against the filter, returning true if it passed.
bool CheckMessage(Class log_class, Level level) const;
/// Returns true if any logging classes are set to debug
bool IsDebug() const;
private:
std::array<Level, (size_t)Class::Count> class_levels;
};

View File

@@ -16,7 +16,7 @@
#include <sys/mman.h>
#endif
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
#include <unistd.h>
#define PAGE_MASK (getpagesize() - 1)
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
@@ -30,7 +30,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
static char* map_hint = nullptr;
#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
// This OS has no flag to enforce allocation below the 4 GB boundary,
// but if we hint that we want a low address it is very likely we will
// get one.
@@ -42,7 +42,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
#endif
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE
#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT)
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
| (low ? MAP_32BIT : 0)
#endif
,
@@ -57,7 +57,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
#endif
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
}
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
else {
if (low) {
map_hint += size;

View File

@@ -134,7 +134,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
size_t dir_end = full_path.find_last_of("/"
// windows needs the : included for something like just "C:" to be considered a directory
#ifdef _WIN32
":"
"\\:"
#endif
);
if (std::string::npos == dir_end)

View File

@@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) {
inline u64 swap64(u64 _data) {
return _byteswap_uint64(_data);
}
#elif _M_ARM
#elif ARCHITECTURE_ARM
inline u16 swap16(u16 _data) {
u32 data = _data;
__asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));

View File

@@ -116,6 +116,8 @@ public:
*/
virtual void LoadContext(const ThreadContext& ctx) = 0;
virtual void ClearExclusiveState() = 0;
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
};

View File

@@ -226,6 +226,10 @@ void ARM_Dynarmic::ClearInstructionCache() {
jit->ClearCache();
}
void ARM_Dynarmic::ClearExclusiveState() {
jit->ClearExclusiveState();
}
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit(cb);
current_page_table = Memory::GetCurrentPageTable();

View File

@@ -39,6 +39,7 @@ public:
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
void PageTableChanged() override;

View File

@@ -193,11 +193,11 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
if (last_bkpt_hit) {
if (last_bkpt_hit || (num_instructions == 1)) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
}
GDBStub::SendTrap(thread, 5);
}
}
@@ -263,6 +263,8 @@ void ARM_Unicorn::PrepareReschedule() {
CHECKED(uc_emu_stop(uc));
}
void ARM_Unicorn::ClearExclusiveState() {}
void ARM_Unicorn::ClearInstructionCache() {}
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {

View File

@@ -31,6 +31,7 @@ public:
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ExecuteInstructions(int num_instructions);
void Run() override;
void Step() override;

View File

@@ -58,11 +58,13 @@ ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::
}
ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const {
if (!FileUtil::Exists(path)) {
std::string full_path = base_directory + path;
if (!FileUtil::Exists(full_path)) {
return ERROR_PATH_NOT_FOUND;
}
FileUtil::Delete(path);
FileUtil::Delete(full_path);
return RESULT_SUCCESS;
}

View File

@@ -11,6 +11,9 @@ namespace FileSys {
namespace ErrCodes {
enum {
NotFound = 1,
SaveDataNotFound = 1002,
SdCardNotFound = 2001,
RomFSNotFound = 2520,
};
}

View File

@@ -167,35 +167,4 @@ public:
virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0;
};
class FileSystemFactory : NonCopyable {
public:
virtual ~FileSystemFactory() {}
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
*/
virtual std::string GetName() const = 0;
/**
* Tries to open the archive of this type with the specified path
* @param path Path to the archive
* @return An ArchiveBackend corresponding operating specified archive path.
*/
virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0;
/**
* Deletes the archive contents and then re-creates the base folder
* @param path Path to the archive
* @return ResultCode of the operation, 0 on success
*/
virtual ResultCode Format(const Path& path) = 0;
/**
* Retrieves the format info about the archive with the specified path
* @param path Path to the archive
* @return Format information about the archive or error code
*/
virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0;
};
} // namespace FileSys

View File

@@ -11,28 +11,17 @@
namespace FileSys {
RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) {
ResultVal<std::unique_ptr<FileSystemBackend>> RomFSFactory::Open(u64 title_id) {
// TODO(DarkLordZach): Use title id.
auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode RomFS_Factory::Format(const Path& path) {
LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
} // namespace FileSys

View File

@@ -15,16 +15,11 @@
namespace FileSys {
/// File system interface to the RomFS archive
class RomFS_Factory final : public FileSystemFactory {
class RomFSFactory {
public:
explicit RomFS_Factory(Loader::AppLoader& app_loader);
explicit RomFSFactory(Loader::AppLoader& app_loader);
std::string GetName() const override {
return "ArchiveFactory_RomFS";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
ResultVal<std::unique_ptr<FileSystemBackend>> Open(u64 title_id);
private:
std::shared_ptr<FileUtil::IOFile> romfs_file;

View File

@@ -12,11 +12,64 @@
namespace FileSys {
SaveData_Factory::SaveData_Factory(std::string nand_directory)
std::string SaveDataDescriptor::DebugInfo() {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
}
SaveDataFactory::SaveDataFactory(std::string nand_directory)
: nand_directory(std::move(nand_directory)) {}
ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
std::string save_directory = GetFullPath();
ResultVal<std::unique_ptr<FileSystemBackend>> SaveDataFactory::Open(SaveDataSpaceId space,
SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).",
meta.zero_1);
}
if (meta.zero_2 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).",
meta.zero_2);
}
if (meta.zero_3 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).",
meta.zero_3);
}
}
if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is "
"non-zero ({:016X}).",
meta.title_id);
}
std::string save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
// TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
// But, user_ids don't match so this works for now.
if (!FileUtil::Exists(save_directory)) {
// TODO(bunnei): This is a work-around to always create a save data directory if it does not
// already exist. This is a hack, as we do not understand yet how this works on hardware.
// Without a save data directory, many games will assert on boot. This should not have any
// bad side-effects.
FileUtil::CreateFullPath(save_directory);
}
// TODO(DarkLordZach): For some reason, CreateFullPath doesn't create the last bit. Should be
// fixed with VFS.
if (!FileUtil::IsDirectory(save_directory)) {
FileUtil::CreateDir(save_directory);
}
// Return an error if the save data doesn't actually exist.
if (!FileUtil::IsDirectory(save_directory)) {
// TODO(Subv): Find out correct error code.
@@ -27,28 +80,35 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path&
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode SaveData_Factory::Format(const Path& path) {
LOG_WARNING(Service_FS, "Format archive {}", GetName());
// Create the save data directory.
if (!FileUtil::CreateFullPath(GetFullPath())) {
// TODO(Subv): Find the correct error code.
return ResultCode(-1);
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) const {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0)
title_id = Core::CurrentProcess()->program_id;
std::string prefix;
switch (space) {
case SaveDataSpaceId::NandSystem:
prefix = nand_directory + "system/save/";
break;
case SaveDataSpaceId::NandUser:
prefix = nand_directory + "user/save/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
}
return RESULT_SUCCESS;
}
ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
std::string SaveData_Factory::GetFullPath() const {
u64 title_id = Core::CurrentProcess()->program_id;
// TODO(Subv): Somehow obtain this value.
u32 user = 0;
return fmt::format("{}save/{:016X}/{:08X}/", nand_directory, title_id, user);
switch (type) {
case SaveDataType::SystemSaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}", prefix, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", prefix, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
}
}
} // namespace FileSys

View File

@@ -12,22 +12,50 @@
namespace FileSys {
/// File system interface to the SaveData archive
class SaveData_Factory final : public FileSystemFactory {
public:
explicit SaveData_Factory(std::string nand_directory);
enum class SaveDataSpaceId : u8 {
NandSystem = 0,
NandUser = 1,
SdCard = 2,
TemporaryStorage = 3,
};
std::string GetName() const override {
return "SaveData_Factory";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
enum class SaveDataType : u8 {
SystemSaveData = 0,
SaveData = 1,
BcatDeliveryCacheStorage = 2,
DeviceSaveData = 3,
TemporaryStorage = 4,
CacheStorage = 5,
};
struct SaveDataDescriptor {
u64_le title_id;
u128 user_id;
u64_le save_id;
SaveDataType type;
INSERT_PADDING_BYTES(7);
u64_le zero_1;
u64_le zero_2;
u64_le zero_3;
std::string DebugInfo();
};
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
/// File system interface to the SaveData archive
class SaveDataFactory {
public:
explicit SaveDataFactory(std::string nand_directory);
ResultVal<std::unique_ptr<FileSystemBackend>> Open(SaveDataSpaceId space,
SaveDataDescriptor meta);
private:
std::string nand_directory;
std::string sd_directory;
std::string GetFullPath() const;
std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id,
u64 save_id) const;
};
} // namespace FileSys

View File

@@ -12,9 +12,9 @@
namespace FileSys {
SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
SDMCFactory::SDMCFactory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) {
ResultVal<std::unique_ptr<FileSystemBackend>> SDMCFactory::Open() {
// Create the SD Card directory if it doesn't already exist.
if (!FileUtil::IsDirectory(sd_directory)) {
FileUtil::CreateFullPath(sd_directory);
@@ -24,16 +24,4 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& pat
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode SDMC_Factory::Format(const Path& path) {
LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
// TODO(Subv): Find the right error code for this
return ResultCode(-1);
}
ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
} // namespace FileSys

View File

@@ -13,16 +13,11 @@
namespace FileSys {
/// File system interface to the SDCard archive
class SDMC_Factory final : public FileSystemFactory {
class SDMCFactory {
public:
explicit SDMC_Factory(std::string sd_directory);
explicit SDMCFactory(std::string sd_directory);
std::string GetName() const override {
return "SDMC_Factory";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
ResultVal<std::unique_ptr<FileSystemBackend>> Open();
private:
std::string sd_directory;

View File

@@ -61,10 +61,16 @@ const u32 SIGTERM = 15;
const u32 MSG_WAITALL = 8;
#endif
const u32 X30_REGISTER = 30;
const u32 LR_REGISTER = 30;
const u32 SP_REGISTER = 31;
const u32 PC_REGISTER = 32;
const u32 CPSR_REGISTER = 33;
const u32 UC_ARM64_REG_Q0 = 34;
const u32 FPSCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
const u32 TODO_DUMMY_REG_997 = 997;
const u32 TODO_DUMMY_REG_998 = 998;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
@@ -130,6 +136,8 @@ static const char* target_xml =
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
</feature>
<feature name="org.gnu.gdb.aarch64.fpu">
</feature>
</target>
)";
@@ -144,6 +152,7 @@ static u32 latest_signal = 0;
static bool memory_break = false;
static Kernel::Thread* current_thread = nullptr;
static u32 current_core = 0;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
@@ -171,13 +180,34 @@ static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
struct Module {
std::string name;
PAddr beg;
PAddr end;
};
static std::vector<Module> modules;
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
Module module;
if (add_elf_ext) {
Common::SplitPath(name, nullptr, &module.name, nullptr);
module.name += ".elf";
} else {
module.name = std::move(name);
}
module.beg = beg;
module.end = end;
modules.push_back(std::move(module));
}
static Kernel::Thread* FindThreadById(int id) {
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto thread : threads) {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadId() == id) {
current_thread = thread.get();
return current_thread;
current_core = core;
return thread.get();
}
}
}
@@ -197,6 +227,8 @@ static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
return thread->context.pc;
} else if (id == CPSR_REGISTER) {
return thread->context.cpsr;
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0];
} else {
return 0;
}
@@ -215,6 +247,8 @@ static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
thread->context.pc = val;
} else if (id == CPSR_REGISTER) {
thread->context.cpsr = val;
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val;
}
}
@@ -534,7 +568,11 @@ static void HandleQuery() {
SendReply("T0");
} else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
// PacketSize needs to be large enough for target xml
SendReply("PacketSize=2000;qXfer:features:read+");
std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+";
if (!modules.empty()) {
buffer += ";qXfer:libraries:read+";
}
SendReply(buffer.c_str());
} else if (strncmp(query, "Xfer:features:read:target.xml:",
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
@@ -543,9 +581,9 @@ static void HandleQuery() {
SendReply(buffer.c_str());
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto thread : threads) {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x}", thread->GetThreadId());
val += ",";
}
@@ -554,6 +592,31 @@ static void HandleQuery() {
SendReply(val.c_str());
} else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
SendReply("l");
} else if (strncmp(query, "Xfer:threads:read", strlen("Xfer:threads:read")) == 0) {
std::string buffer;
buffer += "l<?xml version=\"1.0\"?>";
buffer += "<threads>";
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
thread->GetThreadId(), core, thread->GetThreadId());
}
}
buffer += "</threads>";
SendReply(buffer.c_str());
} else if (strncmp(query, "Xfer:libraries:read", strlen("Xfer:libraries:read")) == 0) {
std::string buffer;
buffer += "l<?xml version=\"1.0\"?>";
buffer += "<library-list>";
for (const auto& module : modules) {
buffer +=
fmt::format(R"*("<library name = "{}"><segment address = "0x{:x}"/></library>)*",
module.name, module.beg);
}
buffer += "</library-list>";
SendReply(buffer.c_str());
} else {
SendReply("");
}
@@ -561,33 +624,27 @@ static void HandleQuery() {
/// Handle set thread command from gdb client.
static void HandleSetThread() {
if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) {
int thread_id = -1;
if (command_buffer[2] != '-') {
thread_id = static_cast<int>(HexToInt(
command_buffer + 2,
command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/));
}
if (thread_id >= 1) {
current_thread = FindThreadById(thread_id);
}
if (!current_thread) {
thread_id = 1;
current_thread = FindThreadById(thread_id);
}
if (current_thread) {
SendReply("OK");
return;
}
int thread_id = -1;
if (command_buffer[2] != '-') {
thread_id = static_cast<int>(HexToInt(command_buffer + 2, command_length - 2));
}
if (thread_id >= 1) {
current_thread = FindThreadById(thread_id);
}
if (!current_thread) {
thread_id = 1;
current_thread = FindThreadById(thread_id);
}
if (current_thread) {
SendReply("OK");
return;
}
SendReply("E01");
}
/// Handle thread alive command from gdb client.
static void HandleThreadAlive() {
int thread_id = static_cast<int>(
HexToInt(command_buffer + 1,
command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/));
int thread_id = static_cast<int>(HexToInt(command_buffer + 1, command_length - 1));
if (thread_id == 0) {
thread_id = 1;
}
@@ -610,16 +667,23 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
latest_signal = signal;
std::string buffer;
if (full) {
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER,
Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
Common::swap64(RegRead(SP_REGISTER, thread)));
} else {
buffer = fmt::format("T{:02x};", latest_signal);
if (!thread) {
full = false;
}
buffer += fmt::format("thread:{:x};", thread->GetThreadId());
std::string buffer;
if (full) {
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};{:02x}:{:016x}", latest_signal,
PC_REGISTER, Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
Common::swap64(RegRead(SP_REGISTER, thread)), LR_REGISTER,
Common::swap64(RegRead(LR_REGISTER, thread)));
} else {
buffer = fmt::format("T{:02x}", latest_signal);
}
if (thread) {
buffer += fmt::format(";thread:{:x};", thread->GetThreadId());
}
SendReply(buffer.c_str());
}
@@ -711,8 +775,12 @@ static void ReadRegister() {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == CPSR_REGISTER) {
IntToGdbHex(reply, (u32)RegRead(id, current_thread));
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == FPSCR_REGISTER) {
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
} else {
return SendReply("E01");
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
}
SendReply(reinterpret_cast<char*>(reply));
@@ -729,7 +797,7 @@ static void ReadRegisters() {
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
}
bufptr += (32 * 16);
bufptr += 32 * 16;
LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
@@ -739,6 +807,16 @@ static void ReadRegisters() {
bufptr += 8;
for (int reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) {
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
}
bufptr += 32 * 32;
LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread));
bufptr += 8;
SendReply(reinterpret_cast<char*>(buffer));
}
@@ -759,10 +837,17 @@ static void WriteRegister() {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == CPSR_REGISTER) {
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == FPSCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
} else {
return SendReply("E01");
RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
}
@@ -773,18 +858,25 @@ static void WriteRegisters() {
if (command_buffer[0] != 'G')
return SendReply("E01");
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
if (reg <= SP_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == PC_REGISTER) {
RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == CPSR_REGISTER) {
RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
} else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == FPSCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else {
UNIMPLEMENTED();
}
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
}
@@ -806,6 +898,10 @@ static void ReadMemory() {
SendReply("E01");
}
if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) {
return SendReply("E00");
}
if (!Memory::IsValidVirtualAddress(addr)) {
return SendReply("E00");
}
@@ -840,16 +936,18 @@ static void WriteMemory() {
}
void Break(bool is_memory_break) {
if (!halt_loop) {
halt_loop = true;
send_trap = true;
}
send_trap = true;
memory_break = is_memory_break;
}
/// Tell the CPU that it should perform a single step.
static void Step() {
if (command_length > 1) {
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
}
step_loop = true;
halt_loop = true;
send_trap = true;
@@ -1090,6 +1188,8 @@ static void Init(u16 port) {
breakpoints_read.clear();
breakpoints_write.clear();
modules.clear();
// Start gdb server
LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port);
@@ -1192,8 +1292,12 @@ void SetCpuStepFlag(bool is_step) {
void SendTrap(Kernel::Thread* thread, int trap) {
if (send_trap) {
if (!halt_loop || current_thread == thread) {
current_thread = thread;
SendSignal(thread, trap);
}
halt_loop = true;
send_trap = false;
SendSignal(thread, trap);
}
}
}; // namespace GDBStub

View File

@@ -6,6 +6,7 @@
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/thread.h"
@@ -51,6 +52,9 @@ bool IsServerEnabled();
/// Returns true if there is an active socket connection.
bool IsConnected();
/// Register module.
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
/**
* Signal to the gdbstub server that it should halt CPU execution.
*
@@ -80,10 +84,10 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
*/
bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
// If set to true, the CPU will halt at the beginning of the next CPU loop.
/// If set to true, the CPU will halt at the beginning of the next CPU loop.
bool GetCpuHaltFlag();
// If set to true and the CPU is halted, the CPU will step one instruction.
/// If set to true and the CPU is halted, the CPU will step one instruction.
bool GetCpuStepFlag();
/**

View File

@@ -115,7 +115,7 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
s32 updated_value;
if (waiting_threads.size() == 0) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;
} else {
updated_value = value;
@@ -140,7 +140,9 @@ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool
s32 cur_value = static_cast<s32>(Memory::Read32(address));
if (cur_value < value) {
Memory::Write32(address, static_cast<u32>(cur_value - 1));
if (should_decrement) {
Memory::Write32(address, static_cast<u32>(cur_value - 1));
}
} else {
return ERR_INVALID_STATE;
}

View File

@@ -29,7 +29,8 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
const std::string& reason, u64 timeout,
WakeupCallback&& callback) {
WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback =
@@ -41,7 +42,12 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
return true;
};
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
if (!event) {
// Create event if not provided
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
}
event->Clear();
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
thread->wait_objects = {event};
event->AddWaitingThread(thread);
@@ -214,8 +220,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
(sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32);
ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented");
ASSERT_MSG(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT_MSG(move_objects.size() == handle_descriptor_header->num_handles_to_move);
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
// We don't make a distinction between copy and move handles when translating since HLE
// services don't deal with handles directly. However, the guest applications might check

View File

@@ -118,10 +118,12 @@ public:
* @param callback Callback to be invoked when the thread is resumed. This callback must write
* the entire command response once again, regardless of the state of it before this function
* was called.
* @param event Event to use to wake up the thread. If unspecified, an event will be created.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
u64 timeout, WakeupCallback&& callback);
u64 timeout, WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event = nullptr);
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);

View File

@@ -85,6 +85,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
cpu_core->ClearExclusiveState();
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because

View File

@@ -4,9 +4,11 @@
#include <cinttypes>
#include <stack>
#include "core/core.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
@@ -614,25 +616,14 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 uid = rp.PopRaw<u128>();
u128 uid = rp.PopRaw<u128>(); // What does this do?
LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 4};
FileSys::Path unused;
auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused);
if (savedata.Failed()) {
// Create the save data and return an error indicating that the operation was performed.
FileSystem::FormatFileSystem(FileSystem::Type::SaveData);
// TODO(Subv): Find out the correct error code for this.
rb.Push(ResultCode(ErrorModule::FS, 40));
} else {
rb.Push(RESULT_SUCCESS);
}
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
} // namespace Service::AM
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.

View File

@@ -27,12 +27,12 @@ public:
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
{1, &IAudioOut::StartAudioOut, "StartAudioOut"},
{2, &IAudioOut::StopAudioOut, "StopAudioOut"},
{3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
{3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
{5, &IAudioOut::GetReleasedAudioOutBuffer, "GetReleasedAudioOutBuffer"},
{5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
{6, nullptr, "ContainsAudioOutBuffer"},
{7, nullptr, "AppendAudioOutBufferAuto"},
{8, nullptr, "GetReleasedAudioOutBufferAuto"},
{7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
{9, nullptr, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{11, nullptr, "FlushAudioOutBuffers"},
@@ -96,7 +96,7 @@ private:
rb.PushCopyObjects(buffer_event);
}
void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
@@ -107,7 +107,7 @@ private:
rb.Push(RESULT_SUCCESS);
}
void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) {
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
// TODO(st4rk): This is how libtransistor currently implements the
@@ -163,7 +163,7 @@ private:
AudioState audio_out_state;
};
void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
@@ -179,7 +179,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(1);
}
void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
if (!audio_out_interface) {
@@ -196,10 +196,10 @@ void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
}
AudOutU::AudOutU() : ServiceFramework("audout:u") {
static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
{1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
{2, nullptr, "ListAudioOutsAuto"},
{3, nullptr, "OpenAudioOutAuto"}};
static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
{1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
{2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
{3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
RegisterHandlers(functions);
}

View File

@@ -22,8 +22,8 @@ public:
private:
std::shared_ptr<IAudioOut> audio_out_interface;
void ListAudioOuts(Kernel::HLERequestContext& ctx);
void OpenAudioOut(Kernel::HLERequestContext& ctx);
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
enum class PcmFormat : u32 {
Invalid = 0,

View File

@@ -47,7 +47,7 @@ public:
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
voice_status_list.reserve(worker_params.voice_count);
voice_status_list.resize(worker_params.voice_count);
}
~IAudioRenderer() {
CoreTiming::UnscheduleEvent(audio_event, 0);
@@ -87,8 +87,6 @@ private:
memory_pool[i].state = MemoryPoolStates::Attached;
else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
memory_pool[i].state = MemoryPoolStates::Detached;
else
memory_pool[i].state = mem_pool_info[i].pool_state;
}
std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
@@ -183,7 +181,9 @@ private:
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
voice_resource_size = 0x0;
effects_size = config.effect_count * 0x10;
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +

View File

@@ -4,6 +4,7 @@
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
@@ -16,57 +17,77 @@ namespace Service::FileSystem {
* Map of registered file systems, identified by type. Once an file system is registered here, it
* is never removed until UnregisterFileSystems is called.
*/
static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) {
auto result = filesystem_map.emplace(type, std::move(factory));
bool inserted = result.second;
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
auto& filesystem = result.first->second;
LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
static_cast<u32>(type));
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
romfs_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered RomFS");
return RESULT_SUCCESS;
}
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path) {
LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
// TODO(bunnei): Find a better error code for this
return ResultCode(-1);
}
return itr->second->Open(path);
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data");
save_data_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered save data");
return RESULT_SUCCESS;
}
ResultCode FormatFileSystem(Type type) {
LOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type));
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return RESULT_SUCCESS;
}
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id) {
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id);
if (romfs_factory == nullptr) {
// TODO(bunnei): Find a better error code for this
return ResultCode(-1);
}
FileSys::Path unused;
return itr->second->Format(unused);
return romfs_factory->Open(title_id);
}
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSaveData(
FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct) {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), SaveStructDebugInfo(save_struct));
if (save_data_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound);
}
return save_data_factory->Open(space, save_struct);
}
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC() {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
}
return sdmc_factory->Open();
}
void RegisterFileSystems() {
filesystem_map.clear();
romfs_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr;
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
RegisterFileSystem(std::move(savedata), Type::SaveData);
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
save_data_factory = std::move(savedata);
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
RegisterFileSystem(std::move(sdcard), Type::SDMC);
auto sdcard = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
sdmc_factory = std::move(sdcard);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {

View File

@@ -6,12 +6,13 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/hle/result.h"
namespace FileSys {
class FileSystemBackend;
class FileSystemFactory;
class Path;
} // namespace FileSys
namespace Service {
@@ -22,35 +23,20 @@ class ServiceManager;
namespace FileSystem {
/// Supported FileSystem types
enum class Type {
RomFS = 1,
SaveData = 2,
SDMC = 3,
};
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
/**
* Registers a FileSystem, instances of which can later be opened using its IdCode.
* @param factory FileSystem backend interface to use
* @param type Type used to access this type of FileSystem
*/
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type);
// TODO(DarkLordZach): BIS Filesystem
// ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
/**
* Opens a file system
* @param type Type of the file system to open
* @param path Path to the file system, used with Binary paths
* @return FileSys::FileSystemBackend interface to the file system
*/
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path);
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id);
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSaveData(
FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct);
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC();
/**
* Formats a file system
* @param type Type of the file system to format
* @return ResultCode of the operation
*/
ResultCode FormatFileSystem(Type type);
// TODO(DarkLordZach): BIS Filesystem
// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
/// Registers all Filesystem services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);

View File

@@ -7,16 +7,27 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
namespace Service::FileSystem {
enum class StorageId : u8 {
None = 0,
Host = 1,
GameCard = 2,
NandSystem = 3,
NandUser = 4,
SdCard = 5
};
class IStorage final : public ServiceFramework<IStorage> {
public:
IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend)
@@ -486,17 +497,6 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
RegisterHandlers(functions);
}
void FSP_SRV::TryLoadRomFS() {
if (romfs) {
return;
}
FileSys::Path unused;
auto res = OpenFileSystem(Type::RomFS, unused);
if (res.Succeeded()) {
romfs = std::move(res.Unwrap());
}
}
void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
@@ -507,8 +507,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
IFileSystem filesystem(OpenSDMC().Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -518,25 +517,36 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<std::array<u8, 0x40>>();
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}",
save_struct.DebugInfo(), uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap();
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
auto unk = rp.Pop<u32>();
LOG_INFO(Service_FS, "called with unknown={:08X}", unk);
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
auto filesystem = OpenSaveData(space_id, save_struct);
if (filesystem.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound));
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
rb.PushIpcInterface<IFileSystem>(std::move(filesystem.Unwrap()));
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -550,8 +560,8 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
TryLoadRomFS();
if (!romfs) {
auto romfs = OpenRomFS(Core::System::GetInstance().CurrentProcess()->program_id);
if (romfs.Failed()) {
// TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::ResponseBuilder rb{ctx, 2};
@@ -559,8 +569,8 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
return;
}
// Attempt to open a StorageBackend interface to the RomFS
auto storage = romfs->OpenFile({}, {});
auto storage = romfs.Unwrap()->OpenFile({}, {});
if (storage.Failed()) {
LOG_CRITICAL(Service_FS, "no storage interface available!");
IPC::ResponseBuilder rb{ctx, 2};
@@ -574,8 +584,40 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
}
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess");
OpenDataStorageByCurrentProcess(ctx);
IPC::RequestParser rp{ctx};
auto storage_id = rp.PopRaw<StorageId>();
auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}",
static_cast<u8>(storage_id), title_id);
if (title_id != Core::System::GetInstance().CurrentProcess()->program_id) {
LOG_CRITICAL(
Service_FS,
"Attempting to access RomFS of another title id (current={:016X}, requested={:016X}).",
Core::System::GetInstance().CurrentProcess()->program_id, title_id);
}
auto romfs = OpenRomFS(title_id);
if (romfs.Failed()) {
LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::RomFSNotFound));
return;
}
auto storage = romfs.Unwrap()->OpenFile({}, {});
if (storage.Failed()) {
LOG_CRITICAL(Service_FS, "no storage interface available!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(storage.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));
}
} // namespace Service::FileSystem

View File

@@ -19,8 +19,6 @@ public:
~FSP_SRV() = default;
private:
void TryLoadRomFS();
void Initialize(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);

View File

@@ -18,9 +18,9 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(shinyquagsire23): These need better values.
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
@@ -75,7 +75,7 @@ private:
// Set up controllers as neon red+blue Joy-Con attached to console
ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair;
controller_header.type = ControllerType_Handheld;
controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
controller_header.right_color_body = JOYCON_BODY_NEON_RED;
controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
@@ -84,23 +84,21 @@ private:
for (size_t controller = 0; controller < mem.controllers.size(); controller++) {
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
// TODO(DarkLordZach): Is this layout/controller config actually invalid?
if (controller == Controller_Handheld && index == Layout_Single)
continue;
ControllerLayout& layout = mem.controllers[controller].layouts[index];
layout.header.num_entries = HID_NUM_ENTRIES;
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
// HID shared memory stores the state of the past 17 samples in a circlular buffer,
// each with a timestamp in number of samples since boot.
const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
layout.header.timestamp_ticks = CoreTiming::GetTicks();
layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
entry.timestamp++;
entry.timestamp = last_entry.timestamp + 1;
// TODO(shinyquagsire23): Is this always identical to timestamp?
entry.timestamp_2++;
entry.timestamp_2 = entry.timestamp;
// TODO(shinyquagsire23): More than just handheld input
if (controller != Controller_Handheld)

View File

@@ -148,6 +148,24 @@ private:
LOG_DEBUG(Service_NIFM, "called");
}
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
}
void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
}
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
}
};
IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
@@ -167,11 +185,11 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
{15, nullptr, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, nullptr, "IsWirelessCommunicationEnabled"},
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
{18, nullptr, "GetInternetConnectionStatus"},
{19, nullptr, "SetEthernetCommunicationEnabled"},
{20, nullptr, "IsEthernetCommunicationEnabled"},
{21, nullptr, "IsAnyInternetRequestAccepted"},
{20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
{21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
{22, nullptr, "IsAnyForegroundRequestAccepted"},
{23, nullptr, "PutToSleep"},
{24, nullptr, "WakeUp"},

View File

@@ -18,7 +18,8 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector
}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect) {
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_WARNING(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
@@ -26,7 +27,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
const Tegra::FramebufferConfig framebuffer{
addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform};
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
transform, crop_rect};
Core::System::GetInstance().perf_stats.EndGameFrame();

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/math_util.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
@@ -23,7 +24,8 @@ public:
/// Performs a screen flip, drawing the buffer pointed to by the handle.
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
NVFlinger::BufferQueue::BufferTransformFlags transform);
NVFlinger::BufferQueue::BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
private:
std::shared_ptr<nvmap> nvmap_dev;

View File

@@ -13,8 +13,8 @@ namespace Service {
namespace NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
native_handle->Signal();
buffer_wait_event =
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
@@ -26,10 +26,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
queue.emplace_back(buffer);
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
buffer_wait_event->Signal();
}
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
@@ -48,8 +45,6 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
return boost::none;
}
buffer_wait_event = nullptr;
itr->status = Buffer::Status::Dequeued;
return itr->slot;
}
@@ -62,13 +57,15 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
return itr->igbp_buffer;
}
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform) {
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Dequeued);
itr->status = Buffer::Status::Queued;
itr->transform = transform;
itr->crop_rect = crop_rect;
}
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
@@ -88,9 +85,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
buffer_wait_event->Signal();
}
u32 BufferQueue::Query(QueryType type) {
@@ -106,10 +101,5 @@ u32 BufferQueue::Query(QueryType type) {
return 0;
}
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
buffer_wait_event = std::move(wait_event);
}
} // namespace NVFlinger
} // namespace Service

View File

@@ -6,6 +6,7 @@
#include <vector>
#include <boost/optional.hpp>
#include "common/math_util.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
@@ -68,23 +69,24 @@ public:
Status status = Status::Free;
IGBPBuffer igbp_buffer;
BufferTransformFlags transform;
MathUtil::Rectangle<int> crop_rect;
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform);
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
boost::optional<const Buffer&> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
u32 GetId() const {
return id;
}
Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
return native_handle;
Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const {
return buffer_wait_event;
}
private:
@@ -92,9 +94,6 @@ private:
u64 layer_id;
std::vector<Buffer> queue;
Kernel::SharedPtr<Kernel::Event> native_handle;
/// Used to signal waiting thread when no buffers are available
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
};

View File

@@ -149,12 +149,10 @@ void NVFlinger::Compose() {
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform);
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform,
buffer->crop_rect);
buffer_queue->ReleaseBuffer(buffer->slot);
// TODO(Subv): Figure out when we should actually signal this event.
buffer_queue->GetNativeHandle()->Signal();
}
}

View File

@@ -19,10 +19,9 @@ void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // bsd errno
}
void BSD::Socket(Kernel::HLERequestContext& ctx) {

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <boost/optional.hpp>
#include "common/alignment.h"
#include "common/math_util.h"
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
@@ -27,8 +28,8 @@ struct DisplayInfo {
char display_name[0x40]{"Default"};
u64 unknown_1{1};
u64 unknown_2{1};
u64 width{1920};
u64 height{1080};
u64 width{1280};
u64 height{720};
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
@@ -327,8 +328,8 @@ public:
protected:
void SerializeData() override {
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try
// to read an IGBPBuffer object from the parcel.
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
// try to read an IGBPBuffer object from the parcel.
Write<u32_le>(1);
WriteObject(buffer);
Write<u32_le>(0);
@@ -360,8 +361,8 @@ public:
INSERT_PADDING_WORDS(3);
u32_le timestamp;
s32_le is_auto_timestamp;
s32_le crop_left;
s32_le crop_top;
s32_le crop_left;
s32_le crop_right;
s32_le crop_bottom;
s32_le scaling_mode;
@@ -370,6 +371,10 @@ public:
INSERT_PADDING_WORDS(2);
u32_le fence_is_valid;
std::array<Fence, 2> fences;
MathUtil::Rectangle<int> GetCropRect() const {
return {crop_left, crop_top, crop_right, crop_bottom};
}
};
static_assert(sizeof(Data) == 80, "ParcelData has wrong size");
@@ -495,7 +500,7 @@ private:
ctx.WriteBuffer(response.Serialize());
} else {
// Wait the current thread until a buffer becomes available
auto wait_event = ctx.SleepClientThread(
ctx.SleepClientThread(
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
ThreadWakeupReason reason) {
@@ -506,8 +511,8 @@ private:
ctx.WriteBuffer(response.Serialize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
});
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
},
buffer_queue->GetBufferWaitEvent());
}
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
@@ -519,7 +524,8 @@ private:
} else if (transaction == TransactionId::QueueBuffer) {
IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
buffer_queue->QueueBuffer(request.data.slot, request.data.transform,
request.data.GetCropRect());
IGBPQueueBufferResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
@@ -532,7 +538,7 @@ private:
IGBPQueryResponseParcel response{value};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
} else {
ASSERT_MSG(false, "Unimplemented");
}
@@ -565,7 +571,7 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(buffer_queue->GetNativeHandle());
rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent());
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;

View File

@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/romfs_factory.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -133,6 +134,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
} else {
next_load_addr = load_addr;
}
@@ -151,8 +154,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
// Register the RomFS if a ".romfs" file was found
if (!filepath_romfs.empty()) {
Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
Service::FileSystem::Type::RomFS);
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
}
is_loaded = true;

View File

@@ -7,10 +7,12 @@
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -259,6 +261,8 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
} else {
next_load_addr = load_addr;
}
@@ -273,8 +277,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
metadata.GetMainThreadStackSize());
if (nca->GetRomFsSize() > 0)
Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
Service::FileSystem::Type::RomFS);
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
is_loaded = true;
return ResultStatus::Success;

View File

@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nro.h"
@@ -115,6 +116,9 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
// Register module with GDBStub
GDBStub::RegisterModule(codeset->name, load_base, load_base);
return true;
}

View File

@@ -10,6 +10,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nso.h"
@@ -114,7 +115,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]);
for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j)
for (auto j = 0; j < nso_header.segments_compressed_size[i]; ++j)
compressed_data[j] = file_data[nso_header.segments[i].offset + j];
std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
program_image.resize(nso_header.segments[i].location);
@@ -147,6 +148,9 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
// Register module with GDBStub
GDBStub::RegisterModule(codeset->name, load_base, load_base);
return load_base + image_size;
}

View File

@@ -398,27 +398,6 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
return regs.reg_array[method];
}
bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const {
// The Vertex stage is always enabled.
if (stage == Regs::ShaderStage::Vertex)
return true;
switch (stage) {
case Regs::ShaderStage::TesselationControl:
return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::TesselationControl)]
.enable != 0;
case Regs::ShaderStage::TesselationEval:
return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::TesselationEval)]
.enable != 0;
case Regs::ShaderStage::Geometry:
return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::Geometry)].enable != 0;
case Regs::ShaderStage::Fragment:
return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::Fragment)].enable != 0;
}
UNREACHABLE();
}
void Maxwell3D::ProcessClearBuffers() {
ASSERT(regs.clear_buffers.R == regs.clear_buffers.G &&
regs.clear_buffers.R == regs.clear_buffers.B &&

View File

@@ -379,6 +379,14 @@ public:
}
};
bool IsShaderConfigEnabled(size_t index) const {
// The VertexB is always enabled.
if (index == static_cast<size_t>(Regs::ShaderProgram::VertexB)) {
return true;
}
return shader_config[index].enable != 0;
}
union {
struct {
INSERT_PADDING_WORDS(0x45);
@@ -512,7 +520,18 @@ public:
u32 enable[NumRenderTargets];
} blend;
INSERT_PADDING_WORDS(0xB);
struct {
u32 enable;
u32 front_op_fail;
u32 front_op_zfail;
u32 front_op_zpass;
u32 front_func_func;
u32 front_func_ref;
u32 front_func_mask;
u32 front_mask;
} stencil;
INSERT_PADDING_WORDS(0x3);
union {
BitField<4, 1, u32> triangle_rast_flip;
@@ -548,7 +567,17 @@ public:
}
} tic;
INSERT_PADDING_WORDS(0x21);
INSERT_PADDING_WORDS(0x5);
struct {
u32 enable;
u32 back_op_fail;
u32 back_op_zfail;
u32 back_op_zpass;
u32 back_func_func;
} stencil_two_side;
INSERT_PADDING_WORDS(0x17);
union {
BitField<2, 1, u32> coord_origin;
@@ -780,9 +809,6 @@ public:
/// Returns the texture information for a specific texture in a specific shader stage.
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
/// Returns whether the specified shader stage is enabled or not.
bool IsShaderStageEnabled(Regs::ShaderStage stage) const;
private:
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
@@ -846,10 +872,12 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
ASSERT_REG_POSITION(blend, 0x4CF);
ASSERT_REG_POSITION(stencil, 0x4E0);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(stencil_two_side, 0x565);
ASSERT_REG_POSITION(point_coord_replace, 0x581);
ASSERT_REG_POSITION(code_address, 0x582);
ASSERT_REG_POSITION(draw, 0x585);

View File

@@ -142,6 +142,7 @@ enum class PredCondition : u64 {
GreaterThan = 4,
NotEqual = 5,
GreaterEqual = 6,
LessThanWithNan = 9,
NotEqualWithNan = 13,
// TODO(Subv): Other condition types
};
@@ -201,6 +202,11 @@ enum class IMinMaxExchange : u64 {
XHi = 3,
};
enum class FlowCondition : u64 {
Always = 0xF,
Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
};
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -297,6 +303,13 @@ union Instruction {
BitField<56, 1, u64> negate_a;
} iadd32i;
union {
BitField<53, 1, u64> negate_b;
BitField<54, 1, u64> abs_a;
BitField<56, 1, u64> negate_a;
BitField<57, 1, u64> abs_b;
} fadd32i;
union {
BitField<20, 8, u64> shift_position;
BitField<28, 8, u64> shift_length;
@@ -308,6 +321,10 @@ union Instruction {
}
} bfe;
union {
BitField<0, 5, FlowCondition> cond;
} flow;
union {
BitField<48, 1, u64> negate_b;
BitField<49, 1, u64> negate_c;
@@ -487,6 +504,7 @@ public:
FADD_C,
FADD_R,
FADD_IMM,
FADD32I,
FMUL_C,
FMUL_R,
FMUL_IMM,
@@ -679,13 +697,14 @@ private:
INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"),
INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"),
INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"),
INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"),
INST("000010----------", Id::FADD32I, Type::ArithmeticImmediate, "FADD32I"),
INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),

View File

@@ -67,6 +67,7 @@ struct FramebufferConfig {
using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
TransformFlags transform_flags;
MathUtil::Rectangle<int> crop_rect;
};
namespace Engines {

View File

@@ -15,6 +15,7 @@
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/hle/kernel/process.h"
#include "core/settings.h"
#include "video_core/engines/maxwell_3d.h"
@@ -22,6 +23,7 @@
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/maxwell_to_gl.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h"
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using PixelFormat = SurfaceParams::PixelFormat;
@@ -181,6 +183,19 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
return {array_ptr, buffer_offset};
}
static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program) {
auto& gpu = Core::System().GetInstance().GPU().Maxwell3D();
// Fetch program code from memory
GLShader::ProgramCode program_code;
auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)];
const u64 gpu_address{gpu.regs.code_address.CodeAddress() + shader_config.offset};
const boost::optional<VAddr> cpu_address{gpu.memory_manager.GpuToCpuAddress(gpu_address)};
Memory::ReadBlock(*cpu_address, program_code.data(), program_code.size() * sizeof(u64));
return program_code;
}
void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
// Helper function for uploading uniform data
const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) {
@@ -193,26 +208,23 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
};
auto& gpu = Core::System().GetInstance().GPU().Maxwell3D();
ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!");
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
u32 current_constbuffer_bindpoint = uniform_buffers.size();
u32 current_texture_bindpoint = 0;
for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) {
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
auto& shader_config = gpu.regs.shader_config[index];
const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)};
const auto& stage = index - 1; // Stage indices are 0 - 5
const bool is_enabled = gpu.IsShaderStageEnabled(static_cast<Maxwell::ShaderStage>(stage));
// Skip stages that are not enabled
if (!is_enabled) {
if (!gpu.regs.IsShaderConfigEnabled(index)) {
continue;
}
const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
GLShader::MaxwellUniformData ubo{};
ubo.SetFromRegs(gpu.state.shader_stages[stage]);
std::memcpy(buffer_ptr, &ubo, sizeof(ubo));
@@ -228,16 +240,21 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
buffer_ptr += sizeof(GLShader::MaxwellUniformData);
buffer_offset += sizeof(GLShader::MaxwellUniformData);
// Fetch program code from memory
GLShader::ProgramCode program_code;
const u64 gpu_address{gpu.regs.code_address.CodeAddress() + shader_config.offset};
const boost::optional<VAddr> cpu_address{gpu.memory_manager.GpuToCpuAddress(gpu_address)};
Memory::ReadBlock(*cpu_address, program_code.data(), program_code.size() * sizeof(u64));
GLShader::ShaderSetup setup{std::move(program_code)};
GLShader::ShaderSetup setup{GetShaderProgramCode(program)};
GLShader::ShaderEntries shader_resources;
switch (program) {
case Maxwell::ShaderProgram::VertexA: {
// VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders.
// Conventional HW does not support this, so we combine VertexA and VertexB into one
// stage here.
setup.SetProgramB(GetShaderProgramCode(Maxwell::ShaderProgram::VertexB));
GLShader::MaxwellVSConfig vs_config{setup};
shader_resources =
shader_program_manager->UseProgrammableVertexShader(vs_config, setup);
break;
}
case Maxwell::ShaderProgram::VertexB: {
GLShader::MaxwellVSConfig vs_config{setup};
shader_resources =
@@ -268,6 +285,12 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
current_texture_bindpoint =
SetupTextures(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program,
current_texture_bindpoint, shader_resources.texture_samplers);
// When VertexA is enabled, we have dual vertex shaders
if (program == Maxwell::ShaderProgram::VertexA) {
// VertexB was combined with VertexA, so we skip the VertexB iteration
index++;
}
}
shader_program_manager->UseTrivialGeometryShader();
@@ -301,9 +324,6 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
bool using_depth_fb) {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
// Sync the depth test state before configuring the framebuffer surfaces.
SyncDepthTestState();
// TODO(bunnei): Implement this
const bool has_stencil = false;
@@ -368,11 +388,20 @@ void RasterizerOpenGL::Clear() {
if (regs.clear_buffers.Z) {
clear_mask |= GL_DEPTH_BUFFER_BIT;
use_depth_fb = true;
// Always enable the depth write when clearing the depth buffer. The depth write mask is
// ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true.
state.depth.test_enabled = true;
state.depth.write_mask = GL_TRUE;
state.depth.test_func = GL_ALWAYS;
state.Apply();
}
if (clear_mask == 0)
return;
ScopeAcquireGLContext acquire_context;
auto [dirty_color_surface, dirty_depth_surface] =
ConfigureFramebuffers(use_color_fb, use_depth_fb);
@@ -399,9 +428,12 @@ void RasterizerOpenGL::DrawArrays() {
MICROPROFILE_SCOPE(OpenGL_Drawing);
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
ScopeAcquireGLContext acquire_context;
auto [dirty_color_surface, dirty_depth_surface] =
ConfigureFramebuffers(true, regs.zeta.Address() != 0);
SyncDepthTestState();
SyncBlendState();
SyncCullMode();
@@ -605,9 +637,6 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
auto& gpu = Core::System::GetInstance().GPU();
auto& maxwell3d = gpu.Get3DEngine();
ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage),
"Attempted to upload constbuffer of disabled shader stage");
// Reset all buffer draw state for this stage.
for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) {
buffer.bindpoint = 0;
@@ -674,9 +703,6 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program,
auto& gpu = Core::System::GetInstance().GPU();
auto& maxwell3d = gpu.Get3DEngine();
ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage),
"Attempted to upload textures of disabled shader stage");
ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
"Exceeded the number of active textures.");

View File

@@ -105,6 +105,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // BC7U
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8
// DepthStencil formats
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
@@ -112,6 +113,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
false}, // S8Z24
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, ComponentType::UNorm,
false}, // Z16
}};
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
@@ -194,8 +197,9 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::Z24S8>,
MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::Z16>,
};
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
@@ -215,10 +219,12 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::ABGR8>,
nullptr,
MortonCopy<false, PixelFormat::G8R8>,
MortonCopy<false, PixelFormat::Z24S8>,
MortonCopy<false, PixelFormat::S8Z24>,
MortonCopy<false, PixelFormat::Z32F>,
MortonCopy<false, PixelFormat::Z16>,
};
// Allocate an uninitialized texture of appropriate size and format for the surface
@@ -271,9 +277,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
S8Z24 input_pixel{};
Z24S8 output_pixel{};
const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
const size_t offset{y * width + x};
const size_t offset{bpp * (y * width + x)};
std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24));
output_pixel.s8.Assign(input_pixel.s8);
output_pixel.z24.Assign(input_pixel.z24);
@@ -281,6 +288,19 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
}
}
}
static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8)};
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
const size_t offset{bpp * (y * width + x)};
const u8 temp{data[offset]};
data[offset] = data[offset + 1];
data[offset + 1] = temp;
}
}
}
/**
* Helper function to perform software conversion (as needed) when loading a buffer from Switch
* memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
@@ -301,6 +321,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
// Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
ConvertS8Z24ToZ24S8(data, width, height);
break;
case PixelFormat::G8R8:
// Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8.
ConvertG8R8ToR8G8(data, width, height);
break;
}
}

View File

@@ -37,13 +37,15 @@ struct SurfaceParams {
DXN1 = 11, // This is also known as BC4
BC7U = 12,
ASTC_2D_4X4 = 13,
G8R8 = 14,
MaxColorFormat,
// DepthStencil formats
Z24S8 = 14,
S8Z24 = 15,
Z32F = 16,
Z24S8 = 15,
S8Z24 = 16,
Z32F = 17,
Z16 = 18,
MaxDepthStencilFormat,
@@ -95,9 +97,11 @@ struct SurfaceParams {
4, // DXN1
4, // BC7U
4, // ASTC_2D_4X4
1, // G8R8
1, // Z24S8
1, // S8Z24
1, // Z32F
1, // Z16
}};
ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
@@ -123,9 +127,11 @@ struct SurfaceParams {
64, // DXN1
128, // BC7U
32, // ASTC_2D_4X4
16, // G8R8
32, // Z24S8
32, // S8Z24
32, // Z32F
16, // Z16
}};
ASSERT(static_cast<size_t>(format) < bpp_table.size());
@@ -143,6 +149,8 @@ struct SurfaceParams {
return PixelFormat::Z24S8;
case Tegra::DepthFormat::Z32_FLOAT:
return PixelFormat::Z32F;
case Tegra::DepthFormat::Z16_UNORM:
return PixelFormat::Z16;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
@@ -181,6 +189,8 @@ struct SurfaceParams {
return PixelFormat::A1B5G5R5;
case Tegra::Texture::TextureFormat::R8:
return PixelFormat::R8;
case Tegra::Texture::TextureFormat::G8R8:
return PixelFormat::G8R8;
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
return PixelFormat::RGBA16F;
case Tegra::Texture::TextureFormat::BF10GF11RF11:
@@ -218,6 +228,8 @@ struct SurfaceParams {
return Tegra::Texture::TextureFormat::A1B5G5R5;
case PixelFormat::R8:
return Tegra::Texture::TextureFormat::R8;
case PixelFormat::G8R8:
return Tegra::Texture::TextureFormat::G8R8;
case PixelFormat::RGBA16F:
return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
case PixelFormat::R11FG11FB10F:
@@ -249,6 +261,8 @@ struct SurfaceParams {
return Tegra::DepthFormat::Z24_S8_UNORM;
case PixelFormat::Z32F:
return Tegra::DepthFormat::Z32_FLOAT;
case PixelFormat::Z16:
return Tegra::DepthFormat::Z16_UNORM;
default:
UNREACHABLE();
}
@@ -295,6 +309,7 @@ struct SurfaceParams {
static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::Z16_UNORM:
case Tegra::DepthFormat::S8_Z24_UNORM:
case Tegra::DepthFormat::Z24_S8_UNORM:
return ComponentType::UNorm;

View File

@@ -42,13 +42,14 @@ enum class ExitMethod {
struct Subroutine {
/// Generates a name suitable for GLSL source code.
std::string GetName() const {
return "sub_" + std::to_string(begin) + '_' + std::to_string(end);
return "sub_" + std::to_string(begin) + '_' + std::to_string(end) + '_' + suffix;
}
u32 begin; ///< Entry point of the subroutine.
u32 end; ///< Return point of the subroutine.
ExitMethod exit_method; ///< Exit method of the subroutine.
std::set<u32> labels; ///< Addresses refereced by JMP instructions.
u32 begin; ///< Entry point of the subroutine.
u32 end; ///< Return point of the subroutine.
const std::string& suffix; ///< Suffix of the shader, used to make a unique subroutine name
ExitMethod exit_method; ///< Exit method of the subroutine.
std::set<u32> labels; ///< Addresses refereced by JMP instructions.
bool operator<(const Subroutine& rhs) const {
return std::tie(begin, end) < std::tie(rhs.begin, rhs.end);
@@ -58,11 +59,11 @@ struct Subroutine {
/// Analyzes shader code and produces a set of subroutines.
class ControlFlowAnalyzer {
public:
ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset)
ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix)
: program_code(program_code) {
// Recursively finds all subroutines.
const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END);
const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix);
if (program_main.exit_method != ExitMethod::AlwaysEnd)
throw DecompileFail("Program does not always end");
}
@@ -77,12 +78,12 @@ private:
std::map<std::pair<u32, u32>, ExitMethod> exit_method_map;
/// Adds and analyzes a new subroutine if it is not added yet.
const Subroutine& AddSubroutine(u32 begin, u32 end) {
auto iter = subroutines.find(Subroutine{begin, end});
const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) {
auto iter = subroutines.find(Subroutine{begin, end, suffix});
if (iter != subroutines.end())
return *iter;
Subroutine subroutine{begin, end};
Subroutine subroutine{begin, end, suffix};
subroutine.exit_method = Scan(begin, end, subroutine.labels);
if (subroutine.exit_method == ExitMethod::Undetermined)
throw DecompileFail("Recursive function detected");
@@ -191,7 +192,8 @@ public:
UnsignedInteger,
};
GLSLRegister(size_t index, ShaderWriter& shader) : index{index}, shader{shader} {}
GLSLRegister(size_t index, ShaderWriter& shader, const std::string& suffix)
: index{index}, shader{shader}, suffix{suffix} {}
/// Gets the GLSL type string for a register
static std::string GetTypeString(Type type) {
@@ -216,7 +218,7 @@ public:
/// Returns a GLSL string representing the current state of the register
const std::string GetActiveString() {
declr_type.insert(active_type);
return GetPrefixString(active_type) + std::to_string(index);
return GetPrefixString(active_type) + std::to_string(index) + '_' + suffix;
}
/// Returns true if the active type is a float
@@ -251,6 +253,7 @@ private:
ShaderWriter& shader;
Type active_type{Type::Float};
std::set<Type> declr_type;
const std::string& suffix;
};
/**
@@ -262,8 +265,8 @@ private:
class GLSLRegisterManager {
public:
GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
const Maxwell3D::Regs::ShaderStage& stage)
: shader{shader}, declarations{declarations}, stage{stage} {
const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix)
: shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} {
BuildRegisterList();
}
@@ -430,12 +433,12 @@ public:
}
/// Add declarations for registers
void GenerateDeclarations() {
void GenerateDeclarations(const std::string& suffix) {
for (const auto& reg : regs) {
for (const auto& type : reg.DeclaredTypes()) {
declarations.AddLine(GLSLRegister::GetTypeString(type) + ' ' +
GLSLRegister::GetPrefixString(type) +
std::to_string(reg.GetIndex()) + " = 0;");
reg.GetPrefixString(type) + std::to_string(reg.GetIndex()) +
'_' + suffix + " = 0;");
}
}
declarations.AddNewLine();
@@ -558,7 +561,7 @@ private:
/// Build the GLSL register list.
void BuildRegisterList() {
for (size_t index = 0; index < Register::NumRegisters; ++index) {
regs.emplace_back(index, shader);
regs.emplace_back(index, shader, suffix);
}
}
@@ -620,16 +623,17 @@ private:
std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;
std::vector<SamplerEntry> used_samplers;
const Maxwell3D::Regs::ShaderStage& stage;
const std::string& suffix;
};
class GLSLGenerator {
public:
GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
u32 main_offset, Maxwell3D::Regs::ShaderStage stage)
u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix)
: subroutines(subroutines), program_code(program_code), main_offset(main_offset),
stage(stage) {
stage(stage), suffix(suffix) {
Generate();
Generate(suffix);
}
std::string GetShaderCode() {
@@ -644,7 +648,7 @@ public:
private:
/// Gets the Subroutine object corresponding to the specified address.
const Subroutine& GetSubroutine(u32 begin, u32 end) const {
auto iter = subroutines.find(Subroutine{begin, end});
auto iter = subroutines.find(Subroutine{begin, end, suffix});
ASSERT(iter != subroutines.end());
return *iter;
}
@@ -689,7 +693,7 @@ private:
// Can't assign to the constant predicate.
ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
std::string variable = 'p' + std::to_string(pred);
std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
shader.AddLine(variable + " = " + value + ';');
declr_predicates.insert(std::move(variable));
}
@@ -707,7 +711,7 @@ private:
if (index == static_cast<u64>(Pred::UnusedIndex))
variable = "true";
else
variable = 'p' + std::to_string(index);
variable = 'p' + std::to_string(index) + '_' + suffix;
if (negate) {
return "!(" + variable + ')';
@@ -728,10 +732,10 @@ private:
const std::string& op_a, const std::string& op_b) const {
using Tegra::Shader::PredCondition;
static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
{PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
{PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
{PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="},
{PredCondition::NotEqualWithNan, "!="},
{PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
{PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
{PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="},
{PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="},
};
const auto& comparison{PredicateComparisonStrings.find(condition)};
@@ -739,7 +743,8 @@ private:
"Unknown predicate comparison operation");
std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'};
if (condition == PredCondition::NotEqualWithNan) {
if (condition == PredCondition::LessThanWithNan ||
condition == PredCondition::NotEqualWithNan) {
predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')';
}
@@ -968,6 +973,29 @@ private:
regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
break;
}
case OpCode::Id::FADD32I: {
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
std::string op_b = GetImmediate32(instr);
if (instr.fadd32i.abs_a) {
op_a = "abs(" + op_a + ')';
}
if (instr.fadd32i.negate_a) {
op_a = "-(" + op_a + ')';
}
if (instr.fadd32i.abs_b) {
op_b = "abs(" + op_b + ')';
}
if (instr.fadd32i.negate_b) {
op_b = "-(" + op_b + ')';
}
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1);
break;
}
}
break;
}
@@ -1616,16 +1644,32 @@ private:
shader.AddLine("color.a = " + regs.GetRegisterAsFloat(3) + ';');
}
shader.AddLine("return true;");
if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
// If this is an unconditional exit then just end processing here, otherwise
// we have to account for the possibility of the condition not being met, so
// continue processing the next instruction.
offset = PROGRAM_END - 1;
switch (instr.flow.cond) {
case Tegra::Shader::FlowCondition::Always:
shader.AddLine("return true;");
if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
// If this is an unconditional exit then just end processing here,
// otherwise we have to account for the possibility of the condition
// not being met, so continue processing the next instruction.
offset = PROGRAM_END - 1;
}
break;
case Tegra::Shader::FlowCondition::Fcsm_Tr:
// TODO(bunnei): What is this used for? If we assume this conditon is not
// satisifed, dual vertex shaders in Farming Simulator make more sense
LOG_CRITICAL(HW_GPU, "Skipping unknown FlowCondition::Fcsm_Tr");
break;
default:
LOG_CRITICAL(HW_GPU, "Unhandled flow condition: {}",
static_cast<u32>(instr.flow.cond.Value()));
UNREACHABLE();
}
break;
}
case OpCode::Id::KIL: {
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
shader.AddLine("discard;");
break;
}
@@ -1646,8 +1690,9 @@ private:
// can ignore this when generating GLSL code.
break;
}
case OpCode::Id::DEPBAR:
case OpCode::Id::SYNC: {
case OpCode::Id::SYNC:
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
case OpCode::Id::DEPBAR: {
// TODO(Subv): Find out if we actually have to care about these instructions or if
// the GLSL compiler takes care of that for us.
LOG_WARNING(HW_GPU, "DEPBAR/SYNC instruction is stubbed");
@@ -1687,7 +1732,7 @@ private:
return program_counter;
}
void Generate() {
void Generate(const std::string& suffix) {
// Add declarations for all subroutines
for (const auto& subroutine : subroutines) {
shader.AddLine("bool " + subroutine.GetName() + "();");
@@ -1695,7 +1740,7 @@ private:
shader.AddNewLine();
// Add the main entry point
shader.AddLine("bool exec_shader() {");
shader.AddLine("bool exec_" + suffix + "() {");
++shader.scope;
CallSubroutine(GetSubroutine(main_offset, PROGRAM_END));
--shader.scope;
@@ -1758,7 +1803,7 @@ private:
/// Add declarations for registers
void GenerateDeclarations() {
regs.GenerateDeclarations();
regs.GenerateDeclarations(suffix);
for (const auto& pred : declr_predicates) {
declarations.AddLine("bool " + pred + " = false;");
@@ -1771,27 +1816,30 @@ private:
const ProgramCode& program_code;
const u32 main_offset;
Maxwell3D::Regs::ShaderStage stage;
const std::string& suffix;
ShaderWriter shader;
ShaderWriter declarations;
GLSLRegisterManager regs{shader, declarations, stage};
GLSLRegisterManager regs{shader, declarations, stage, suffix};
// Declarations
std::set<std::string> declr_predicates;
}; // namespace Decompiler
std::string GetCommonDeclarations() {
std::string declarations = "bool exec_shader();\n";
std::string declarations;
declarations += "#define MAX_CONSTBUFFER_ELEMENTS " +
std::to_string(RasterizerOpenGL::MaxConstbufferSize / (sizeof(GLvec4)));
declarations += '\n';
return declarations;
}
boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
Maxwell3D::Regs::ShaderStage stage) {
Maxwell3D::Regs::ShaderStage stage,
const std::string& suffix) {
try {
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines();
GLSLGenerator generator(subroutines, program_code, main_offset, stage);
auto subroutines = ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix);
return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
} catch (const DecompileFail& exception) {
LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());

View File

@@ -20,7 +20,8 @@ using Tegra::Engines::Maxwell3D;
std::string GetCommonDeclarations();
boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
Maxwell3D::Regs::ShaderStage stage);
Maxwell3D::Regs::ShaderStage stage,
const std::string& suffix);
} // namespace Decompiler
} // namespace GLShader

View File

@@ -17,10 +17,17 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConf
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
out += "bool exec_vertex();\n";
if (setup.IsDualProgram()) {
out += "bool exec_vertex_b();\n";
}
ProgramResult program =
Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
.get_value_or({});
ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Vertex)
.get_value_or({});
out += R"(
out gl_PerVertex {
@@ -34,7 +41,14 @@ layout (std140) uniform vs_config {
};
void main() {
exec_shader();
exec_vertex();
)";
if (setup.IsDualProgram()) {
out += " exec_vertex_b();";
}
out += R"(
// Viewport can be flipped, which is unsupported by glViewport
position.xy *= viewport_flip.xy;
@@ -44,8 +58,19 @@ void main() {
// For now, this is here to bring order in lieu of proper emulation
position.w = 1.0;
}
)";
out += program.first;
if (setup.IsDualProgram()) {
ProgramResult program_b =
Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
.get_value_or({});
out += program_b.first;
}
return {out, program.second};
}
@@ -53,12 +78,13 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSCo
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
out += "bool exec_fragment();\n";
ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Fragment)
.get_value_or({});
ProgramResult program =
Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
.get_value_or({});
out += R"(
in vec4 position;
out vec4 color;
@@ -67,7 +93,7 @@ layout (std140) uniform fs_config {
};
void main() {
exec_shader();
exec_fragment();
}
)";

View File

@@ -115,21 +115,48 @@ struct ShaderEntries {
using ProgramResult = std::pair<std::string, ShaderEntries>;
struct ShaderSetup {
ShaderSetup(ProgramCode&& program_code) : program_code(std::move(program_code)) {}
ShaderSetup(const ProgramCode& program_code) {
program.code = program_code;
}
struct {
ProgramCode code;
ProgramCode code_b; // Used for dual vertex shaders
} program;
ProgramCode program_code;
bool program_code_hash_dirty = true;
u64 GetProgramCodeHash() {
if (program_code_hash_dirty) {
program_code_hash = Common::ComputeHash64(&program_code, sizeof(program_code));
program_code_hash = GetNewHash();
program_code_hash_dirty = false;
}
return program_code_hash;
}
/// Used in scenarios where we have a dual vertex shaders
void SetProgramB(const ProgramCode& program_b) {
program.code_b = program_b;
has_program_b = true;
}
bool IsDualProgram() const {
return has_program_b;
}
private:
u64 GetNewHash() const {
if (has_program_b) {
// Compute hash over dual shader programs
return Common::ComputeHash64(&program, sizeof(program));
} else {
// Compute hash over a single shader program
return Common::ComputeHash64(&program.code, program.code.size());
}
}
u64 program_code_hash{};
bool has_program_b{};
};
struct MaxwellShaderConfigCommon {

View File

@@ -92,11 +92,24 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
return matrix;
}
ScopeAcquireGLContext::ScopeAcquireGLContext() {
if (Settings::values.use_multi_core) {
VideoCore::g_emu_window->MakeCurrent();
}
}
ScopeAcquireGLContext::~ScopeAcquireGLContext() {
if (Settings::values.use_multi_core) {
VideoCore::g_emu_window->DoneCurrent();
}
}
RendererOpenGL::RendererOpenGL() = default;
RendererOpenGL::~RendererOpenGL() = default;
/// Swap buffers (render frame)
void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) {
ScopeAcquireGLContext acquire_context;
Core::System::GetInstance().perf_stats.EndSystemFrame();
// Maintain the rasterizer's state as a priority
@@ -141,6 +154,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
// Framebuffer orientation handling
framebuffer_transform_flags = framebuffer.transform_flags;
framebuffer_crop_rect = framebuffer.crop_rect;
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
// only allows rows to have a memory alignement of 4.
@@ -307,11 +321,24 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
}
}
ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented");
ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
// Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
// (e.g. handheld mode) on a 1920x1080 framebuffer.
f32 scale_u = 1.f, scale_v = 1.f;
if (framebuffer_crop_rect.GetWidth() > 0) {
scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / screen_info.texture.width;
}
if (framebuffer_crop_rect.GetHeight() > 0) {
scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / screen_info.texture.height;
}
std::array<ScreenRectVertex, 4> vertices = {{
ScreenRectVertex(x, y, texcoords.top, left),
ScreenRectVertex(x + w, y, texcoords.bottom, left),
ScreenRectVertex(x, y + h, texcoords.top, right),
ScreenRectVertex(x + w, y + h, texcoords.bottom, right),
ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v),
ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v),
ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v),
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
}};
state.texture_units[0].texture_2d = screen_info.display_texture;
@@ -418,7 +445,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
/// Initialize the renderer
bool RendererOpenGL::Init() {
render_window->MakeCurrent();
ScopeAcquireGLContext acquire_context;
if (GLAD_GL_KHR_debug) {
glEnable(GL_DEBUG_OUTPUT);

View File

@@ -31,6 +31,13 @@ struct ScreenInfo {
TextureInfo texture;
};
/// Helper class to acquire/release OpenGL context within a given scope
class ScopeAcquireGLContext : NonCopyable {
public:
ScopeAcquireGLContext();
~ScopeAcquireGLContext();
};
class RendererOpenGL : public RendererBase {
public:
RendererOpenGL();
@@ -90,4 +97,5 @@ private:
/// Used for transforming the framebuffer orientation
Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
MathUtil::Rectangle<int> framebuffer_crop_rect;
};

View File

@@ -62,6 +62,7 @@ u32 BytesPerPixel(TextureFormat format) {
return 4;
case TextureFormat::A1B5G5R5:
case TextureFormat::B5G6R5:
case TextureFormat::G8R8:
return 2;
case TextureFormat::R8:
return 1;
@@ -77,6 +78,8 @@ u32 BytesPerPixel(TextureFormat format) {
static u32 DepthBytesPerPixel(DepthFormat format) {
switch (format) {
case DepthFormat::Z16_UNORM:
return 2;
case DepthFormat::S8_Z24_UNORM:
case DepthFormat::Z24_S8_UNORM:
case DepthFormat::Z32_FLOAT:
@@ -110,6 +113,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
case TextureFormat::A1B5G5R5:
case TextureFormat::B5G6R5:
case TextureFormat::R8:
case TextureFormat::G8R8:
case TextureFormat::R16_G16_B16_A16:
case TextureFormat::R32_G32_B32_A32:
case TextureFormat::BF10GF11RF11:
@@ -133,6 +137,7 @@ std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 wid
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
switch (format) {
case DepthFormat::Z16_UNORM:
case DepthFormat::S8_Z24_UNORM:
case DepthFormat::Z24_S8_UNORM:
case DepthFormat::Z32_FLOAT:
@@ -164,6 +169,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::A1B5G5R5:
case TextureFormat::B5G6R5:
case TextureFormat::R8:
case TextureFormat::G8R8:
case TextureFormat::BF10GF11RF11:
case TextureFormat::R32_G32_B32_A32:
// TODO(Subv): For the time being just forward the same data without any decoding.

View File

@@ -20,7 +20,10 @@
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
void EmuThread::run() {
render_window->MakeCurrent();
if (!Settings::values.use_multi_core) {
// Single core mode must acquire OpenGL context for entire emulation session
render_window->MakeCurrent();
}
MicroProfileOnThreadCreate("EmuThread");

View File

@@ -97,7 +97,7 @@ void Config::ReadValues() {
qt_config->endGroup();
qt_config->beginGroup("System");
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", true).toBool();
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("Miscellaneous");

View File

@@ -14,6 +14,13 @@
namespace Debugger {
void ToggleConsole() {
static bool console_shown = false;
if (console_shown == UISettings::values.show_console) {
return;
} else {
console_shown = UISettings::values.show_console;
}
#if defined(_WIN32) && !defined(_DEBUG)
FILE* temp;
if (UISettings::values.show_console) {

View File

@@ -374,6 +374,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
render_window->DoneCurrent();
if (result != Core::System::ResultStatus::Success) {
switch (result) {
case Core::System::ResultStatus::ErrorGetLoader:
@@ -908,8 +910,6 @@ void GMainWindow::UpdateUITheme() {
#endif
int main(int argc, char* argv[]) {
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -918,6 +918,7 @@ int main(int argc, char* argv[]) {
QCoreApplication::setApplicationName("yuzu");
QApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
QApplication app(argc, argv);
// Qt changes the locale and causes issues in float conversion using std::to_string() when

View File

@@ -110,7 +110,7 @@ void Config::ReadValues() {
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", true);
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
// Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");

View File

@@ -163,7 +163,7 @@ use_virtual_sd =
[System]
# Whether the system is docked
# 1 (default): Yes, 0: No
# 1: Yes, 0 (default): No
use_docked_mode =
# The system region that yuzu will use during emulation

View File

@@ -126,7 +126,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (render_window == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting...");
LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError());
exit(1);
}
@@ -137,12 +137,12 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
gl_context = SDL_GL_CreateContext(render_window);
if (gl_context == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting...");
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError());
exit(1);
}
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting...");
LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
exit(1);
}

View File

@@ -22,10 +22,8 @@
#include "yuzu_cmd/config.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#ifdef _MSC_VER
#include <getopt.h>
#else
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
@@ -127,6 +125,7 @@ int main(int argc, char** argv) {
#endif
Log::Filter log_filter(Log::Level::Debug);
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
@@ -142,8 +141,6 @@ int main(int argc, char** argv) {
return -1;
}
log_filter.ParseFilterString(Settings::values.log_filter);
// Apply the command line arguments
Settings::values.gdbstub_port = gdb_port;
Settings::values.use_gdbstub = use_gdbstub;
@@ -151,6 +148,11 @@ int main(int argc, char** argv) {
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)};
if (!Settings::values.use_multi_core) {
// Single core mode must acquire OpenGL context for entire emulation session
emu_window->MakeCurrent();
}
Core::System& system{Core::System::GetInstance()};
SCOPE_EXIT({ system.Shutdown(); });