Compare commits

...

74 Commits

Author SHA1 Message Date
Lioncash
523e4be02c renderer_opengl: Use LOG_DEBUG for GL_DEBUG_SEVERITY_NOTIFICATION and GL_DEBUG_SEVERITY_LOW logs
LOG_TRACE is only enabled on debug builds which can be quite slow when
trying to debug graphics issues. Instead we can log the messages to the
debug log, which is available on both release and debug builds.
2018-08-21 00:23:09 -04:00
bunnei
fde3b1b6f2 Merge pull request #1133 from lioncash/guard
gl_stream_buffer: Add missing header guard
2018-08-20 23:37:55 -04:00
Lioncash
93a4097e9d gl_stream_buffer: Add missing header guard
Prevents potential compilation errors from occuring due to multiple
inclusions
2018-08-20 23:25:08 -04:00
bunnei
c4ce7e456a Merge pull request #1126 from lioncash/telem
telemetry_session: Don't allocate std::string instances for program lifetime in GetTelemetryId() and RegenerateTelemetryId()
2018-08-20 22:15:56 -04:00
bunnei
e33452f7e8 Merge pull request #1131 from bunnei/impl-tex3d-texcube
gl_shader_decompiler: Implement TextureCube/Texture3D for TEX/TEXS.
2018-08-20 22:15:18 -04:00
bunnei
5aaee2ff8d Merge pull request #1106 from Subv/multiple_rendertargets
Shaders: Write all the enabled color outputs when a fragment shader exits.
2018-08-20 21:56:06 -04:00
bunnei
2ae88feea7 shader_bytecode: Replace some UNIMPLEMENTED logs. 2018-08-20 21:53:49 -04:00
bunnei
16db8b9d9f gl_shader_decompiler: Implement Texture3D for TEXS. 2018-08-20 21:53:18 -04:00
bunnei
948002635f gl_shader_decompiler: Implement TextureCube for TEX. 2018-08-20 21:53:00 -04:00
bunnei
ea99819f37 Merge pull request #1130 from Subv/tex_2d
Shaders: Fixed texture coordinates in TEX with Texture2D
2018-08-20 21:49:47 -04:00
Subv
eac3cf301c Shaders: Fixed the coords in TEX with Texture2D.
The X and Y coordinates should be in gpr8 and gpr8+1, respectively.

This fixes the cutscene rendering in Sonic Mania.
2018-08-20 20:45:46 -05:00
Subv
fc5b489b0f Shaders: Log and crash when using an unimplemented texture type in a texture sampling instruction. 2018-08-20 20:44:56 -05:00
bunnei
19b05c3f55 Merge pull request #1122 from lioncash/acc
acc/profile_manager: General cleanup
2018-08-20 20:54:34 -04:00
bunnei
2788144f46 Merge pull request #1125 from bunnei/update-dynarmic
externals: Update dynarmic to a42f301c.
2018-08-20 20:46:16 -04:00
bunnei
dd70ddad7e Merge pull request #1095 from DarkLordZach/sysarchives
filesystem: Add support for loading of system archives
2018-08-20 20:17:57 -04:00
James Rowe
c0fb321935 Merge pull request #1127 from yuzu-emu/revert-838-port-3616
Revert "Port #3616 from Citra: "appveyor: set jobs to 4 for mingw""
2018-08-20 18:14:54 -06:00
Zach Hilman
34f3d58470 Revert "Port #3616 from Citra: "appveyor: set jobs to 4 for mingw"" 2018-08-20 20:13:28 -04:00
Lioncash
b5fb246a99 telemetry_session: Don't allocate std::string instances for program lifetime in GetTelemetryId() and RegenerateTelemetryId()
Given these functions aren't intended to be used frequently, there's no
need to keep the std::string instances allocated for the whole lifetime
of the program. It's just a waste of memory.
2018-08-20 20:06:25 -04:00
bunnei
c6fda4c758 externals: Update dynarmic to a42f301c. 2018-08-20 19:50:49 -04:00
Lioncash
609cb04f3f acc: Replace profile_manager include with a forward declaration
This is only used in a shared_ptr, so we can forward declare it.
2018-08-20 19:48:57 -04:00
Lioncash
eb88fedc5d acc: Simplify WriteBuffer call within LoadImage()
We have an overload of WriteBuffer that accepts containers that satisfy
the ContiguousContainer concept, which std::array does, so we only need
to pass in the array itself.
2018-08-20 19:48:57 -04:00
Lioncash
f5b132676f acc: Correct IProfile's constructor initializer list order
Arranges them in the order the members would be initialized
2018-08-20 19:48:57 -04:00
Lioncash
0fcdf37917 acc: Remove unused DEFAULT_USER_ID
This is no longer used, so it can be removed.
2018-08-20 19:48:57 -04:00
Lioncash
350f6e0aa4 profile_manager: Use INVALID_UUID in the initializer of last_opened_user
Makes it a little bit more self-documenting.
2018-08-20 19:48:57 -04:00
Lioncash
9d8f19d7bf profile_manager: Remove unnecessary memcpy in GetProfileBaseAndData()
Given the source and destination types are the same std::array type, we
can simply use regular assignment to perform the same behavior.
2018-08-20 19:48:57 -04:00
Lioncash
38cd4e9c61 profile_manager: Use type aliases for username data, profile data, and user arrays
Avoids the need to repeatedly specify the whole array type in multiple
places.
2018-08-20 19:48:57 -04:00
Lioncash
f9a26d468c profile_manager: Take ProfileInfo by const reference where applicable
ProfileInfo is quite a large struct in terms of data, and we don't need
to perform a copy in these instances, so we can just pass constant
references instead.
2018-08-20 19:48:57 -04:00
Lioncash
1277556c69 profile_manager: Make array parameter to CreateNewUser a const reference
This doesn't modify the passed in array, so this can be a const
reference.
2018-08-20 19:48:57 -04:00
Lioncash
dfdf4a46fe profile_manager: Remove unnecessary static
This can just be constexpr like the others
2018-08-20 19:48:57 -04:00
Lioncash
69dd37d874 profile_manager: Simplify UUID's two param constructor, operator==, and operator bool
We can use the constructor initializer list and just compare the
contained u128's together instead of comparing each element
individually. Ditto for comparing against an invalid UUID.
2018-08-20 19:48:57 -04:00
Lioncash
f13a66b963 profile_manager: Move UUID generation function to the cpp file
This avoids needing to dump the contents of <random> into other files
that include the profile manager header.
2018-08-20 19:48:53 -04:00
bunnei
b1d238bbb8 Merge pull request #1064 from lioncash/telemetry
common/telemetry: Migrate core-independent info gathering to common
2018-08-20 19:43:17 -04:00
Subv
7784ce1854 Shaders: Write all the enabled color outputs when a fragment shader exits.
We were only writing to the first render target before.
Note that this is only the GLSL side of the implementation, supporting multiple render targets requires more changes in the OpenGL renderer.

