Compare commits
74 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
523e4be02c | ||
|
|
fde3b1b6f2 | ||
|
|
93a4097e9d | ||
|
|
c4ce7e456a | ||
|
|
e33452f7e8 | ||
|
|
5aaee2ff8d | ||
|
|
2ae88feea7 | ||
|
|
16db8b9d9f | ||
|
|
948002635f | ||
|
|
ea99819f37 | ||
|
|
eac3cf301c | ||
|
|
fc5b489b0f | ||
|
|
19b05c3f55 | ||
|
|
2788144f46 | ||
|
|
dd70ddad7e | ||
|
|
c0fb321935 | ||
|
|
34f3d58470 | ||
|
|
b5fb246a99 | ||
|
|
c6fda4c758 | ||
|
|
609cb04f3f | ||
|
|
eb88fedc5d | ||
|
|
f5b132676f | ||
|
|
0fcdf37917 | ||
|
|
350f6e0aa4 | ||
|
|
9d8f19d7bf | ||
|
|
38cd4e9c61 | ||
|
|
f9a26d468c | ||
|
|
1277556c69 | ||
|
|
dfdf4a46fe | ||
|
|
69dd37d874 | ||
|
|
f13a66b963 | ||
|
|
b1d238bbb8 | ||
|
|
7784ce1854 | ||
|
|
e8cb6f5c9b | ||
|
|
9e9a4bb3a7 | ||
|
|
028d90eb79 | ||
|
|
296e57fa0e | ||
|
|
b20ed93884 | ||
|
|
185b35bfcd | ||
|
|
943771e703 | ||
|
|
ce4b77bd7d | ||
|
|
6ee8b15abe | ||
|
|
ffd60ee476 | ||
|
|
8a88110060 | ||
|
|
6cf719a4ab | ||
|
|
7fb406c3fc | ||
|
|
3ef4b3d4b4 | ||
|
|
73b937b190 | ||
|
|
656758fd81 | ||
|
|
27da7bc9da | ||
|
|
e0f66c1fbf | ||
|
|
60f476cd8f | ||
|
|
10f494eefe | ||
|
|
448290bee4 | ||
|
|
2592e41301 | ||
|
|
0b6f8ba51e | ||
|
|
d0b2950434 | ||
|
|
42431d2aa6 | ||
|
|
b8e70faa2d | ||
|
|
662218e997 | ||
|
|
c3013c7c9c | ||
|
|
acff922762 | ||
|
|
dfea525cbe | ||
|
|
82fa0bcea7 | ||
|
|
4fa4d80c68 | ||
|
|
6aa8ee6943 | ||
|
|
b76ddb7647 | ||
|
|
2a3b335b15 | ||
|
|
4e1471ef21 | ||
|
|
e9978fd4f5 | ||
|
|
75169c7570 | ||
|
|
03d7faf583 | ||
|
|
6f691e71bf | ||
|
|
5f8d253ce0 |
@@ -53,7 +53,7 @@ build_script:
|
||||
# https://www.appveyor.com/docs/build-phase
|
||||
msbuild msvc_build/yuzu.sln /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -j4 -C mingw_build/ 2>&1'
|
||||
C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -C mingw_build/ 2>&1'
|
||||
}
|
||||
|
||||
after_build:
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 550d662f0e...a42f301c28
@@ -3,8 +3,15 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/telemetry.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#endif
|
||||
|
||||
namespace Telemetry {
|
||||
|
||||
void FieldCollection::Accept(VisitorInterface& visitor) const {
|
||||
@@ -37,4 +44,62 @@ template class Field<std::string>;
|
||||
template class Field<const char*>;
|
||||
template class Field<std::chrono::microseconds>;
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
static const char* CpuVendorToStr(Common::CPUVendor vendor) {
|
||||
switch (vendor) {
|
||||
case Common::CPUVendor::INTEL:
|
||||
return "Intel";
|
||||
case Common::CPUVendor::AMD:
|
||||
return "Amd";
|
||||
case Common::CPUVendor::OTHER:
|
||||
return "Other";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
void AppendBuildInfo(FieldCollection& fc) {
|
||||
const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr};
|
||||
fc.AddField(FieldType::App, "Git_IsDirty", is_git_dirty);
|
||||
fc.AddField(FieldType::App, "Git_Branch", Common::g_scm_branch);
|
||||
fc.AddField(FieldType::App, "Git_Revision", Common::g_scm_rev);
|
||||
fc.AddField(FieldType::App, "BuildDate", Common::g_build_date);
|
||||
fc.AddField(FieldType::App, "BuildName", Common::g_build_name);
|
||||
}
|
||||
|
||||
void AppendCPUInfo(FieldCollection& fc) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Vendor", CpuVendorToStr(Common::GetCPUCaps().vendor));
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2);
|
||||
#else
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppendOSInfo(FieldCollection& fc) {
|
||||
#ifdef __APPLE__
|
||||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Apple");
|
||||
#elif defined(_WIN32)
|
||||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Windows");
|
||||
#elif defined(__linux__) || defined(linux) || defined(__linux)
|
||||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Linux");
|
||||
#else
|
||||
fc.AddField(FieldType::UserSystem, "OsPlatform", "Unknown");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Telemetry
|
||||
|
||||
@@ -180,4 +180,16 @@ struct NullVisitor : public VisitorInterface {
|
||||
void Complete() override {}
|
||||
};
|
||||
|
||||
/// Appends build-specific information to the given FieldCollection,
|
||||
/// such as branch name, revision hash, etc.
|
||||
void AppendBuildInfo(FieldCollection& fc);
|
||||
|
||||
/// Appends CPU-specific information to the given FieldCollection,
|
||||
/// such as instruction set extensions, etc.
|
||||
void AppendCPUInfo(FieldCollection& fc);
|
||||
|
||||
/// Appends OS-specific information to the given FieldCollection,
|
||||
/// such as platform name, etc.
|
||||
void AppendOSInfo(FieldCollection& fc);
|
||||
|
||||
} // namespace Telemetry
|
||||
|
||||
@@ -122,6 +122,8 @@ add_library(core STATIC
|
||||
hle/service/acc/acc_u0.h
|
||||
hle/service/acc/acc_u1.cpp
|
||||
hle/service/acc/acc_u1.h
|
||||
hle/service/acc/profile_manager.cpp
|
||||
hle/service/acc/profile_manager.h
|
||||
hle/service/am/am.cpp
|
||||
hle/service/am/am.h
|
||||
hle/service/am/applet_ae.cpp
|
||||
|
||||
@@ -27,6 +27,7 @@ enum class NCAContentType : u8 {
|
||||
Control = 2,
|
||||
Manual = 3,
|
||||
Data = 4,
|
||||
Data_Unknown5 = 5, ///< Seems to be used on some system archives
|
||||
};
|
||||
|
||||
enum class NCASectionCryptoType : u8 {
|
||||
|
||||
@@ -77,12 +77,13 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
case NCAContentType::Control:
|
||||
return ContentRecordType::Control;
|
||||
case NCAContentType::Data:
|
||||
case NCAContentType::Data_Unknown5:
|
||||
return ContentRecordType::Data;
|
||||
case NCAContentType::Manual:
|
||||
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
|
||||
return ContentRecordType::Manual;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -17,9 +19,41 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id) {
|
||||
// TODO(DarkLordZach): Use title id.
|
||||
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
|
||||
return MakeResult<VirtualFile>(file);
|
||||
}
|
||||
|
||||
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
|
||||
switch (storage) {
|
||||
case StorageId::NandSystem: {
|
||||
const auto res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
|
||||
if (res == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return ResultCode(-1);
|
||||
}
|
||||
const auto romfs = res->GetRomFS();
|
||||
if (romfs == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return MakeResult<VirtualFile>(romfs);
|
||||
}
|
||||
case StorageId::NandUser: {
|
||||
const auto res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
|
||||
if (res == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return ResultCode(-1);
|
||||
}
|
||||
const auto romfs = res->GetRomFS();
|
||||
if (romfs == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return MakeResult<VirtualFile>(romfs);
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -11,12 +11,22 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class StorageId : u8 {
|
||||
None = 0,
|
||||
Host = 1,
|
||||
GameCard = 2,
|
||||
NandSystem = 3,
|
||||
NandUser = 4,
|
||||
SdCard = 5,
|
||||
};
|
||||
|
||||
/// File system interface to the RomFS archive
|
||||
class RomFSFactory {
|
||||
public:
|
||||
explicit RomFSFactory(Loader::AppLoader& app_loader);
|
||||
|
||||
ResultVal<VirtualFile> Open(u64 title_id);
|
||||
ResultVal<VirtualFile> OpenCurrentProcess();
|
||||
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
|
||||
|
||||
private:
|
||||
VirtualFile file;
|
||||
|
||||
@@ -3,17 +3,19 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
#include "core/hle/service/acc/acc_aa.h"
|
||||
#include "core/hle/service/acc/acc_su.h"
|
||||
#include "core/hle/service/acc/acc_u0.h"
|
||||
#include "core/hle/service/acc/acc_u1.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
// TODO: RE this structure
|
||||
struct UserData {
|
||||
INSERT_PADDING_WORDS(1);
|
||||
@@ -25,19 +27,10 @@ struct UserData {
|
||||
};
|
||||
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
|
||||
|
||||
struct ProfileBase {
|
||||
u128 user_id;
|
||||
u64 timestamp;
|
||||
std::array<u8, 0x20> username;
|
||||
};
|
||||
static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size");
|
||||
|
||||
// TODO(ogniK): Generate a real user id based on username, md5(username) maybe?
|
||||
static constexpr u128 DEFAULT_USER_ID{1ull, 0ull};
|
||||
|
||||
class IProfile final : public ServiceFramework<IProfile> {
|
||||
public:
|
||||
explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
|
||||
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
|
||||
: ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IProfile::Get, "Get"},
|
||||
{1, &IProfile::GetBase, "GetBase"},
|
||||
@@ -49,46 +42,42 @@ public:
|
||||
|
||||
private:
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
profile_base.user_id = user_id;
|
||||
if (Settings::values.username.size() > profile_base.username.size()) {
|
||||
std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
|
||||
profile_base.username.begin());
|
||||
std::array<u8, MAX_DATA> data{};
|
||||
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
|
||||
ctx.WriteBuffer(data);
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
std::copy(Settings::values.username.begin(), Settings::values.username.end(),
|
||||
profile_base.username.begin());
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
|
||||
user_id.Format());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(profile_base);
|
||||
}
|
||||
|
||||
void GetBase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
// TODO(Subv): Retrieve this information from somewhere.
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
profile_base.user_id = user_id;
|
||||
if (Settings::values.username.size() > profile_base.username.size()) {
|
||||
std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
|
||||
profile_base.username.begin());
|
||||
if (profile_manager.GetProfileBase(user_id, profile_base)) {
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
std::copy(Settings::values.username.begin(), Settings::values.username.end(),
|
||||
profile_base.username.begin());
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
|
||||
}
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(profile_base);
|
||||
}
|
||||
|
||||
void LoadImage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
// smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
|
||||
const u32 jpeg_size = 107;
|
||||
static const std::array<u8, jpeg_size> jpeg{
|
||||
constexpr u32 jpeg_size = 107;
|
||||
static constexpr std::array<u8, jpeg_size> jpeg{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
|
||||
0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
|
||||
@@ -98,13 +87,14 @@ private:
|
||||
0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
|
||||
0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
};
|
||||
ctx.WriteBuffer(jpeg.data(), jpeg_size);
|
||||
ctx.WriteBuffer(jpeg);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(jpeg_size);
|
||||
}
|
||||
|
||||
u128 user_id; ///< The user id this profile refers to.
|
||||
const ProfileManager& profile_manager;
|
||||
UUID user_id; ///< The user id this profile refers to.
|
||||
};
|
||||
|
||||
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
|
||||
@@ -141,44 +131,57 @@ private:
|
||||
};
|
||||
|
||||
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(1);
|
||||
rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
|
||||
}
|
||||
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
UUID user_id = rp.PopRaw<UUID>();
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(true); // TODO: Check when this is supposed to return true and when not
|
||||
rb.Push(profile_manager->UserExists(user_id));
|
||||
}
|
||||
|
||||
void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
// TODO(Subv): There is only one user for now.
|
||||
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
|
||||
ctx.WriteBuffer(user_ids);
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
ctx.WriteBuffer(profile_manager->GetAllUsers());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
// TODO(Subv): There is only one user for now.
|
||||
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
|
||||
ctx.WriteBuffer(user_ids);
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
ctx.WriteBuffer(profile_manager->GetOpenUsers());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser());
|
||||
}
|
||||
|
||||
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u128 user_id = rp.PopRaw<u128>();
|
||||
UUID user_id = rp.PopRaw<UUID>();
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IProfile>(user_id);
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{:016X}{:016X}", user_id[1], user_id[0]);
|
||||
rb.PushIpcInterface<IProfile>(user_id, *profile_manager);
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(profile_manager->CanSystemRegisterUser());
|
||||
}
|
||||
|
||||
void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
|
||||
@@ -194,22 +197,20 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
}
|
||||
|
||||
void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(DEFAULT_USER_ID);
|
||||
}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
profile_manager(std::move(profile_manager)) {}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<ACC_AA>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_SU>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U0>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U1>(module)->InstallAsService(service_manager);
|
||||
auto profile_manager = std::make_shared<ProfileManager>();
|
||||
std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
class ProfileManager;
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void GetUserCount(Kernel::HLERequestContext& ctx);
|
||||
void GetUserExistence(Kernel::HLERequestContext& ctx);
|
||||
@@ -22,9 +26,11 @@ public:
|
||||
void GetProfile(Kernel::HLERequestContext& ctx);
|
||||
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
|
||||
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
std::shared_ptr<ProfileManager> profile_manager;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_AA::ACC_AA(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:aa") {
|
||||
ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "EnsureCacheAsync"},
|
||||
{1, nullptr, "LoadCache"},
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_AA final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_AA(std::shared_ptr<Module> module);
|
||||
explicit ACC_AA(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
};
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:su") {
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_SU::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_SU::GetUserExistence, "GetUserExistence"},
|
||||
@@ -15,7 +16,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
|
||||
{4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"},
|
||||
{5, &ACC_SU::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, nullptr, "IsUserRegistrationRequestPermitted"},
|
||||
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
|
||||
@@ -11,7 +11,8 @@ namespace Account {
|
||||
|
||||
class ACC_SU final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_SU(std::shared_ptr<Module> module);
|
||||
explicit ACC_SU(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
};
|
||||
|
||||
} // namespace Account
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u0") {
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_U0::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
|
||||
@@ -15,7 +16,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
|
||||
{4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
|
||||
{5, &ACC_U0::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, nullptr, "IsUserRegistrationRequestPermitted"},
|
||||
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_U0 final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_U0(std::shared_ptr<Module> module);
|
||||
explicit ACC_U0(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
};
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u1") {
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_U1::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_U1::GetUserExistence, "GetUserExistence"},
|
||||
@@ -15,7 +16,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
|
||||
{4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"},
|
||||
{5, &ACC_U1::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, nullptr, "IsUserRegistrationRequestPermitted"},
|
||||
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_U1 final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_U1(std::shared_ptr<Module> module);
|
||||
explicit ACC_U1(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
};
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
236
src/core/hle/service/acc/profile_manager.cpp
Normal file
236
src/core/hle/service/acc/profile_manager.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <random>
|
||||
#include <boost/optional.hpp>
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Account {
|
||||
// TODO(ogniK): Get actual error codes
|
||||
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
|
||||
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
|
||||
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
|
||||
|
||||
const UUID& UUID::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
uuid[0] = distribution(gen);
|
||||
uuid[1] = distribution(gen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProfileManager::ProfileManager() {
|
||||
// TODO(ogniK): Create the default user we have for now until loading/saving users is added
|
||||
auto user_uuid = UUID{1, 0};
|
||||
CreateNewUser(user_uuid, Settings::values.username);
|
||||
OpenUser(user_uuid);
|
||||
}
|
||||
|
||||
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
|
||||
/// internal management of the users profiles
|
||||
boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
|
||||
if (user_count >= MAX_USERS) {
|
||||
return boost::none;
|
||||
}
|
||||
profiles[user_count] = user;
|
||||
return user_count++;
|
||||
}
|
||||
|
||||
/// Deletes a specific profile based on it's profile index
|
||||
bool ProfileManager::RemoveProfileAtIndex(size_t index) {
|
||||
if (index >= MAX_USERS || index >= user_count) {
|
||||
return false;
|
||||
}
|
||||
if (index < user_count - 1) {
|
||||
std::rotate(profiles.begin() + index, profiles.begin() + index + 1, profiles.end());
|
||||
}
|
||||
profiles.back() = {};
|
||||
user_count--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Helper function to register a user to the system
|
||||
ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
|
||||
if (AddToProfiles(user) == boost::none) {
|
||||
return ERROR_TOO_MANY_USERS;
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Create a new user on the system. If the uuid of the user already exists, the user is not
|
||||
/// created.
|
||||
ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) {
|
||||
if (user_count == MAX_USERS) {
|
||||
return ERROR_TOO_MANY_USERS;
|
||||
}
|
||||
if (!uuid) {
|
||||
return ERROR_ARGUMENT_IS_NULL;
|
||||
}
|
||||
if (username[0] == 0x0) {
|
||||
return ERROR_ARGUMENT_IS_NULL;
|
||||
}
|
||||
if (std::any_of(profiles.begin(), profiles.end(),
|
||||
[&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) {
|
||||
return ERROR_USER_ALREADY_EXISTS;
|
||||
}
|
||||
ProfileInfo profile;
|
||||
profile.user_uuid = uuid;
|
||||
profile.username = username;
|
||||
profile.data = {};
|
||||
profile.creation_time = 0x0;
|
||||
profile.is_open = false;
|
||||
return AddUser(profile);
|
||||
}
|
||||
|
||||
/// Creates a new user on the system. This function allows a much simpler method of registration
|
||||
/// specifically by allowing an std::string for the username. This is required specifically since
|
||||
/// we're loading a string straight from the config
|
||||
ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
|
||||
ProfileUsername username_output;
|
||||
if (username.size() > username_output.size()) {
|
||||
std::copy_n(username.begin(), username_output.size(), username_output.begin());
|
||||
} else {
|
||||
std::copy(username.begin(), username.end(), username_output.begin());
|
||||
}
|
||||
return CreateNewUser(uuid, username_output);
|
||||
}
|
||||
|
||||
/// Returns a users profile index based on their user id.
|
||||
boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
|
||||
if (!uuid) {
|
||||
return boost::none;
|
||||
}
|
||||
auto iter = std::find_if(profiles.begin(), profiles.end(),
|
||||
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
|
||||
if (iter == profiles.end()) {
|
||||
return boost::none;
|
||||
}
|
||||
return static_cast<size_t>(std::distance(profiles.begin(), iter));
|
||||
}
|
||||
|
||||
/// Returns a users profile index based on their profile
|
||||
boost::optional<size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
|
||||
return GetUserIndex(user.user_uuid);
|
||||
}
|
||||
|
||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||
bool ProfileManager::GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const {
|
||||
if (index == boost::none || index >= MAX_USERS) {
|
||||
return false;
|
||||
}
|
||||
const auto& prof_info = profiles[index.get()];
|
||||
profile.user_uuid = prof_info.user_uuid;
|
||||
profile.username = prof_info.username;
|
||||
profile.timestamp = prof_info.creation_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||
bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
return GetProfileBase(idx, profile);
|
||||
}
|
||||
|
||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||
bool ProfileManager::GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const {
|
||||
return GetProfileBase(user.user_uuid, profile);
|
||||
}
|
||||
|
||||
/// Returns the current user count on the system. We keep a variable which tracks the count so we
|
||||
/// don't have to loop the internal profile array every call.
|
||||
size_t ProfileManager::GetUserCount() const {
|
||||
return user_count;
|
||||
}
|
||||
|
||||
/// Lists the current "opened" users on the system. Users are typically not open until they sign
|
||||
/// into something or pick a profile. As of right now users should all be open until qlaunch is
|
||||
/// booting
|
||||
size_t ProfileManager::GetOpenUserCount() const {
|
||||
return std::count_if(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& p) { return p.is_open; });
|
||||
}
|
||||
|
||||
/// Checks if a user id exists in our profile manager
|
||||
bool ProfileManager::UserExists(UUID uuid) const {
|
||||
return (GetUserIndex(uuid) != boost::none);
|
||||
}
|
||||
|
||||
/// Opens a specific user
|
||||
void ProfileManager::OpenUser(UUID uuid) {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
if (idx == boost::none) {
|
||||
return;
|
||||
}
|
||||
profiles[idx.get()].is_open = true;
|
||||
last_opened_user = uuid;
|
||||
}
|
||||
|
||||
/// Closes a specific user
|
||||
void ProfileManager::CloseUser(UUID uuid) {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
if (idx == boost::none) {
|
||||
return;
|
||||
}
|
||||
profiles[idx.get()].is_open = false;
|
||||
}
|
||||
|
||||
/// Gets all valid user ids on the system
|
||||
UserIDArray ProfileManager::GetAllUsers() const {
|
||||
UserIDArray output;
|
||||
std::transform(profiles.begin(), profiles.end(), output.begin(),
|
||||
[](const ProfileInfo& p) { return p.user_uuid; });
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Get all the open users on the system and zero out the rest of the data. This is specifically
|
||||
/// needed for GetOpenUsers and we need to ensure the rest of the output buffer is zero'd out
|
||||
UserIDArray ProfileManager::GetOpenUsers() const {
|
||||
UserIDArray output;
|
||||
std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
|
||||
if (p.is_open)
|
||||
return p.user_uuid;
|
||||
return UUID{};
|
||||
});
|
||||
std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Returns the last user which was opened
|
||||
UUID ProfileManager::GetLastOpenedUser() const {
|
||||
return last_opened_user;
|
||||
}
|
||||
|
||||
/// Return the users profile base and the unknown arbitary data.
|
||||
bool ProfileManager::GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile,
|
||||
ProfileData& data) const {
|
||||
if (GetProfileBase(index, profile)) {
|
||||
data = profiles[index.get()].data;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Return the users profile base and the unknown arbitary data.
|
||||
bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
|
||||
ProfileData& data) const {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
return GetProfileBaseAndData(idx, profile, data);
|
||||
}
|
||||
|
||||
/// Return the users profile base and the unknown arbitary data.
|
||||
bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
|
||||
ProfileData& data) const {
|
||||
return GetProfileBaseAndData(user.user_uuid, profile, data);
|
||||
}
|
||||
|
||||
/// Returns if the system is allowing user registrations or not
|
||||
bool ProfileManager::CanSystemRegisterUser() const {
|
||||
return false; // TODO(ogniK): Games shouldn't have
|
||||
// access to user registration, when we
|
||||
// emulate qlaunch. Update this to dynamically change.
|
||||
}
|
||||
|
||||
}; // namespace Service::Account
|
||||
117
src/core/hle/service/acc/profile_manager.h
Normal file
117
src/core/hle/service/acc/profile_manager.h
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "boost/optional.hpp"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::Account {
|
||||
constexpr size_t MAX_USERS = 8;
|
||||
constexpr size_t MAX_DATA = 128;
|
||||
constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid = INVALID_UUID;
|
||||
UUID() = default;
|
||||
explicit UUID(const u128& id) : uuid{id} {}
|
||||
explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
|
||||
explicit operator bool() const {
|
||||
return uuid != INVALID_UUID;
|
||||
}
|
||||
|
||||
bool operator==(const UUID& rhs) const {
|
||||
return uuid == rhs.uuid;
|
||||
}
|
||||
|
||||
bool operator!=(const UUID& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate uuids based on RFC-4122
|
||||
const UUID& Generate();
|
||||
|
||||
// Set the UUID to {0,0} to be considered an invalid user
|
||||
void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
std::string Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
using ProfileUsername = std::array<u8, 0x20>;
|
||||
using ProfileData = std::array<u8, MAX_DATA>;
|
||||
using UserIDArray = std::array<UUID, MAX_USERS>;
|
||||
|
||||
/// This holds general information about a users profile. This is where we store all the information
|
||||
/// based on a specific user
|
||||
struct ProfileInfo {
|
||||
UUID user_uuid;
|
||||
ProfileUsername username;
|
||||
u64 creation_time;
|
||||
ProfileData data; // TODO(ognik): Work out what this is
|
||||
bool is_open;
|
||||
};
|
||||
|
||||
struct ProfileBase {
|
||||
UUID user_uuid;
|
||||
u64_le timestamp;
|
||||
ProfileUsername username;
|
||||
|
||||
// Zero out all the fields to make the profile slot considered "Empty"
|
||||
void Invalidate() {
|
||||
user_uuid.Invalidate();
|
||||
timestamp = 0;
|
||||
username.fill(0);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
|
||||
|
||||
/// The profile manager is used for handling multiple user profiles at once. It keeps track of open
|
||||
/// users, all the accounts registered on the "system" as well as fetching individual "ProfileInfo"
|
||||
/// objects
|
||||
class ProfileManager {
|
||||
public:
|
||||
ProfileManager(); // TODO(ogniK): Load from system save
|
||||
ResultCode AddUser(const ProfileInfo& user);
|
||||
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
|
||||
ResultCode CreateNewUser(UUID uuid, const std::string& username);
|
||||
boost::optional<size_t> GetUserIndex(const UUID& uuid) const;
|
||||
boost::optional<size_t> GetUserIndex(const ProfileInfo& user) const;
|
||||
bool GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const;
|
||||
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
|
||||
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
|
||||
bool GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile,
|
||||
ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
|
||||
ProfileData& data) const;
|
||||
size_t GetUserCount() const;
|
||||
size_t GetOpenUserCount() const;
|
||||
bool UserExists(UUID uuid) const;
|
||||
void OpenUser(UUID uuid);
|
||||
void CloseUser(UUID uuid);
|
||||
UserIDArray GetOpenUsers() const;
|
||||
UserIDArray GetAllUsers() const;
|
||||
UUID GetLastOpenedUser() const;
|
||||
|
||||
bool CanSystemRegisterUser() const;
|
||||
|
||||
private:
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
size_t user_count = 0;
|
||||
boost::optional<size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
bool RemoveProfileAtIndex(size_t index);
|
||||
UUID last_opened_user{INVALID_UUID};
|
||||
};
|
||||
|
||||
}; // namespace Service::Account
|
||||
@@ -256,15 +256,28 @@ ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id) {
|
||||
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id);
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
|
||||
LOG_TRACE(Service_FS, "Opening RomFS for current process");
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
// TODO(bunnei): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return romfs_factory->Open(title_id);
|
||||
return romfs_factory->OpenCurrentProcess();
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type) {
|
||||
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
|
||||
title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
// TODO(bunnei): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return romfs_factory->Open(title_id, storage_id, type);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
|
||||
@@ -27,7 +27,9 @@ ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory)
|
||||
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
|
||||
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id);
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
FileSys::SaveDataDescriptor save_struct);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
|
||||
@@ -23,15 +23,6 @@
|
||||
|
||||
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:
|
||||
explicit IStorage(FileSys::VirtualFile backend_)
|
||||
@@ -467,7 +458,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{110, nullptr, "OpenContentStorageFileSystem"},
|
||||
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
|
||||
{201, nullptr, "OpenDataStorageByProgramId"},
|
||||
{202, nullptr, "OpenDataStorageByDataId"},
|
||||
{202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
|
||||
{203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"},
|
||||
{400, nullptr, "OpenDeviceOperator"},
|
||||
{500, nullptr, "OpenSdCardDetectionEventNotifier"},
|
||||
@@ -580,7 +571,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
auto romfs = OpenRomFS(Core::System::GetInstance().CurrentProcess()->program_id);
|
||||
auto romfs = OpenRomFSCurrentProcess();
|
||||
if (romfs.Failed()) {
|
||||
// TODO (bunnei): Find the right error code to use here
|
||||
LOG_CRITICAL(Service_FS, "no file system interface available!");
|
||||
@@ -596,10 +587,37 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage));
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto storage_id = rp.PopRaw<FileSys::StorageId>();
|
||||
const auto unknown = rp.PopRaw<u32>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
|
||||
static_cast<u8>(storage_id), unknown, title_id);
|
||||
|
||||
auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
|
||||
if (data.Failed()) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
LOG_ERROR(Service_FS,
|
||||
"could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,
|
||||
static_cast<u8>(storage_id));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
IStorage storage(std::move(data.Unwrap()));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage));
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto storage_id = rp.PopRaw<StorageId>();
|
||||
auto storage_id = rp.PopRaw<FileSys::StorageId>();
|
||||
auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}",
|
||||
|
||||
@@ -25,6 +25,7 @@ private:
|
||||
void MountSaveData(Kernel::HLERequestContext& ctx);
|
||||
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
|
||||
void OpenRomStorage(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSys::VirtualFile romfs;
|
||||
|
||||
@@ -14,7 +14,8 @@ public:
|
||||
IParentalControlService() : ServiceFramework("IParentalControlService") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IParentalControlService::Initialize, "Initialize"},
|
||||
{1001, nullptr, "CheckFreeCommunicationPermission"},
|
||||
{1001, &IParentalControlService::CheckFreeCommunicationPermission,
|
||||
"CheckFreeCommunicationPermission"},
|
||||
{1002, nullptr, "ConfirmLaunchApplicationPermission"},
|
||||
{1003, nullptr, "ConfirmResumeApplicationPermission"},
|
||||
{1004, nullptr, "ConfirmSnsPostPermission"},
|
||||
@@ -116,6 +117,12 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -2,34 +2,16 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/scm_rev.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#endif
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
static const char* CpuVendorToStr(Common::CPUVendor vendor) {
|
||||
switch (vendor) {
|
||||
case Common::CPUVendor::INTEL:
|
||||
return "Intel";
|
||||
case Common::CPUVendor::AMD:
|
||||
return "Amd";
|
||||
case Common::CPUVendor::OTHER:
|
||||
return "Other";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
static u64 GenerateTelemetryId() {
|
||||
u64 telemetry_id{};
|
||||
return telemetry_id;
|
||||
@@ -37,8 +19,8 @@ static u64 GenerateTelemetryId() {
|
||||
|
||||
u64 GetTelemetryId() {
|
||||
u64 telemetry_id{};
|
||||
static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
|
||||
"telemetry_id"};
|
||||
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
|
||||
"telemetry_id"};
|
||||
|
||||
if (FileUtil::Exists(filename)) {
|
||||
FileUtil::IOFile file(filename, "rb");
|
||||
@@ -62,8 +44,8 @@ u64 GetTelemetryId() {
|
||||
|
||||
u64 RegenerateTelemetryId() {
|
||||
const u64 new_telemetry_id{GenerateTelemetryId()};
|
||||
static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
|
||||
"telemetry_id"};
|
||||
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
|
||||
"telemetry_id"};
|
||||
|
||||
FileUtil::IOFile file(filename, "wb");
|
||||
if (!file.IsOpen()) {
|
||||
@@ -112,48 +94,11 @@ TelemetrySession::TelemetrySession() {
|
||||
}
|
||||
|
||||
// Log application information
|
||||
const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr};
|
||||
AddField(Telemetry::FieldType::App, "Git_IsDirty", is_git_dirty);
|
||||
AddField(Telemetry::FieldType::App, "Git_Branch", Common::g_scm_branch);
|
||||
AddField(Telemetry::FieldType::App, "Git_Revision", Common::g_scm_rev);
|
||||
AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date);
|
||||
AddField(Telemetry::FieldType::App, "BuildName", Common::g_build_name);
|
||||
Telemetry::AppendBuildInfo(field_collection);
|
||||
|
||||
// Log user system information
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_BrandString",
|
||||
Common::GetCPUCaps().brand_string);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Vendor",
|
||||
CpuVendorToStr(Common::GetCPUCaps().vendor));
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSSE3",
|
||||
Common::GetCPUCaps().ssse3);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE41",
|
||||
Common::GetCPUCaps().sse4_1);
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42",
|
||||
Common::GetCPUCaps().sse4_2);
|
||||
#else
|
||||
AddField(Telemetry::FieldType::UserSystem, "CPU_Model", "Other");
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Apple");
|
||||
#elif defined(_WIN32)
|
||||
AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Windows");
|
||||
#elif defined(__linux__) || defined(linux) || defined(__linux)
|
||||
AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Linux");
|
||||
#else
|
||||
AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Unknown");
|
||||
#endif
|
||||
// Log user system information
|
||||
Telemetry::AppendCPUInfo(field_collection);
|
||||
Telemetry::AppendOSInfo(field_collection);
|
||||
|
||||
// Log user configuration information
|
||||
AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit);
|
||||
|
||||
@@ -679,7 +679,19 @@ public:
|
||||
|
||||
INSERT_PADDING_WORDS(0x7);
|
||||
|
||||
INSERT_PADDING_WORDS(0x46);
|
||||
INSERT_PADDING_WORDS(0x20);
|
||||
|
||||
struct {
|
||||
u32 is_instanced[NumVertexArrays];
|
||||
|
||||
/// Returns whether the vertex array specified by index is supposed to be
|
||||
/// accessed per instance or not.
|
||||
bool IsInstancingEnabled(u32 index) const {
|
||||
return is_instanced[index];
|
||||
}
|
||||
} instanced_arrays;
|
||||
|
||||
INSERT_PADDING_WORDS(0x6);
|
||||
|
||||
Cull cull;
|
||||
|
||||
@@ -928,6 +940,7 @@ ASSERT_REG_POSITION(point_coord_replace, 0x581);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(index_array, 0x5F2);
|
||||
ASSERT_REG_POSITION(instanced_arrays, 0x620);
|
||||
ASSERT_REG_POSITION(cull, 0x646);
|
||||
ASSERT_REG_POSITION(clear_buffers, 0x674);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
|
||||
@@ -223,6 +223,13 @@ enum class PredicateResultMode : u64 {
|
||||
NotZero = 0x3,
|
||||
};
|
||||
|
||||
enum class TextureType : u64 {
|
||||
Texture1D = 0,
|
||||
Texture2D = 1,
|
||||
Texture3D = 2,
|
||||
TextureCube = 3,
|
||||
};
|
||||
|
||||
union Instruction {
|
||||
Instruction& operator=(const Instruction& instr) {
|
||||
value = instr.value;
|
||||
@@ -434,6 +441,8 @@ union Instruction {
|
||||
} conversion;
|
||||
|
||||
union {
|
||||
BitField<28, 1, u64> array;
|
||||
BitField<29, 2, TextureType> texture_type;
|
||||
BitField<31, 4, u64> component_mask;
|
||||
|
||||
bool IsComponentEnabled(size_t component) const {
|
||||
@@ -442,9 +451,41 @@ union Instruction {
|
||||
} tex;
|
||||
|
||||
union {
|
||||
BitField<50, 3, u64> component_mask_selector;
|
||||
BitField<28, 1, u64> array;
|
||||
BitField<29, 2, TextureType> texture_type;
|
||||
BitField<56, 2, u64> component;
|
||||
} tld4;
|
||||
|
||||
union {
|
||||
BitField<52, 2, u64> component;
|
||||
} tld4s;
|
||||
|
||||
union {
|
||||
BitField<0, 8, Register> gpr0;
|
||||
BitField<28, 8, Register> gpr28;
|
||||
BitField<50, 3, u64> component_mask_selector;
|
||||
BitField<53, 4, u64> texture_info;
|
||||
|
||||
TextureType GetTextureType() const {
|
||||
// The TEXS instruction has a weird encoding for the texture type.
|
||||
if (texture_info == 0)
|
||||
return TextureType::Texture1D;
|
||||
if (texture_info >= 1 && texture_info <= 9)
|
||||
return TextureType::Texture2D;
|
||||
if (texture_info >= 10 && texture_info <= 11)
|
||||
return TextureType::Texture3D;
|
||||
if (texture_info >= 12 && texture_info <= 13)
|
||||
return TextureType::TextureCube;
|
||||
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
||||
static_cast<u32>(texture_info.Value()));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool IsArrayTexture() const {
|
||||
// TEXS only supports Texture2D arrays.
|
||||
return texture_info >= 7 && texture_info <= 9;
|
||||
}
|
||||
|
||||
bool HasTwoDestinations() const {
|
||||
return gpr28.Value() != Register::ZeroIndex;
|
||||
@@ -468,6 +509,33 @@ union Instruction {
|
||||
}
|
||||
} texs;
|
||||
|
||||
union {
|
||||
BitField<53, 4, u64> texture_info;
|
||||
|
||||
TextureType GetTextureType() const {
|
||||
// The TLDS instruction has a weird encoding for the texture type.
|
||||
if (texture_info >= 0 && texture_info <= 1) {
|
||||
return TextureType::Texture1D;
|
||||
}
|
||||
if (texture_info == 2 || texture_info == 8 || texture_info == 12 ||
|
||||
texture_info >= 4 && texture_info <= 6) {
|
||||
return TextureType::Texture2D;
|
||||
}
|
||||
if (texture_info == 7) {
|
||||
return TextureType::Texture3D;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
|
||||
static_cast<u32>(texture_info.Value()));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool IsArrayTexture() const {
|
||||
// TEXS only supports Texture2D arrays.
|
||||
return texture_info == 8;
|
||||
}
|
||||
} tlds;
|
||||
|
||||
union {
|
||||
BitField<20, 24, u64> target;
|
||||
BitField<5, 1, u64> constant_buffer;
|
||||
@@ -533,9 +601,11 @@ public:
|
||||
LDG, // Load from global memory
|
||||
STG, // Store in global memory
|
||||
TEX,
|
||||
TEXQ, // Texture Query
|
||||
TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
|
||||
TLDS, // Texture Load with scalar/non-vec4 source/destinations
|
||||
TEXQ, // Texture Query
|
||||
TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
|
||||
TLDS, // Texture Load with scalar/non-vec4 source/destinations
|
||||
TLD4, // Texture Load 4
|
||||
TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations
|
||||
EXIT,
|
||||
IPA,
|
||||
FFMA_IMM, // Fused Multiply and Add
|
||||
@@ -749,6 +819,8 @@ private:
|
||||
INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"),
|
||||
INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
|
||||
INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
|
||||
INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"),
|
||||
INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"),
|
||||
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
|
||||
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
|
||||
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
|
||||
|
||||
@@ -98,7 +98,8 @@ RasterizerOpenGL::~RasterizerOpenGL() {}
|
||||
std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
GLintptr buffer_offset) {
|
||||
MICROPROFILE_SCOPE(OpenGL_VAO);
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
state.draw.vertex_array = hw_vao.handle;
|
||||
state.draw.vertex_buffer = stream_buffer.GetHandle();
|
||||
@@ -110,9 +111,13 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
if (!vertex_array.IsEnabled())
|
||||
continue;
|
||||
|
||||
const Tegra::GPUVAddr start = vertex_array.StartAddress();
|
||||
Tegra::GPUVAddr start = vertex_array.StartAddress();
|
||||
const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
|
||||
|
||||
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
|
||||
start += vertex_array.stride * (gpu.state.current_instance / vertex_array.divisor);
|
||||
}
|
||||
|
||||
ASSERT(end > start);
|
||||
u64 size = end - start + 1;
|
||||
|
||||
@@ -124,7 +129,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset,
|
||||
vertex_array.stride);
|
||||
|
||||
ASSERT_MSG(vertex_array.divisor == 0, "Instanced vertex arrays are not supported");
|
||||
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
|
||||
// Tell OpenGL that this is an instanced vertex buffer to prevent accessing different
|
||||
// indexes on each vertex. We do the instance indexing manually by incrementing the
|
||||
// start address of the vertex buffer.
|
||||
glVertexBindingDivisor(index, 1);
|
||||
} else {
|
||||
// Disable the vertex buffer instancing.
|
||||
glVertexBindingDivisor(index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
|
||||
|
||||
@@ -26,6 +26,7 @@ using Tegra::Shader::Sampler;
|
||||
using Tegra::Shader::SubOp;
|
||||
|
||||
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
|
||||
constexpr u32 PROGRAM_HEADER_SIZE = 0x50;
|
||||
|
||||
class DecompileFail : public std::runtime_error {
|
||||
public:
|
||||
@@ -439,13 +440,12 @@ public:
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
|
||||
// Append the sampler2D array for the used textures.
|
||||
size_t num_samplers = GetSamplers().size();
|
||||
if (num_samplers > 0) {
|
||||
declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' +
|
||||
std::to_string(num_samplers) + "];");
|
||||
declarations.AddNewLine();
|
||||
const auto& samplers = GetSamplers();
|
||||
for (const auto& sampler : samplers) {
|
||||
declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
|
||||
';');
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
}
|
||||
|
||||
/// Returns a list of constant buffer declarations
|
||||
@@ -457,13 +457,14 @@ public:
|
||||
}
|
||||
|
||||
/// Returns a list of samplers used in the shader
|
||||
std::vector<SamplerEntry> GetSamplers() const {
|
||||
const std::vector<SamplerEntry>& GetSamplers() const {
|
||||
return used_samplers;
|
||||
}
|
||||
|
||||
/// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
|
||||
/// necessary.
|
||||
std::string AccessSampler(const Sampler& sampler) {
|
||||
std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
|
||||
bool is_array) {
|
||||
size_t offset = static_cast<size_t>(sampler.index.Value());
|
||||
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
@@ -472,12 +473,13 @@ public:
|
||||
[&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
|
||||
|
||||
if (itr != used_samplers.end()) {
|
||||
ASSERT(itr->GetType() == type && itr->IsArray() == is_array);
|
||||
return itr->GetName();
|
||||
}
|
||||
|
||||
// Otherwise create a new mapping for this sampler
|
||||
size_t next_index = used_samplers.size();
|
||||
SamplerEntry entry{stage, offset, next_index};
|
||||
SamplerEntry entry{stage, offset, next_index, type, is_array};
|
||||
used_samplers.emplace_back(entry);
|
||||
return entry.GetName();
|
||||
}
|
||||
@@ -620,6 +622,23 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// Shader program header for a Fragment Shader.
|
||||
struct FragmentHeader {
|
||||
INSERT_PADDING_WORDS(5);
|
||||
INSERT_PADDING_WORDS(13);
|
||||
u32 enabled_color_outputs;
|
||||
union {
|
||||
BitField<0, 1, u32> writes_samplemask;
|
||||
BitField<1, 1, u32> writes_depth;
|
||||
};
|
||||
|
||||
bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
|
||||
u32 bit = render_target * 4 + component;
|
||||
return enabled_color_outputs & (1 << bit);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(FragmentHeader) == PROGRAM_HEADER_SIZE, "FragmentHeader size is wrong");
|
||||
|
||||
/// Gets the Subroutine object corresponding to the specified address.
|
||||
const Subroutine& GetSubroutine(u32 begin, u32 end) const {
|
||||
auto iter = subroutines.find(Subroutine{begin, end, suffix});
|
||||
@@ -638,8 +657,8 @@ private:
|
||||
}
|
||||
|
||||
/// Generates code representing a texture sampler.
|
||||
std::string GetSampler(const Sampler& sampler) {
|
||||
return regs.AccessSampler(sampler);
|
||||
std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) {
|
||||
return regs.AccessSampler(sampler, type, is_array);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -839,29 +858,29 @@ private:
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
|
||||
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
|
||||
// goes into gpr28+0 and gpr28+1
|
||||
size_t texs_offset{};
|
||||
// TEXS has two destination registers and a swizzle. The first two elements in the swizzle
|
||||
// go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
|
||||
|
||||
size_t src_elem{};
|
||||
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
|
||||
size_t dest_elem{};
|
||||
for (unsigned elem = 0; elem < 2; ++elem) {
|
||||
if (!instr.texs.IsComponentEnabled(src_elem++)) {
|
||||
// Skip disabled components
|
||||
continue;
|
||||
}
|
||||
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
|
||||
dest_elem++);
|
||||
size_t written_components = 0;
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
if (!instr.texs.IsComponentEnabled(component)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!instr.texs.HasTwoDestinations()) {
|
||||
// Skip the second destination
|
||||
break;
|
||||
if (written_components < 2) {
|
||||
// Write the first two swizzle components to gpr0 and gpr0+1
|
||||
regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false,
|
||||
written_components % 2);
|
||||
} else {
|
||||
ASSERT(instr.texs.HasTwoDestinations());
|
||||
// Write the rest of the swizzle components to gpr28 and gpr28+1
|
||||
regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false,
|
||||
written_components % 2);
|
||||
}
|
||||
|
||||
texs_offset += 2;
|
||||
++written_components;
|
||||
}
|
||||
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
}
|
||||
@@ -893,6 +912,31 @@ private:
|
||||
shader.AddLine('}');
|
||||
}
|
||||
|
||||
/// Writes the output values from a fragment shader to the corresponding GLSL output variables.
|
||||
void EmitFragmentOutputsWrite() {
|
||||
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
|
||||
FragmentHeader header;
|
||||
std::memcpy(&header, program_code.data(), PROGRAM_HEADER_SIZE);
|
||||
|
||||
ASSERT_MSG(header.writes_depth == 0, "Depth write is unimplemented");
|
||||
ASSERT_MSG(header.writes_samplemask == 0, "Samplemask write is unimplemented");
|
||||
|
||||
// Write the color outputs using the data in the shader registers, disabled
|
||||
// rendertargets/components are skipped in the register assignment.
|
||||
u32 current_reg = 0;
|
||||
for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
|
||||
++render_target) {
|
||||
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
if (header.IsColorComponentOutputEnabled(render_target, component)) {
|
||||
shader.AddLine(fmt::format("color[{}][{}] = {};", render_target, component,
|
||||
regs.GetRegisterAsFloat(current_reg)));
|
||||
++current_reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single instruction from Tegra to GLSL.
|
||||
* @param offset the offset of the Tegra shader instruction.
|
||||
@@ -1507,10 +1551,39 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TEX: {
|
||||
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
|
||||
std::string coord{};
|
||||
|
||||
switch (instr.tex.texture_type) {
|
||||
case Tegra::Shader::TextureType::Texture2D: {
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureType::Texture3D: {
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
std::string z = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureType::TextureCube: {
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
|
||||
ASSERT(instr.gpr20.Value() == Register::ZeroIndex);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
|
||||
static_cast<u32>(instr.tex.texture_type.Value()));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, instr.tex.texture_type, instr.tex.array);
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine("{");
|
||||
@@ -1532,24 +1605,129 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TEXS: {
|
||||
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
std::string coord{};
|
||||
|
||||
switch (instr.texs.GetTextureType()) {
|
||||
case Tegra::Shader::TextureType::Texture2D: {
|
||||
if (instr.texs.IsArrayTexture()) {
|
||||
std::string index = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
|
||||
} else {
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureType::Texture3D: {
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureType::TextureCube: {
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
std::string z = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
|
||||
static_cast<u32>(instr.texs.GetTextureType()));
|
||||
UNREACHABLE();
|
||||
}
|
||||
const std::string sampler = GetSampler(instr.sampler, instr.texs.GetTextureType(),
|
||||
instr.texs.IsArrayTexture());
|
||||
|
||||
const std::string texture = "texture(" + sampler + ", coords)";
|
||||
WriteTexsInstruction(instr, coord, texture);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLDS: {
|
||||
const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");";
|
||||
ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D);
|
||||
ASSERT(instr.tlds.IsArrayTexture() == false);
|
||||
std::string coord{};
|
||||
|
||||
switch (instr.tlds.GetTextureType()) {
|
||||
case Tegra::Shader::TextureType::Texture2D: {
|
||||
if (instr.tlds.IsArrayTexture()) {
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture");
|
||||
UNREACHABLE();
|
||||
} else {
|
||||
std::string x = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsInteger(instr.gpr20);
|
||||
coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
|
||||
static_cast<u32>(instr.tlds.GetTextureType()));
|
||||
UNREACHABLE();
|
||||
}
|
||||
const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(),
|
||||
instr.tlds.IsArrayTexture());
|
||||
const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
|
||||
WriteTexsInstruction(instr, coord, texture);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLD4: {
|
||||
ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
|
||||
ASSERT(instr.tld4.array == 0);
|
||||
std::string coord{};
|
||||
|
||||
switch (instr.tld4.texture_type) {
|
||||
case Tegra::Shader::TextureType::Texture2D: {
|
||||
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
|
||||
static_cast<u32>(instr.tld4.texture_type.Value()));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, instr.tld4.texture_type, instr.tld4.array);
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine("{");
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
const std::string texture = "textureGather(" + sampler + ", coords, " +
|
||||
std::to_string(instr.tld4.component) + ')';
|
||||
|
||||
size_t dest_elem{};
|
||||
for (size_t elem = 0; elem < 4; ++elem) {
|
||||
if (!instr.tex.IsComponentEnabled(elem)) {
|
||||
// Skip disabled components
|
||||
continue;
|
||||
}
|
||||
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
|
||||
++dest_elem;
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine("}");
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLD4S: {
|
||||
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
// TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
|
||||
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
const std::string texture = "textureGather(" + sampler + ", coords, " +
|
||||
std::to_string(instr.tld4s.component) + ')';
|
||||
WriteTexsInstruction(instr, coord, texture);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName());
|
||||
UNREACHABLE();
|
||||
@@ -1849,12 +2027,8 @@ private:
|
||||
default: {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::EXIT: {
|
||||
// Final color output is currently hardcoded to GPR0-3 for fragment shaders
|
||||
if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
|
||||
shader.AddLine("color.r = " + regs.GetRegisterAsFloat(0) + ';');
|
||||
shader.AddLine("color.g = " + regs.GetRegisterAsFloat(1) + ';');
|
||||
shader.AddLine("color.b = " + regs.GetRegisterAsFloat(2) + ';');
|
||||
shader.AddLine("color.a = " + regs.GetRegisterAsFloat(3) + ';');
|
||||
EmitFragmentOutputsWrite();
|
||||
}
|
||||
|
||||
switch (instr.flow.cond) {
|
||||
|
||||
@@ -87,7 +87,7 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSCo
|
||||
.get_value_or({});
|
||||
out += R"(
|
||||
in vec4 position;
|
||||
out vec4 color;
|
||||
layout(location = 0) out vec4 color[8];
|
||||
|
||||
layout (std140) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
@@ -72,8 +73,9 @@ class SamplerEntry {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
public:
|
||||
SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index)
|
||||
: offset(offset), stage(stage), sampler_index(index) {}
|
||||
SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index,
|
||||
Tegra::Shader::TextureType type, bool is_array)
|
||||
: offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {}
|
||||
|
||||
size_t GetOffset() const {
|
||||
return offset;
|
||||
@@ -88,8 +90,41 @@ public:
|
||||
}
|
||||
|
||||
std::string GetName() const {
|
||||
return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' +
|
||||
std::to_string(sampler_index) + ']';
|
||||
return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '_' +
|
||||
std::to_string(sampler_index);
|
||||
}
|
||||
|
||||
std::string GetTypeString() const {
|
||||
using Tegra::Shader::TextureType;
|
||||
std::string glsl_type;
|
||||
|
||||
switch (type) {
|
||||
case TextureType::Texture1D:
|
||||
glsl_type = "sampler1D";
|
||||
break;
|
||||
case TextureType::Texture2D:
|
||||
glsl_type = "sampler2D";
|
||||
break;
|
||||
case TextureType::Texture3D:
|
||||
glsl_type = "sampler3D";
|
||||
break;
|
||||
case TextureType::TextureCube:
|
||||
glsl_type = "samplerCube";
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
if (is_array)
|
||||
glsl_type += "Array";
|
||||
return glsl_type;
|
||||
}
|
||||
|
||||
Tegra::Shader::TextureType GetType() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
bool IsArray() const {
|
||||
return is_array;
|
||||
}
|
||||
|
||||
static std::string GetArrayName(Maxwell::ShaderStage stage) {
|
||||
@@ -100,11 +135,14 @@ private:
|
||||
static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
|
||||
"tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
|
||||
};
|
||||
|
||||
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling
|
||||
/// instruction.
|
||||
size_t offset;
|
||||
Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
|
||||
size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
|
||||
Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
|
||||
size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
|
||||
Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
|
||||
bool is_array; ///< Whether the texture is being sampled as an array texture or not.
|
||||
};
|
||||
|
||||
struct ShaderEntries {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <glad/glad.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -432,7 +432,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
LOG_TRACE(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
|
||||
|
||||
std::vector<u8> icon;
|
||||
std::string name;
|
||||
u64 program_id;
|
||||
u64 program_id = 0;
|
||||
loader->ReadProgramId(program_id);
|
||||
|
||||
const auto& control =
|
||||
@@ -509,7 +509,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
std::vector<u8> icon;
|
||||
const auto res1 = loader->ReadIcon(icon);
|
||||
|
||||
u64 program_id;
|
||||
u64 program_id = 0;
|
||||
const auto res2 = loader->ReadProgramId(program_id);
|
||||
|
||||
std::string name = " ";
|
||||
|
||||
Reference in New Issue
Block a user