Dual Source blending is not implemented and stuff that uses it might not work at all.
2018-08-20 17:31:25 -05:00
Zach Hilman
e8cb6f5c9b registration: Add Data_Unknown5 NCAContentType 2018-08-20 17:34:18 -04:00
Lioncash
9e9a4bb3a7 profile_manager: Remove unnecessary std::move in AddToProfiles() and CreateNewUser()
Moving a const reference isn't possible, so this just results in a copy
(and given ProfileInfo is composed of trivial types and aggregates, a
move wouldn't really do anything).
2018-08-20 17:18:31 -04:00
bunnei
028d90eb79 Merge pull request #1104 from Subv/instanced_arrays
GLRasterizer: Implemented instanced vertex arrays.
2018-08-20 14:32:50 -04:00
bunnei
296e57fa0e Merge pull request #1115 from Subv/texs_mask
Shaders/TEXS: Write to the correct output register when swizzling.
2018-08-20 14:31:33 -04:00
bunnei
b20ed93884 Merge pull request #1112 from Subv/sampler_types
Shaders: Use the correct shader type when sampling textures.
2018-08-20 14:30:45 -04:00
bunnei
185b35bfcd Merge pull request #1117 from ogniK5377/CheckFreeCommunicationPermission
Added CheckFreeCommunicationPermission
2018-08-20 11:00:26 -04:00
bunnei
943771e703 Merge pull request #1017 from ogniK5377/better-account
New account backend to allow for future extended support
2018-08-20 10:59:15 -04:00
bunnei
ce4b77bd7d Merge pull request #1120 from ogniK5377/rgba8-uint
Implemented RGBA8_UINT
2018-08-20 10:54:51 -04:00
bunnei
6ee8b15abe Merge pull request #1119 from lioncash/uninit
game_list: Avoid uninitialized variables when retrieving program ID
2018-08-20 10:53:52 -04:00
Lioncash
ffd60ee476 game_list: Avoid uninitialized variables when retrieving program ID
Avoids potentially leaving this variable uninitialized based off the
loader failing to retrieve the ID value.
2018-08-20 04:23:05 -04:00
David Marcec
8a88110060 Added CheckFreeCommunicationPermission
This fixes save files not loading in splatoon 2
2018-08-20 18:14:49 +10:00
Subv
6cf719a4ab Shaders/TEXS: Fixed the component mask in the TEXS instruction.
Previously we could end up with a TEXS that didn't write any outputs, this was wrong.
2018-08-19 17:09:40 -05:00
Subv
7fb406c3fc Shader: Implemented the TLD4 and TLD4S opcodes using GLSL's textureGather.
It is unknown how TLD4S determines the sampler type, more research is needed.
2018-08-19 12:57:58 -05:00
Subv
3ef4b3d4b4 Shader: Use the right sampler type in the TEX, TEXS and TLDS instructions.
Different sampler types have their parameters in different registers.
2018-08-19 12:57:54 -05:00
Subv
73b937b190 Shader: Added bitfields for the texture type of the various sampling instructions. 2018-08-19 12:57:51 -05:00
Subv
656758fd81 Shaders: Added decodings for TLD4 and TLD4S 2018-08-19 12:57:08 -05:00
Zach Hilman
27da7bc9da filesystem: Add support for loading of system archives 2018-08-18 21:28:23 -04:00
Subv
e0f66c1fbf GLRasterizer: Implemented instanced vertex arrays.
Before each draw call, for every enabled vertex array configured as instanced, we take the current instance id and divide it by its configured divisor, then we multiply that by the corresponding stride and increment the start address by the resulting amount. This way we can simulate the vertex array being incremented once per instance without actually using OpenGL's instancing functions.
2018-08-18 14:42:26 -05:00
Lioncash
60f476cd8f common/telemetry: Migrate core-independent info gathering to common
Previously core itself was the library containing the code to gather
common information (build info, CPU info, and OS info), however all of
this isn't core-dependent and can be moved to the common code and use
the common interfaces. We can then just call those functions from the
core instead.

This will allow replacing our CPU detection with Xbyak's which has
better detection facilities than ours. It also keeps more
architecture-dependent code in common instead of core.
2018-08-14 18:57:46 -04:00
David Marcec
10f494eefe Better UUID randomness 2018-08-12 02:31:43 +10:00
David Marcec
448290bee4 Removed un-needed count from ListOpenUsers and ListAllUsers 2018-08-12 02:11:04 +10:00
David Marcec
2592e41301 Added better explanations in the profile manager 2018-08-12 01:51:31 +10:00
David Marcec
0b6f8ba51e Code cleanup for profile manager 2018-08-12 01:34:22 +10:00
David Marcec
d0b2950434 Removed const from ProfileBase Invalidate 2018-08-12 00:41:17 +10:00
David Marcec
42431d2aa6 fixed invalid uuid bool operator 2018-08-11 21:29:10 +10:00
David Marcec
b8e70faa2d Added GetOpenUserCount 2018-08-11 20:45:06 +10:00
David Marcec
662218e997 Removed all for loops from the profile manager 2018-08-11 20:15:59 +10:00
David Marcec
c3013c7c9c Added missing ListAllUsers count 2018-08-11 20:06:06 +10:00
David Marcec
acff922762 If statement style change 2018-08-11 18:46:42 +10:00
David Marcec
dfea525cbe Second round of account changes 2018-08-11 18:26:13 +10:00
David Marcec
82fa0bcea7 First round of account changes 2018-08-11 16:47:33 +10:00
David Marcec
4fa4d80c68 Rebase with dynarmic master 2018-08-11 13:35:04 +10:00
David Marcec
6aa8ee6943 Refactored profile manager sharing 2018-08-11 13:17:06 +10:00
David Marcec
b76ddb7647 Merge remote-tracking branch 'origin/master' into better-account 2018-08-11 10:35:47 +10:00
David Marcec
2a3b335b15 Added IsUserRegistrationRequestPermitted 2018-08-11 10:33:11 +10:00
David Marcec
4e1471ef21 Don't add user if the uuid already exists 2018-08-09 13:30:58 +10:00
David Marcec
e9978fd4f5 Open first user added 2018-08-09 01:37:55 +10:00
David Marcec
75169c7570 Inital pass of account backend implementation
This commit verified working on puyo
2018-08-09 01:09:12 +10:00
David Marcec
03d7faf583 GetProfileBase and GetProfileBaseAndData added 2018-08-08 23:41:12 +10:00
David Marcec
6f691e71bf began initial implementation of "ProfileManager" 2018-08-08 22:26:42 +10:00
David Marcec
5f8d253ce0 Switched uuids from u128 to new UUID struct 2018-08-08 21:09:45 +10:00
36 changed files with 1021 additions and 230 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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;

View 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

View File

@@ -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;
};
};

View File

@@ -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"},

View File

@@ -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

View File

@@ -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"},

View File

@@ -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

View File

@@ -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"},

View File

@@ -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

View File

@@ -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"},

View File

@@ -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

View 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

View 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

View File

@@ -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,

View File

@@ -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();

View File

@@ -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}",

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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"),

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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;
}
}

View File

@@ -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 = " ";