summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--appveyor.yml2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/acc/acc.cpp126
-rw-r--r--src/core/hle/service/acc/acc.h6
-rw-r--r--src/core/hle/service/acc/acc_aa.cpp3
-rw-r--r--src/core/hle/service/acc/acc_aa.h3
-rw-r--r--src/core/hle/service/acc/acc_su.cpp5
-rw-r--r--src/core/hle/service/acc/acc_su.h3
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp5
-rw-r--r--src/core/hle/service/acc/acc_u0.h3
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp5
-rw-r--r--src/core/hle/service/acc/acc_u1.h3
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp226
-rw-r--r--src/core/hle/service/acc/profile_manager.h124
-rw-r--r--src/core/hle/service/pctl/module.cpp9
-rw-r--r--src/video_core/engines/shader_bytecode.h111
-rw-r--r--src/video_core/gpu.cpp1
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h98
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp331
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h50
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h2
-rw-r--r--src/yuzu/game_list.cpp4
-rw-r--r--src/yuzu/main.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
26 files changed, 918 insertions, 212 deletions
diff --git a/appveyor.yml b/appveyor.yml
index a6f12b267..436d98fa1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -53,7 +53,7 @@ build_script:
53 # https://www.appveyor.com/docs/build-phase 53 # https://www.appveyor.com/docs/build-phase
54 msbuild msvc_build/yuzu.sln /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 54 msbuild msvc_build/yuzu.sln /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
55 } else { 55 } else {
56 C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -C mingw_build/ 2>&1' 56 C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -j4 -C mingw_build/ 2>&1'
57 } 57 }
58 58
59after_build: 59after_build:
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 67ad6109a..31a7bf6fd 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -122,6 +122,8 @@ add_library(core STATIC
122 hle/service/acc/acc_u0.h 122 hle/service/acc/acc_u0.h
123 hle/service/acc/acc_u1.cpp 123 hle/service/acc/acc_u1.cpp
124 hle/service/acc/acc_u1.h 124 hle/service/acc/acc_u1.h
125 hle/service/acc/profile_manager.cpp
126 hle/service/acc/profile_manager.h
125 hle/service/am/am.cpp 127 hle/service/am/am.cpp
126 hle/service/am/am.h 128 hle/service/am/am.h
127 hle/service/am/applet_ae.cpp 129 hle/service/am/applet_ae.cpp
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index f3c5b1b9c..979f2f892 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -3,7 +3,10 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include "common/common_types.h"
6#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/swap.h"
9#include "core/core_timing.h"
7#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/acc/acc.h" 11#include "core/hle/service/acc/acc.h"
9#include "core/hle/service/acc/acc_aa.h" 12#include "core/hle/service/acc/acc_aa.h"
@@ -13,7 +16,6 @@
13#include "core/settings.h" 16#include "core/settings.h"
14 17
15namespace Service::Account { 18namespace Service::Account {
16
17// TODO: RE this structure 19// TODO: RE this structure
18struct UserData { 20struct UserData {
19 INSERT_PADDING_WORDS(1); 21 INSERT_PADDING_WORDS(1);
@@ -25,19 +27,13 @@ struct UserData {
25}; 27};
26static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); 28static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
27 29
28struct ProfileBase {
29 u128 user_id;
30 u64 timestamp;
31 std::array<u8, 0x20> username;
32};
33static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size");
34
35// TODO(ogniK): Generate a real user id based on username, md5(username) maybe? 30// TODO(ogniK): Generate a real user id based on username, md5(username) maybe?
36static constexpr u128 DEFAULT_USER_ID{1ull, 0ull}; 31static UUID DEFAULT_USER_ID{1ull, 0ull};
37 32
38class IProfile final : public ServiceFramework<IProfile> { 33class IProfile final : public ServiceFramework<IProfile> {
39public: 34public:
40 explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) { 35 explicit IProfile(UUID user_id, ProfileManager& profile_manager)
36 : ServiceFramework("IProfile"), user_id(user_id), profile_manager(profile_manager) {
41 static const FunctionInfo functions[] = { 37 static const FunctionInfo functions[] = {
42 {0, &IProfile::Get, "Get"}, 38 {0, &IProfile::Get, "Get"},
43 {1, &IProfile::GetBase, "GetBase"}, 39 {1, &IProfile::GetBase, "GetBase"},
@@ -49,38 +45,34 @@ public:
49 45
50private: 46private:
51 void Get(Kernel::HLERequestContext& ctx) { 47 void Get(Kernel::HLERequestContext& ctx) {
52 LOG_WARNING(Service_ACC, "(STUBBED) called"); 48 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
53 ProfileBase profile_base{}; 49 ProfileBase profile_base{};
54 profile_base.user_id = user_id; 50 std::array<u8, MAX_DATA> data{};
55 if (Settings::values.username.size() > profile_base.username.size()) { 51 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
56 std::copy_n(Settings::values.username.begin(), profile_base.username.size(), 52 ctx.WriteBuffer(data);
57 profile_base.username.begin()); 53 IPC::ResponseBuilder rb{ctx, 16};
54 rb.Push(RESULT_SUCCESS);
55 rb.PushRaw(profile_base);
58 } else { 56 } else {
59 std::copy(Settings::values.username.begin(), Settings::values.username.end(), 57 LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
60 profile_base.username.begin()); 58 user_id.Format());
59 IPC::ResponseBuilder rb{ctx, 2};
60 rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
61 } 61 }
62
63 IPC::ResponseBuilder rb{ctx, 16};
64 rb.Push(RESULT_SUCCESS);
65 rb.PushRaw(profile_base);
66 } 62 }
67 63
68 void GetBase(Kernel::HLERequestContext& ctx) { 64 void GetBase(Kernel::HLERequestContext& ctx) {
69 LOG_WARNING(Service_ACC, "(STUBBED) called"); 65 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
70
71 // TODO(Subv): Retrieve this information from somewhere.
72 ProfileBase profile_base{}; 66 ProfileBase profile_base{};
73 profile_base.user_id = user_id; 67 if (profile_manager.GetProfileBase(user_id, profile_base)) {
74 if (Settings::values.username.size() > profile_base.username.size()) { 68 IPC::ResponseBuilder rb{ctx, 16};
75 std::copy_n(Settings::values.username.begin(), profile_base.username.size(), 69 rb.Push(RESULT_SUCCESS);
76 profile_base.username.begin()); 70 rb.PushRaw(profile_base);
77 } else { 71 } else {
78 std::copy(Settings::values.username.begin(), Settings::values.username.end(), 72 LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
79 profile_base.username.begin()); 73 IPC::ResponseBuilder rb{ctx, 2};
74 rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
80 } 75 }
81 IPC::ResponseBuilder rb{ctx, 16};
82 rb.Push(RESULT_SUCCESS);
83 rb.PushRaw(profile_base);
84 } 76 }
85 77
86 void LoadImage(Kernel::HLERequestContext& ctx) { 78 void LoadImage(Kernel::HLERequestContext& ctx) {
@@ -104,7 +96,8 @@ private:
104 rb.Push<u32>(jpeg_size); 96 rb.Push<u32>(jpeg_size);
105 } 97 }
106 98
107 u128 user_id; ///< The user id this profile refers to. 99 const ProfileManager& profile_manager;
100 UUID user_id; ///< The user id this profile refers to.
108}; 101};
109 102
110class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { 103class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
@@ -141,44 +134,57 @@ private:
141}; 134};
142 135
143void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { 136void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
144 LOG_WARNING(Service_ACC, "(STUBBED) called"); 137 LOG_INFO(Service_ACC, "called");
145 IPC::ResponseBuilder rb{ctx, 3}; 138 IPC::ResponseBuilder rb{ctx, 3};
146 rb.Push(RESULT_SUCCESS); 139 rb.Push(RESULT_SUCCESS);
147 rb.Push<u32>(1); 140 rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
148} 141}
149 142
150void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { 143void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
151 LOG_WARNING(Service_ACC, "(STUBBED) called"); 144 IPC::RequestParser rp{ctx};
145 UUID user_id = rp.PopRaw<UUID>();
146 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
147
152 IPC::ResponseBuilder rb{ctx, 3}; 148 IPC::ResponseBuilder rb{ctx, 3};
153 rb.Push(RESULT_SUCCESS); 149 rb.Push(RESULT_SUCCESS);
154 rb.Push(true); // TODO: Check when this is supposed to return true and when not 150 rb.Push(profile_manager->UserExists(user_id));
155} 151}
156 152
157void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { 153void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
158 LOG_WARNING(Service_ACC, "(STUBBED) called"); 154 LOG_INFO(Service_ACC, "called");
159 // TODO(Subv): There is only one user for now. 155 ctx.WriteBuffer(profile_manager->GetAllUsers());
160 const std::vector<u128> user_ids = {DEFAULT_USER_ID};
161 ctx.WriteBuffer(user_ids);
162 IPC::ResponseBuilder rb{ctx, 2}; 156 IPC::ResponseBuilder rb{ctx, 2};
163 rb.Push(RESULT_SUCCESS); 157 rb.Push(RESULT_SUCCESS);
164} 158}
165 159
166void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { 160void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
167 LOG_WARNING(Service_ACC, "(STUBBED) called"); 161 LOG_INFO(Service_ACC, "called");
168 // TODO(Subv): There is only one user for now. 162 ctx.WriteBuffer(profile_manager->GetOpenUsers());
169 const std::vector<u128> user_ids = {DEFAULT_USER_ID};
170 ctx.WriteBuffer(user_ids);
171 IPC::ResponseBuilder rb{ctx, 2}; 163 IPC::ResponseBuilder rb{ctx, 2};
172 rb.Push(RESULT_SUCCESS); 164 rb.Push(RESULT_SUCCESS);
173} 165}
174 166
167void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
168 LOG_INFO(Service_ACC, "called");
169 IPC::ResponseBuilder rb{ctx, 6};
170 rb.Push(RESULT_SUCCESS);
171 rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser());
172}
173
175void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { 174void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
176 IPC::RequestParser rp{ctx}; 175 IPC::RequestParser rp{ctx};
177 u128 user_id = rp.PopRaw<u128>(); 176 UUID user_id = rp.PopRaw<UUID>();
178 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 177 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
179 rb.Push(RESULT_SUCCESS); 178 rb.Push(RESULT_SUCCESS);
180 rb.PushIpcInterface<IProfile>(user_id); 179 rb.PushIpcInterface<IProfile>(user_id, *profile_manager);
181 LOG_DEBUG(Service_ACC, "called user_id=0x{:016X}{:016X}", user_id[1], user_id[0]); 180 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
181}
182
183void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx) {
184 LOG_WARNING(Service_ACC, "(STUBBED) called");
185 IPC::ResponseBuilder rb{ctx, 3};
186 rb.Push(RESULT_SUCCESS);
187 rb.Push(profile_manager->CanSystemRegisterUser());
182} 188}
183 189
184void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { 190void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
@@ -194,22 +200,18 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
194 LOG_DEBUG(Service_ACC, "called"); 200 LOG_DEBUG(Service_ACC, "called");
195} 201}
196 202
197void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { 203Module::Interface::Interface(std::shared_ptr<Module> module,
198 LOG_WARNING(Service_ACC, "(STUBBED) called"); 204 std::shared_ptr<ProfileManager> profile_manager, const char* name)
199 IPC::ResponseBuilder rb{ctx, 6}; 205 : ServiceFramework(name), module(std::move(module)),
200 rb.Push(RESULT_SUCCESS); 206 profile_manager(std::move(profile_manager)) {}
201 rb.PushRaw(DEFAULT_USER_ID);
202}
203
204Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
205 : ServiceFramework(name), module(std::move(module)) {}
206 207
207void InstallInterfaces(SM::ServiceManager& service_manager) { 208void InstallInterfaces(SM::ServiceManager& service_manager) {
208 auto module = std::make_shared<Module>(); 209 auto module = std::make_shared<Module>();
209 std::make_shared<ACC_AA>(module)->InstallAsService(service_manager); 210 auto profile_manager = std::make_shared<ProfileManager>();
210 std::make_shared<ACC_SU>(module)->InstallAsService(service_manager); 211 std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
211 std::make_shared<ACC_U0>(module)->InstallAsService(service_manager); 212 std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
212 std::make_shared<ACC_U1>(module)->InstallAsService(service_manager); 213 std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
214 std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
213} 215}
214 216
215} // namespace Service::Account 217} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 88cabaa01..d7c6d2415 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/acc/profile_manager.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service::Account { 10namespace Service::Account {
@@ -12,7 +13,8 @@ class Module final {
12public: 13public:
13 class Interface : public ServiceFramework<Interface> { 14 class Interface : public ServiceFramework<Interface> {
14 public: 15 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 16 explicit Interface(std::shared_ptr<Module> module,
17 std::shared_ptr<ProfileManager> profile_manager, const char* name);
16 18
17 void GetUserCount(Kernel::HLERequestContext& ctx); 19 void GetUserCount(Kernel::HLERequestContext& ctx);
18 void GetUserExistence(Kernel::HLERequestContext& ctx); 20 void GetUserExistence(Kernel::HLERequestContext& ctx);
@@ -22,9 +24,11 @@ public:
22 void GetProfile(Kernel::HLERequestContext& ctx); 24 void GetProfile(Kernel::HLERequestContext& ctx);
23 void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); 25 void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
24 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); 26 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
27 void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
25 28
26 protected: 29 protected:
27 std::shared_ptr<Module> module; 30 std::shared_ptr<Module> module;
31 std::shared_ptr<ProfileManager> profile_manager;
28 }; 32 };
29}; 33};
30 34
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp
index 280b3e464..9bd595a37 100644
--- a/src/core/hle/service/acc/acc_aa.cpp
+++ b/src/core/hle/service/acc/acc_aa.cpp
@@ -6,7 +6,8 @@
6 6
7namespace Service::Account { 7namespace Service::Account {
8 8
9ACC_AA::ACC_AA(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:aa") { 9ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") {
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {0, nullptr, "EnsureCacheAsync"}, 12 {0, nullptr, "EnsureCacheAsync"},
12 {1, nullptr, "LoadCache"}, 13 {1, nullptr, "LoadCache"},
diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h
index 796f7ef85..2e08c781a 100644
--- a/src/core/hle/service/acc/acc_aa.h
+++ b/src/core/hle/service/acc/acc_aa.h
@@ -10,7 +10,8 @@ namespace Service::Account {
10 10
11class ACC_AA final : public Module::Interface { 11class ACC_AA final : public Module::Interface {
12public: 12public:
13 explicit ACC_AA(std::shared_ptr<Module> module); 13 explicit ACC_AA(std::shared_ptr<Module> module,
14 std::shared_ptr<ProfileManager> profile_manager);
14}; 15};
15 16
16} // namespace Service::Account 17} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index 8b2a71f37..0218ee859 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -6,7 +6,8 @@
6 6
7namespace Service::Account { 7namespace Service::Account {
8 8
9ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:su") { 9ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {0, &ACC_SU::GetUserCount, "GetUserCount"}, 12 {0, &ACC_SU::GetUserCount, "GetUserCount"},
12 {1, &ACC_SU::GetUserExistence, "GetUserExistence"}, 13 {1, &ACC_SU::GetUserExistence, "GetUserExistence"},
@@ -15,7 +16,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
15 {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, 16 {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"},
16 {5, &ACC_SU::GetProfile, "GetProfile"}, 17 {5, &ACC_SU::GetProfile, "GetProfile"},
17 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
18 {50, nullptr, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
19 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, nullptr, "TrySelectUserWithoutInteraction"},
20 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
21 {100, nullptr, "GetUserRegistrationNotifier"}, 22 {100, nullptr, "GetUserRegistrationNotifier"},
diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h
index 3894a6991..79a47d88d 100644
--- a/src/core/hle/service/acc/acc_su.h
+++ b/src/core/hle/service/acc/acc_su.h
@@ -11,7 +11,8 @@ namespace Account {
11 11
12class ACC_SU final : public Module::Interface { 12class ACC_SU final : public Module::Interface {
13public: 13public:
14 explicit ACC_SU(std::shared_ptr<Module> module); 14 explicit ACC_SU(std::shared_ptr<Module> module,
15 std::shared_ptr<ProfileManager> profile_manager);
15}; 16};
16 17
17} // namespace Account 18} // namespace Account
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index d84c8b2e1..84a4d05b8 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -6,7 +6,8 @@
6 6
7namespace Service::Account { 7namespace Service::Account {
8 8
9ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u0") { 9ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {0, &ACC_U0::GetUserCount, "GetUserCount"}, 12 {0, &ACC_U0::GetUserCount, "GetUserCount"},
12 {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, 13 {1, &ACC_U0::GetUserExistence, "GetUserExistence"},
@@ -15,7 +16,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
15 {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, 16 {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
16 {5, &ACC_U0::GetProfile, "GetProfile"}, 17 {5, &ACC_U0::GetProfile, "GetProfile"},
17 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
18 {50, nullptr, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
19 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, nullptr, "TrySelectUserWithoutInteraction"},
20 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
21 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, 22 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h
index 6ded596b3..e8a114f99 100644
--- a/src/core/hle/service/acc/acc_u0.h
+++ b/src/core/hle/service/acc/acc_u0.h
@@ -10,7 +10,8 @@ namespace Service::Account {
10 10
11class ACC_U0 final : public Module::Interface { 11class ACC_U0 final : public Module::Interface {
12public: 12public:
13 explicit ACC_U0(std::shared_ptr<Module> module); 13 explicit ACC_U0(std::shared_ptr<Module> module,
14 std::shared_ptr<ProfileManager> profile_manager);
14}; 15};
15 16
16} // namespace Service::Account 17} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 0ceaf06b5..495693949 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -6,7 +6,8 @@
6 6
7namespace Service::Account { 7namespace Service::Account {
8 8
9ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u1") { 9ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
10 : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {0, &ACC_U1::GetUserCount, "GetUserCount"}, 12 {0, &ACC_U1::GetUserCount, "GetUserCount"},
12 {1, &ACC_U1::GetUserExistence, "GetUserExistence"}, 13 {1, &ACC_U1::GetUserExistence, "GetUserExistence"},
@@ -15,7 +16,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
15 {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, 16 {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"},
16 {5, &ACC_U1::GetProfile, "GetProfile"}, 17 {5, &ACC_U1::GetProfile, "GetProfile"},
17 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
18 {50, nullptr, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
19 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, nullptr, "TrySelectUserWithoutInteraction"},
20 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
21 {100, nullptr, "GetUserRegistrationNotifier"}, 22 {100, nullptr, "GetUserRegistrationNotifier"},
diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h
index 5e3e7659b..a77520e6f 100644
--- a/src/core/hle/service/acc/acc_u1.h
+++ b/src/core/hle/service/acc/acc_u1.h
@@ -10,7 +10,8 @@ namespace Service::Account {
10 10
11class ACC_U1 final : public Module::Interface { 11class ACC_U1 final : public Module::Interface {
12public: 12public:
13 explicit ACC_U1(std::shared_ptr<Module> module); 13 explicit ACC_U1(std::shared_ptr<Module> module,
14 std::shared_ptr<ProfileManager> profile_manager);
14}; 15};
15 16
16} // namespace Service::Account 17} // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
new file mode 100644
index 000000000..62c2121fa
--- /dev/null
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -0,0 +1,226 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <boost/optional.hpp>
6#include "core/hle/service/acc/profile_manager.h"
7#include "core/settings.h"
8
9namespace Service::Account {
10// TODO(ogniK): Get actual error codes
11constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
12constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
13constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
14
15ProfileManager::ProfileManager() {
16 // TODO(ogniK): Create the default user we have for now until loading/saving users is added
17 auto user_uuid = UUID{1, 0};
18 CreateNewUser(user_uuid, Settings::values.username);
19 OpenUser(user_uuid);
20}
21
22/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
23/// internal management of the users profiles
24boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
25 if (user_count >= MAX_USERS) {
26 return boost::none;
27 }
28 profiles[user_count] = std::move(user);
29 return user_count++;
30}
31
32/// Deletes a specific profile based on it's profile index
33bool ProfileManager::RemoveProfileAtIndex(size_t index) {
34 if (index >= MAX_USERS || index >= user_count) {
35 return false;
36 }
37 if (index < user_count - 1) {
38 std::rotate(profiles.begin() + index, profiles.begin() + index + 1, profiles.end());
39 }
40 profiles.back() = {};
41 user_count--;
42 return true;
43}
44
45/// Helper function to register a user to the system
46ResultCode ProfileManager::AddUser(ProfileInfo user) {
47 if (AddToProfiles(user) == boost::none) {
48 return ERROR_TOO_MANY_USERS;
49 }
50 return RESULT_SUCCESS;
51}
52
53/// Create a new user on the system. If the uuid of the user already exists, the user is not
54/// created.
55ResultCode ProfileManager::CreateNewUser(UUID uuid, std::array<u8, 0x20>& username) {
56 if (user_count == MAX_USERS) {
57 return ERROR_TOO_MANY_USERS;
58 }
59 if (!uuid) {
60 return ERROR_ARGUMENT_IS_NULL;
61 }
62 if (username[0] == 0x0) {
63 return ERROR_ARGUMENT_IS_NULL;
64 }
65 if (std::any_of(profiles.begin(), profiles.end(),
66 [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) {
67 return ERROR_USER_ALREADY_EXISTS;
68 }
69 ProfileInfo profile;
70 profile.user_uuid = std::move(uuid);
71 profile.username = username;
72 profile.data = {};
73 profile.creation_time = 0x0;
74 profile.is_open = false;
75 return AddUser(profile);
76}
77
78/// Creates a new user on the system. This function allows a much simpler method of registration
79/// specifically by allowing an std::string for the username. This is required specifically since
80/// we're loading a string straight from the config
81ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
82 std::array<u8, 0x20> username_output;
83 if (username.size() > username_output.size()) {
84 std::copy_n(username.begin(), username_output.size(), username_output.begin());
85 } else {
86 std::copy(username.begin(), username.end(), username_output.begin());
87 }
88 return CreateNewUser(uuid, username_output);
89}
90
91/// Returns a users profile index based on their user id.
92boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
93 if (!uuid) {
94 return boost::none;
95 }
96 auto iter = std::find_if(profiles.begin(), profiles.end(),
97 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
98 if (iter == profiles.end()) {
99 return boost::none;
100 }
101 return static_cast<size_t>(std::distance(profiles.begin(), iter));
102}
103
104/// Returns a users profile index based on their profile
105boost::optional<size_t> ProfileManager::GetUserIndex(ProfileInfo user) const {
106 return GetUserIndex(user.user_uuid);
107}
108
109/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
110bool ProfileManager::GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const {
111 if (index == boost::none || index >= MAX_USERS) {
112 return false;
113 }
114 const auto& prof_info = profiles[index.get()];
115 profile.user_uuid = prof_info.user_uuid;
116 profile.username = prof_info.username;
117 profile.timestamp = prof_info.creation_time;
118 return true;
119}
120
121/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
122bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
123 auto idx = GetUserIndex(uuid);
124 return GetProfileBase(idx, profile);
125}
126
127/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
128bool ProfileManager::GetProfileBase(ProfileInfo user, ProfileBase& profile) const {
129 return GetProfileBase(user.user_uuid, profile);
130}
131
132/// Returns the current user count on the system. We keep a variable which tracks the count so we
133/// don't have to loop the internal profile array every call.
134size_t ProfileManager::GetUserCount() const {
135 return user_count;
136}
137
138/// Lists the current "opened" users on the system. Users are typically not open until they sign
139/// into something or pick a profile. As of right now users should all be open until qlaunch is
140/// booting
141size_t ProfileManager::GetOpenUserCount() const {
142 return std::count_if(profiles.begin(), profiles.end(),
143 [](const ProfileInfo& p) { return p.is_open; });
144}
145
146/// Checks if a user id exists in our profile manager
147bool ProfileManager::UserExists(UUID uuid) const {
148 return (GetUserIndex(uuid) != boost::none);
149}
150
151/// Opens a specific user
152void ProfileManager::OpenUser(UUID uuid) {
153 auto idx = GetUserIndex(uuid);
154 if (idx == boost::none) {
155 return;
156 }
157 profiles[idx.get()].is_open = true;
158 last_opened_user = uuid;
159}
160
161/// Closes a specific user
162void ProfileManager::CloseUser(UUID uuid) {
163 auto idx = GetUserIndex(uuid);
164 if (idx == boost::none) {
165 return;
166 }
167 profiles[idx.get()].is_open = false;
168}
169
170/// Gets all valid user ids on the system
171std::array<UUID, MAX_USERS> ProfileManager::GetAllUsers() const {
172 std::array<UUID, MAX_USERS> output;
173 std::transform(profiles.begin(), profiles.end(), output.begin(),
174 [](const ProfileInfo& p) { return p.user_uuid; });
175 return output;
176}
177
178/// Get all the open users on the system and zero out the rest of the data. This is specifically
179/// needed for GetOpenUsers and we need to ensure the rest of the output buffer is zero'd out
180std::array<UUID, MAX_USERS> ProfileManager::GetOpenUsers() const {
181 std::array<UUID, MAX_USERS> output;
182 std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
183 if (p.is_open)
184 return p.user_uuid;
185 return UUID{};
186 });
187 std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
188 return output;
189}
190
191/// Returns the last user which was opened
192UUID ProfileManager::GetLastOpenedUser() const {
193 return last_opened_user;
194}
195
196/// Return the users profile base and the unknown arbitary data.
197bool ProfileManager::GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile,
198 std::array<u8, MAX_DATA>& data) const {
199 if (GetProfileBase(index, profile)) {
200 std::memcpy(data.data(), profiles[index.get()].data.data(), MAX_DATA);
201 return true;
202 }
203 return false;
204}
205
206/// Return the users profile base and the unknown arbitary data.
207bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
208 std::array<u8, MAX_DATA>& data) const {
209 auto idx = GetUserIndex(uuid);
210 return GetProfileBaseAndData(idx, profile, data);
211}
212
213/// Return the users profile base and the unknown arbitary data.
214bool ProfileManager::GetProfileBaseAndData(ProfileInfo user, ProfileBase& profile,
215 std::array<u8, MAX_DATA>& data) const {
216 return GetProfileBaseAndData(user.user_uuid, profile, data);
217}
218
219/// Returns if the system is allowing user registrations or not
220bool ProfileManager::CanSystemRegisterUser() const {
221 return false; // TODO(ogniK): Games shouldn't have
222 // access to user registration, when we
223 // emulate qlaunch. Update this to dynamically change.
224}
225
226}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
new file mode 100644
index 000000000..314bccbf9
--- /dev/null
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -0,0 +1,124 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <random>
9#include "boost/optional.hpp"
10#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/result.h"
13
14namespace Service::Account {
15constexpr size_t MAX_USERS = 8;
16constexpr size_t MAX_DATA = 128;
17static const u128 INVALID_UUID = {0, 0};
18
19struct UUID {
20 // UUIDs which are 0 are considered invalid!
21 u128 uuid = INVALID_UUID;
22 UUID() = default;
23 explicit UUID(const u128& id) : uuid{id} {}
24 explicit UUID(const u64 lo, const u64 hi) {
25 uuid[0] = lo;
26 uuid[1] = hi;
27 };
28 explicit operator bool() const {
29 return uuid[0] != INVALID_UUID[0] || uuid[1] != INVALID_UUID[1];
30 }
31
32 bool operator==(const UUID& rhs) const {
33 return std::tie(uuid[0], uuid[1]) == std::tie(rhs.uuid[0], rhs.uuid[1]);
34 }
35
36 bool operator!=(const UUID& rhs) const {
37 return !operator==(rhs);
38 }
39
40 // TODO(ogniK): Properly generate uuids based on RFC-4122
41 const UUID& Generate() {
42 std::random_device device;
43 std::mt19937 gen(device());
44 std::uniform_int_distribution<uint64_t> distribution(1,
45 std::numeric_limits<uint64_t>::max());
46 uuid[0] = distribution(gen);
47 uuid[1] = distribution(gen);
48 return *this;
49 }
50
51 // Set the UUID to {0,0} to be considered an invalid user
52 void Invalidate() {
53 uuid = INVALID_UUID;
54 }
55 std::string Format() const {
56 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
57 }
58};
59static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
60
61/// This holds general information about a users profile. This is where we store all the information
62/// based on a specific user
63struct ProfileInfo {
64 UUID user_uuid;
65 std::array<u8, 0x20> username;
66 u64 creation_time;
67 std::array<u8, MAX_DATA> data; // TODO(ognik): Work out what this is
68 bool is_open;
69};
70
71struct ProfileBase {
72 UUID user_uuid;
73 u64_le timestamp;
74 std::array<u8, 0x20> username;
75
76 // Zero out all the fields to make the profile slot considered "Empty"
77 void Invalidate() {
78 user_uuid.Invalidate();
79 timestamp = 0;
80 username.fill(0);
81 }
82};
83static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
84
85/// The profile manager is used for handling multiple user profiles at once. It keeps track of open
86/// users, all the accounts registered on the "system" as well as fetching individual "ProfileInfo"
87/// objects
88class ProfileManager {
89public:
90 ProfileManager(); // TODO(ogniK): Load from system save
91 ResultCode AddUser(ProfileInfo user);
92 ResultCode CreateNewUser(UUID uuid, std::array<u8, 0x20>& username);
93 ResultCode CreateNewUser(UUID uuid, const std::string& username);
94 boost::optional<size_t> GetUserIndex(const UUID& uuid) const;
95 boost::optional<size_t> GetUserIndex(ProfileInfo user) const;
96 bool GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const;
97 bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
98 bool GetProfileBase(ProfileInfo user, ProfileBase& profile) const;
99 bool GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile,
100 std::array<u8, MAX_DATA>& data) const;
101 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
102 std::array<u8, MAX_DATA>& data) const;
103 bool GetProfileBaseAndData(ProfileInfo user, ProfileBase& profile,
104 std::array<u8, MAX_DATA>& data) const;
105 size_t GetUserCount() const;
106 size_t GetOpenUserCount() const;
107 bool UserExists(UUID uuid) const;
108 void OpenUser(UUID uuid);
109 void CloseUser(UUID uuid);
110 std::array<UUID, MAX_USERS> GetOpenUsers() const;
111 std::array<UUID, MAX_USERS> GetAllUsers() const;
112 UUID GetLastOpenedUser() const;
113
114 bool CanSystemRegisterUser() const;
115
116private:
117 std::array<ProfileInfo, MAX_USERS> profiles{};
118 size_t user_count = 0;
119 boost::optional<size_t> AddToProfiles(const ProfileInfo& profile);
120 bool RemoveProfileAtIndex(size_t index);
121 UUID last_opened_user{0, 0};
122};
123
124}; // namespace Service::Account
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index fcf1f3da3..6cc3b1992 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -14,7 +14,8 @@ public:
14 IParentalControlService() : ServiceFramework("IParentalControlService") { 14 IParentalControlService() : ServiceFramework("IParentalControlService") {
15 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
16 {1, &IParentalControlService::Initialize, "Initialize"}, 16 {1, &IParentalControlService::Initialize, "Initialize"},
17 {1001, nullptr, "CheckFreeCommunicationPermission"}, 17 {1001, &IParentalControlService::CheckFreeCommunicationPermission,
18 "CheckFreeCommunicationPermission"},
18 {1002, nullptr, "ConfirmLaunchApplicationPermission"}, 19 {1002, nullptr, "ConfirmLaunchApplicationPermission"},
19 {1003, nullptr, "ConfirmResumeApplicationPermission"}, 20 {1003, nullptr, "ConfirmResumeApplicationPermission"},
20 {1004, nullptr, "ConfirmSnsPostPermission"}, 21 {1004, nullptr, "ConfirmSnsPostPermission"},
@@ -116,6 +117,12 @@ private:
116 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 117 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
117 rb.Push(RESULT_SUCCESS); 118 rb.Push(RESULT_SUCCESS);
118 } 119 }
120
121 void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) {
122 LOG_WARNING(Service_PCTL, "(STUBBED) called");
123 IPC::ResponseBuilder rb{ctx, 2};
124 rb.Push(RESULT_SUCCESS);
125 }
119}; 126};
120 127
121void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { 128void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b038a9d92..3ba6fe614 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -12,6 +12,7 @@
12 12
13#include <boost/optional.hpp> 13#include <boost/optional.hpp>
14 14
15#include "common/assert.h"
15#include "common/bit_field.h" 16#include "common/bit_field.h"
16#include "common/common_types.h" 17#include "common/common_types.h"
17 18
@@ -79,6 +80,9 @@ union Attribute {
79 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval 80 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
80 // shader. 81 // shader.
81 TessCoordInstanceIDVertexID = 47, 82 TessCoordInstanceIDVertexID = 47,
83 // This attribute contains a tuple of (Unk, Unk, Unk, gl_FrontFacing) when inside a fragment
84 // shader. It is unknown what the other values contain.
85 FrontFacing = 63,
82 }; 86 };
83 87
84 union { 88 union {
@@ -214,6 +218,18 @@ enum class FlowCondition : u64 {
214 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? 218 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
215}; 219};
216 220
221enum class PredicateResultMode : u64 {
222 None = 0x0,
223 NotZero = 0x3,
224};
225
226enum class TextureType : u64 {
227 Texture1D = 0,
228 Texture2D = 1,
229 Texture3D = 2,
230 TextureCube = 3,
231};
232
217union Instruction { 233union Instruction {
218 Instruction& operator=(const Instruction& instr) { 234 Instruction& operator=(const Instruction& instr) {
219 value = instr.value; 235 value = instr.value;
@@ -254,7 +270,7 @@ union Instruction {
254 BitField<39, 1, u64> invert_a; 270 BitField<39, 1, u64> invert_a;
255 BitField<40, 1, u64> invert_b; 271 BitField<40, 1, u64> invert_b;
256 BitField<41, 2, LogicOperation> operation; 272 BitField<41, 2, LogicOperation> operation;
257 BitField<44, 2, u64> unk44; 273 BitField<44, 2, PredicateResultMode> pred_result_mode;
258 BitField<48, 3, Pred> pred48; 274 BitField<48, 3, Pred> pred48;
259 } lop; 275 } lop;
260 276
@@ -284,6 +300,10 @@ union Instruction {
284 } alu; 300 } alu;
285 301
286 union { 302 union {
303 BitField<48, 1, u64> negate_b;
304 } fmul;
305
306 union {
287 BitField<48, 1, u64> is_signed; 307 BitField<48, 1, u64> is_signed;
288 } shift; 308 } shift;
289 309
@@ -421,6 +441,8 @@ union Instruction {
421 } conversion; 441 } conversion;
422 442
423 union { 443 union {
444 BitField<28, 1, u64> array;
445 BitField<29, 2, TextureType> texture_type;
424 BitField<31, 4, u64> component_mask; 446 BitField<31, 4, u64> component_mask;
425 447
426 bool IsComponentEnabled(size_t component) const { 448 bool IsComponentEnabled(size_t component) const {
@@ -429,29 +451,88 @@ union Instruction {
429 } tex; 451 } tex;
430 452
431 union { 453 union {
432 BitField<50, 3, u64> component_mask_selector; 454 BitField<28, 1, u64> array;
455 BitField<29, 2, TextureType> texture_type;
456 BitField<56, 2, u64> component;
457 } tld4;
458
459 union {
460 BitField<52, 2, u64> component;
461 } tld4s;
462
463 union {
433 BitField<0, 8, Register> gpr0; 464 BitField<0, 8, Register> gpr0;
434 BitField<28, 8, Register> gpr28; 465 BitField<28, 8, Register> gpr28;
466 BitField<50, 3, u64> component_mask_selector;
467 BitField<53, 4, u64> texture_info;
468
469 TextureType GetTextureType() const {
470 // The TEXS instruction has a weird encoding for the texture type.
471 if (texture_info == 0)
472 return TextureType::Texture1D;
473 if (texture_info >= 1 && texture_info <= 9)
474 return TextureType::Texture2D;
475 if (texture_info >= 10 && texture_info <= 11)
476 return TextureType::Texture3D;
477 if (texture_info >= 12 && texture_info <= 13)
478 return TextureType::TextureCube;
479
480 UNIMPLEMENTED();
481 }
482
483 bool IsArrayTexture() const {
484 // TEXS only supports Texture2D arrays.
485 return texture_info >= 7 && texture_info <= 9;
486 }
435 487
436 bool HasTwoDestinations() const { 488 bool HasTwoDestinations() const {
437 return gpr28.Value() != Register::ZeroIndex; 489 return gpr28.Value() != Register::ZeroIndex;
438 } 490 }
439 491
440 bool IsComponentEnabled(size_t component) const { 492 bool IsComponentEnabled(size_t component) const {
441 static constexpr std::array<std::array<u32, 8>, 4> mask_lut{ 493 static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{
442 {{}, 494 {},
443 {0x1, 0x2, 0x4, 0x8, 0x3}, 495 {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
444 {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, 496 {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
445 {0x7, 0xb, 0xd, 0xe, 0xf}}}; 497 {0x7, 0xb, 0xd, 0xe, 0xf},
498 }};
446 499
447 size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U}; 500 size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U};
448 index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0; 501 index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0;
449 502
450 return ((1ull << component) & mask_lut[index][component_mask_selector]) != 0; 503 u32 mask = mask_lut[index][component_mask_selector];
504 // A mask of 0 means this instruction uses an unimplemented mask.
505 ASSERT(mask != 0);
506 return ((1ull << component) & mask) != 0;
451 } 507 }
452 } texs; 508 } texs;
453 509
454 union { 510 union {
511 BitField<53, 4, u64> texture_info;
512
513 TextureType GetTextureType() const {
514 // The TLDS instruction has a weird encoding for the texture type.
515 if (texture_info >= 0 && texture_info <= 1) {
516 return TextureType::Texture1D;
517 }
518 if (texture_info == 2 || texture_info == 8 || texture_info == 12 ||
519 texture_info >= 4 && texture_info <= 6) {
520 return TextureType::Texture2D;
521 }
522 if (texture_info == 7) {
523 return TextureType::Texture3D;
524 }
525
526 UNIMPLEMENTED();
527 }
528
529 bool IsArrayTexture() const {
530 // TEXS only supports Texture2D arrays.
531 return texture_info == 8;
532 }
533 } tlds;
534
535 union {
455 BitField<20, 24, u64> target; 536 BitField<20, 24, u64> target;
456 BitField<5, 1, u64> constant_buffer; 537 BitField<5, 1, u64> constant_buffer;
457 538
@@ -513,10 +594,14 @@ public:
513 LD_A, 594 LD_A,
514 LD_C, 595 LD_C,
515 ST_A, 596 ST_A,
597 LDG, // Load from global memory
598 STG, // Store in global memory
516 TEX, 599 TEX,
517 TEXQ, // Texture Query 600 TEXQ, // Texture Query
518 TEXS, // Texture Fetch with scalar/non-vec4 source/destinations 601 TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
519 TLDS, // Texture Load with scalar/non-vec4 source/destinations 602 TLDS, // Texture Load with scalar/non-vec4 source/destinations
603 TLD4, // Texture Load 4
604 TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations
520 EXIT, 605 EXIT,
521 IPA, 606 IPA,
522 FFMA_IMM, // Fused Multiply and Add 607 FFMA_IMM, // Fused Multiply and Add
@@ -724,10 +809,14 @@ private:
724 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 809 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
725 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 810 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
726 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 811 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
812 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
813 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
727 INST("110000----111---", Id::TEX, Type::Memory, "TEX"), 814 INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
728 INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), 815 INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"),
729 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), 816 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
730 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), 817 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
818 INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"),
819 INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"),
731 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), 820 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
732 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 821 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
733 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 822 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 5a593c1f7..9758adcfd 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -55,6 +55,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
55 case RenderTargetFormat::RGBA8_UNORM: 55 case RenderTargetFormat::RGBA8_UNORM:
56 case RenderTargetFormat::RGBA8_SNORM: 56 case RenderTargetFormat::RGBA8_SNORM:
57 case RenderTargetFormat::RGBA8_SRGB: 57 case RenderTargetFormat::RGBA8_SRGB:
58 case RenderTargetFormat::RGBA8_UINT:
58 case RenderTargetFormat::RGB10_A2_UNORM: 59 case RenderTargetFormat::RGB10_A2_UNORM:
59 case RenderTargetFormat::BGRA8_UNORM: 60 case RenderTargetFormat::BGRA8_UNORM:
60 case RenderTargetFormat::RG16_UNORM: 61 case RenderTargetFormat::RG16_UNORM:
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 97dcccb92..2697e1c27 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -30,6 +30,7 @@ enum class RenderTargetFormat : u32 {
30 RGBA8_UNORM = 0xD5, 30 RGBA8_UNORM = 0xD5,
31 RGBA8_SRGB = 0xD6, 31 RGBA8_SRGB = 0xD6,
32 RGBA8_SNORM = 0xD7, 32 RGBA8_SNORM = 0xD7,
33 RGBA8_UINT = 0xD9,
33 RG16_UNORM = 0xDA, 34 RG16_UNORM = 0xDA,
34 RG16_SNORM = 0xDB, 35 RG16_SNORM = 0xDB,
35 RG16_SINT = 0xDC, 36 RG16_SINT = 0xDC,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 38aa067b6..fb7476fb8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -94,6 +94,7 @@ struct FormatTuple {
94static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ 94static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
95 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U 95 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U
96 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S 96 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S
97 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI
97 {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U 98 {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U
98 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, 99 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm,
99 false}, // A2B10G10R10U 100 false}, // A2B10G10R10U
@@ -245,6 +246,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
245 // clang-format off 246 // clang-format off
246 MortonCopy<true, PixelFormat::ABGR8U>, 247 MortonCopy<true, PixelFormat::ABGR8U>,
247 MortonCopy<true, PixelFormat::ABGR8S>, 248 MortonCopy<true, PixelFormat::ABGR8S>,
249 MortonCopy<true, PixelFormat::ABGR8UI>,
248 MortonCopy<true, PixelFormat::B5G6R5U>, 250 MortonCopy<true, PixelFormat::B5G6R5U>,
249 MortonCopy<true, PixelFormat::A2B10G10R10U>, 251 MortonCopy<true, PixelFormat::A2B10G10R10U>,
250 MortonCopy<true, PixelFormat::A1B5G5R5U>, 252 MortonCopy<true, PixelFormat::A1B5G5R5U>,
@@ -299,6 +301,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
299 // clang-format off 301 // clang-format off
300 MortonCopy<false, PixelFormat::ABGR8U>, 302 MortonCopy<false, PixelFormat::ABGR8U>,
301 MortonCopy<false, PixelFormat::ABGR8S>, 303 MortonCopy<false, PixelFormat::ABGR8S>,
304 MortonCopy<false, PixelFormat::ABGR8UI>,
302 MortonCopy<false, PixelFormat::B5G6R5U>, 305 MortonCopy<false, PixelFormat::B5G6R5U>,
303 MortonCopy<false, PixelFormat::A2B10G10R10U>, 306 MortonCopy<false, PixelFormat::A2B10G10R10U>,
304 MortonCopy<false, PixelFormat::A1B5G5R5U>, 307 MortonCopy<false, PixelFormat::A1B5G5R5U>,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index beec01746..fc8b44219 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -25,59 +25,60 @@ struct SurfaceParams {
25 enum class PixelFormat { 25 enum class PixelFormat {
26 ABGR8U = 0, 26 ABGR8U = 0,
27 ABGR8S = 1, 27 ABGR8S = 1,
28 B5G6R5U = 2, 28 ABGR8UI = 2,
29 A2B10G10R10U = 3, 29 B5G6R5U = 3,
30 A1B5G5R5U = 4, 30 A2B10G10R10U = 4,
31 R8U = 5, 31 A1B5G5R5U = 5,
32 R8UI = 6, 32 R8U = 6,
33 RGBA16F = 7, 33 R8UI = 7,
34 RGBA16U = 8, 34 RGBA16F = 8,
35 RGBA16UI = 9, 35 RGBA16U = 9,
36 R11FG11FB10F = 10, 36 RGBA16UI = 10,
37 RGBA32UI = 11, 37 R11FG11FB10F = 11,
38 DXT1 = 12, 38 RGBA32UI = 12,
39 DXT23 = 13, 39 DXT1 = 13,
40 DXT45 = 14, 40 DXT23 = 14,
41 DXN1 = 15, // This is also known as BC4 41 DXT45 = 15,
42 DXN2UNORM = 16, 42 DXN1 = 16, // This is also known as BC4
43 DXN2SNORM = 17, 43 DXN2UNORM = 17,
44 BC7U = 18, 44 DXN2SNORM = 18,
45 ASTC_2D_4X4 = 19, 45 BC7U = 19,
46 G8R8U = 20, 46 ASTC_2D_4X4 = 20,
47 G8R8S = 21, 47 G8R8U = 21,
48 BGRA8 = 22, 48 G8R8S = 22,
49 RGBA32F = 23, 49 BGRA8 = 23,
50 RG32F = 24, 50 RGBA32F = 24,
51 R32F = 25, 51 RG32F = 25,
52 R16F = 26, 52 R32F = 26,
53 R16U = 27, 53 R16F = 27,
54 R16S = 28, 54 R16U = 28,
55 R16UI = 29, 55 R16S = 29,
56 R16I = 30, 56 R16UI = 30,
57 RG16 = 31, 57 R16I = 31,
58 RG16F = 32, 58 RG16 = 32,
59 RG16UI = 33, 59 RG16F = 33,
60 RG16I = 34, 60 RG16UI = 34,
61 RG16S = 35, 61 RG16I = 35,
62 RGB32F = 36, 62 RG16S = 36,
63 SRGBA8 = 37, 63 RGB32F = 37,
64 RG8U = 38, 64 SRGBA8 = 38,
65 RG8S = 39, 65 RG8U = 39,
66 RG32UI = 40, 66 RG8S = 40,
67 R32UI = 41, 67 RG32UI = 41,
68 R32UI = 42,
68 69
69 MaxColorFormat, 70 MaxColorFormat,
70 71
71 // Depth formats 72 // Depth formats
72 Z32F = 42, 73 Z32F = 43,
73 Z16 = 43, 74 Z16 = 44,
74 75
75 MaxDepthFormat, 76 MaxDepthFormat,
76 77
77 // DepthStencil formats 78 // DepthStencil formats
78 Z24S8 = 44, 79 Z24S8 = 45,
79 S8Z24 = 45, 80 S8Z24 = 46,
80 Z32FS8 = 46, 81 Z32FS8 = 47,
81 82
82 MaxDepthStencilFormat, 83 MaxDepthStencilFormat,
83 84
@@ -117,6 +118,7 @@ struct SurfaceParams {
117 constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{ 118 constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
118 1, // ABGR8U 119 1, // ABGR8U
119 1, // ABGR8S 120 1, // ABGR8S
121 1, // ABGR8UI
120 1, // B5G6R5U 122 1, // B5G6R5U
121 1, // A2B10G10R10U 123 1, // A2B10G10R10U
122 1, // A1B5G5R5U 124 1, // A1B5G5R5U
@@ -175,6 +177,7 @@ struct SurfaceParams {
175 constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ 177 constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
176 32, // ABGR8U 178 32, // ABGR8U
177 32, // ABGR8S 179 32, // ABGR8S
180 32, // ABGR8UI
178 16, // B5G6R5U 181 16, // B5G6R5U
179 32, // A2B10G10R10U 182 32, // A2B10G10R10U
180 16, // A1B5G5R5U 183 16, // A1B5G5R5U
@@ -257,6 +260,8 @@ struct SurfaceParams {
257 return PixelFormat::ABGR8U; 260 return PixelFormat::ABGR8U;
258 case Tegra::RenderTargetFormat::RGBA8_SNORM: 261 case Tegra::RenderTargetFormat::RGBA8_SNORM:
259 return PixelFormat::ABGR8S; 262 return PixelFormat::ABGR8S;
263 case Tegra::RenderTargetFormat::RGBA8_UINT:
264 return PixelFormat::ABGR8UI;
260 case Tegra::RenderTargetFormat::BGRA8_UNORM: 265 case Tegra::RenderTargetFormat::BGRA8_UNORM:
261 return PixelFormat::BGRA8; 266 return PixelFormat::BGRA8;
262 case Tegra::RenderTargetFormat::RGB10_A2_UNORM: 267 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
@@ -327,6 +332,8 @@ struct SurfaceParams {
327 return PixelFormat::ABGR8U; 332 return PixelFormat::ABGR8U;
328 case Tegra::Texture::ComponentType::SNORM: 333 case Tegra::Texture::ComponentType::SNORM:
329 return PixelFormat::ABGR8S; 334 return PixelFormat::ABGR8S;
335 case Tegra::Texture::ComponentType::UINT:
336 return PixelFormat::ABGR8UI;
330 } 337 }
331 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", 338 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
332 static_cast<u32>(component_type)); 339 static_cast<u32>(component_type));
@@ -551,6 +558,7 @@ struct SurfaceParams {
551 case Tegra::RenderTargetFormat::R16_UINT: 558 case Tegra::RenderTargetFormat::R16_UINT:
552 case Tegra::RenderTargetFormat::RG32_UINT: 559 case Tegra::RenderTargetFormat::RG32_UINT:
553 case Tegra::RenderTargetFormat::R32_UINT: 560 case Tegra::RenderTargetFormat::R32_UINT:
561 case Tegra::RenderTargetFormat::RGBA8_UINT:
554 return ComponentType::UInt; 562 return ComponentType::UInt;
555 case Tegra::RenderTargetFormat::RG16_SINT: 563 case Tegra::RenderTargetFormat::RG16_SINT:
556 case Tegra::RenderTargetFormat::R16_SINT: 564 case Tegra::RenderTargetFormat::R16_SINT:
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index bb01b3c27..ac6ccfec7 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -439,13 +439,12 @@ public:
439 } 439 }
440 declarations.AddNewLine(); 440 declarations.AddNewLine();
441 441
442 // Append the sampler2D array for the used textures. 442 const auto& samplers = GetSamplers();
443 size_t num_samplers = GetSamplers().size(); 443 for (const auto& sampler : samplers) {
444 if (num_samplers > 0) { 444 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
445 declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' + 445 ';');
446 std::to_string(num_samplers) + "];");
447 declarations.AddNewLine();
448 } 446 }
447 declarations.AddNewLine();
449 } 448 }
450 449
451 /// Returns a list of constant buffer declarations 450 /// Returns a list of constant buffer declarations
@@ -457,13 +456,14 @@ public:
457 } 456 }
458 457
459 /// Returns a list of samplers used in the shader 458 /// Returns a list of samplers used in the shader
460 std::vector<SamplerEntry> GetSamplers() const { 459 const std::vector<SamplerEntry>& GetSamplers() const {
461 return used_samplers; 460 return used_samplers;
462 } 461 }
463 462
464 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if 463 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
465 /// necessary. 464 /// necessary.
466 std::string AccessSampler(const Sampler& sampler) { 465 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
466 bool is_array) {
467 size_t offset = static_cast<size_t>(sampler.index.Value()); 467 size_t offset = static_cast<size_t>(sampler.index.Value());
468 468
469 // If this sampler has already been used, return the existing mapping. 469 // If this sampler has already been used, return the existing mapping.
@@ -472,12 +472,13 @@ public:
472 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); 472 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
473 473
474 if (itr != used_samplers.end()) { 474 if (itr != used_samplers.end()) {
475 ASSERT(itr->GetType() == type && itr->IsArray() == is_array);
475 return itr->GetName(); 476 return itr->GetName();
476 } 477 }
477 478
478 // Otherwise create a new mapping for this sampler 479 // Otherwise create a new mapping for this sampler
479 size_t next_index = used_samplers.size(); 480 size_t next_index = used_samplers.size();
480 SamplerEntry entry{stage, offset, next_index}; 481 SamplerEntry entry{stage, offset, next_index, type, is_array};
481 used_samplers.emplace_back(entry); 482 used_samplers.emplace_back(entry);
482 return entry.GetName(); 483 return entry.GetName();
483 } 484 }
@@ -542,6 +543,10 @@ private:
542 // shader. 543 // shader.
543 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex); 544 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
544 return "vec4(0, 0, uintBitsToFloat(instance_id.x), uintBitsToFloat(gl_VertexID))"; 545 return "vec4(0, 0, uintBitsToFloat(instance_id.x), uintBitsToFloat(gl_VertexID))";
546 case Attribute::Index::FrontFacing:
547 // TODO(Subv): Find out what the values are for the other elements.
548 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
549 return "vec4(0, 0, 0, uintBitsToFloat(gl_FrontFacing ? 1 : 0))";
545 default: 550 default:
546 const u32 index{static_cast<u32>(attribute) - 551 const u32 index{static_cast<u32>(attribute) -
547 static_cast<u32>(Attribute::Index::Attribute_0)}; 552 static_cast<u32>(Attribute::Index::Attribute_0)};
@@ -634,8 +639,8 @@ private:
634 } 639 }
635 640
636 /// Generates code representing a texture sampler. 641 /// Generates code representing a texture sampler.
637 std::string GetSampler(const Sampler& sampler) { 642 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) {
638 return regs.AccessSampler(sampler); 643 return regs.AccessSampler(sampler, type, is_array);
639 } 644 }
640 645
641 /** 646 /**
@@ -743,6 +748,30 @@ private:
743 return op->second; 748 return op->second;
744 } 749 }
745 750
751 /**
752 * Transforms the input string GLSL operand into one that applies the abs() function and negates
753 * the output if necessary. When both abs and neg are true, the negation will be applied after
754 * taking the absolute value.
755 * @param operand The input operand to take the abs() of, negate, or both.
756 * @param abs Whether to apply the abs() function to the input operand.
757 * @param neg Whether to negate the input operand.
758 * @returns String corresponding to the operand after being transformed by the abs() and
759 * negation operations.
760 */
761 static std::string GetOperandAbsNeg(const std::string& operand, bool abs, bool neg) {
762 std::string result = operand;
763
764 if (abs) {
765 result = "abs(" + result + ')';
766 }
767
768 if (neg) {
769 result = "-(" + result + ')';
770 }
771
772 return result;
773 }
774
746 /* 775 /*
747 * Returns whether the instruction at the specified offset is a 'sched' instruction. 776 * Returns whether the instruction at the specified offset is a 'sched' instruction.
748 * Sched instructions always appear before a sequence of 3 instructions. 777 * Sched instructions always appear before a sequence of 3 instructions.
@@ -756,28 +785,51 @@ private:
756 } 785 }
757 786
758 void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a, 787 void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
759 const std::string& op_b) { 788 const std::string& op_b,
789 Tegra::Shader::PredicateResultMode predicate_mode,
790 Tegra::Shader::Pred predicate) {
791 std::string result{};
760 switch (logic_op) { 792 switch (logic_op) {
761 case LogicOperation::And: { 793 case LogicOperation::And: {
762 regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " & " + op_b + ')', 1, 1); 794 result = '(' + op_a + " & " + op_b + ')';
763 break; 795 break;
764 } 796 }
765 case LogicOperation::Or: { 797 case LogicOperation::Or: {
766 regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " | " + op_b + ')', 1, 1); 798 result = '(' + op_a + " | " + op_b + ')';
767 break; 799 break;
768 } 800 }
769 case LogicOperation::Xor: { 801 case LogicOperation::Xor: {
770 regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " ^ " + op_b + ')', 1, 1); 802 result = '(' + op_a + " ^ " + op_b + ')';
771 break; 803 break;
772 } 804 }
773 case LogicOperation::PassB: { 805 case LogicOperation::PassB: {
774 regs.SetRegisterToInteger(dest, true, 0, op_b, 1, 1); 806 result = op_b;
775 break; 807 break;
776 } 808 }
777 default: 809 default:
778 LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op)); 810 LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op));
779 UNREACHABLE(); 811 UNREACHABLE();
780 } 812 }
813
814 if (dest != Tegra::Shader::Register::ZeroIndex) {
815 regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
816 }
817
818 using Tegra::Shader::PredicateResultMode;
819 // Write the predicate value depending on the predicate mode.
820 switch (predicate_mode) {
821 case PredicateResultMode::None:
822 // Do nothing.
823 return;
824 case PredicateResultMode::NotZero:
825 // Set the predicate to true if the result is not zero.
826 SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0");
827 break;
828 default:
829 LOG_CRITICAL(HW_GPU, "Unimplemented predicate result mode: {}",
830 static_cast<u32>(predicate_mode));
831 UNREACHABLE();
832 }
781 } 833 }
782 834
783 void WriteTexsInstruction(const Instruction& instr, const std::string& coord, 835 void WriteTexsInstruction(const Instruction& instr, const std::string& coord,
@@ -788,29 +840,56 @@ private:
788 ++shader.scope; 840 ++shader.scope;
789 shader.AddLine(coord); 841 shader.AddLine(coord);
790 842
791 // TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA 843 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle
792 // goes into gpr28+0 and gpr28+1 844 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
793 size_t texs_offset{}; 845
794 846 size_t written_components = 0;
795 size_t src_elem{}; 847 for (u32 component = 0; component < 4; ++component) {
796 for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) { 848 if (!instr.texs.IsComponentEnabled(component)) {
797 size_t dest_elem{}; 849 continue;
798 for (unsigned elem = 0; elem < 2; ++elem) {
799 if (!instr.texs.IsComponentEnabled(src_elem++)) {
800 // Skip disabled components
801 continue;
802 }
803 regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
804 dest_elem++);
805 } 850 }
806 851
807 if (!instr.texs.HasTwoDestinations()) { 852 if (written_components < 2) {
808 // Skip the second destination 853 // Write the first two swizzle components to gpr0 and gpr0+1
809 break; 854 regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false,
855 written_components % 2);
856 } else {
857 ASSERT(instr.texs.HasTwoDestinations());
858 // Write the rest of the swizzle components to gpr28 and gpr28+1
859 regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false,
860 written_components % 2);
810 } 861 }
811 862
812 texs_offset += 2; 863 ++written_components;
813 } 864 }
865
866 --shader.scope;
867 shader.AddLine('}');
868 }
869
870 /*
871 * Emits code to push the input target address to the SSY address stack, incrementing the stack
872 * top.
873 */
874 void EmitPushToSSYStack(u32 target) {
875 shader.AddLine('{');
876 ++shader.scope;
877 shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;");
878 shader.AddLine("ssy_stack_top++;");
879 --shader.scope;
880 shader.AddLine('}');
881 }
882
883 /*
884 * Emits code to pop an address from the SSY address stack, setting the jump address to the
885 * popped address and decrementing the stack top.
886 */
887 void EmitPopFromSSYStack() {
888 shader.AddLine('{');
889 ++shader.scope;
890 shader.AddLine("ssy_stack_top--;");
891 shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];");
892 shader.AddLine("break;");
814 --shader.scope; 893 --shader.scope;
815 shader.AddLine('}'); 894 shader.AddLine('}');
816 } 895 }
@@ -859,13 +938,6 @@ private:
859 switch (opcode->GetType()) { 938 switch (opcode->GetType()) {
860 case OpCode::Type::Arithmetic: { 939 case OpCode::Type::Arithmetic: {
861 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 940 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
862 if (instr.alu.abs_a) {
863 op_a = "abs(" + op_a + ')';
864 }
865
866 if (instr.alu.negate_a) {
867 op_a = "-(" + op_a + ')';
868 }
869 941
870 std::string op_b; 942 std::string op_b;
871 943
@@ -880,17 +952,10 @@ private:
880 } 952 }
881 } 953 }
882 954
883 if (instr.alu.abs_b) {
884 op_b = "abs(" + op_b + ')';
885 }
886
887 if (instr.alu.negate_b) {
888 op_b = "-(" + op_b + ')';
889 }
890
891 switch (opcode->GetId()) { 955 switch (opcode->GetId()) {
892 case OpCode::Id::MOV_C: 956 case OpCode::Id::MOV_C:
893 case OpCode::Id::MOV_R: { 957 case OpCode::Id::MOV_R: {
958 // MOV does not have neither 'abs' nor 'neg' bits.
894 regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1); 959 regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1);
895 break; 960 break;
896 } 961 }
@@ -898,6 +963,8 @@ private:
898 case OpCode::Id::FMUL_C: 963 case OpCode::Id::FMUL_C:
899 case OpCode::Id::FMUL_R: 964 case OpCode::Id::FMUL_R:
900 case OpCode::Id::FMUL_IMM: { 965 case OpCode::Id::FMUL_IMM: {
966 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit.
967 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
901 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 968 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
902 instr.alu.saturate_d); 969 instr.alu.saturate_d);
903 break; 970 break;
@@ -905,11 +972,14 @@ private:
905 case OpCode::Id::FADD_C: 972 case OpCode::Id::FADD_C:
906 case OpCode::Id::FADD_R: 973 case OpCode::Id::FADD_R:
907 case OpCode::Id::FADD_IMM: { 974 case OpCode::Id::FADD_IMM: {
975 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
976 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
908 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, 977 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
909 instr.alu.saturate_d); 978 instr.alu.saturate_d);
910 break; 979 break;
911 } 980 }
912 case OpCode::Id::MUFU: { 981 case OpCode::Id::MUFU: {
982 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
913 switch (instr.sub_op) { 983 switch (instr.sub_op) {
914 case SubOp::Cos: 984 case SubOp::Cos:
915 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, 985 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
@@ -949,6 +1019,9 @@ private:
949 case OpCode::Id::FMNMX_C: 1019 case OpCode::Id::FMNMX_C:
950 case OpCode::Id::FMNMX_R: 1020 case OpCode::Id::FMNMX_R:
951 case OpCode::Id::FMNMX_IMM: { 1021 case OpCode::Id::FMNMX_IMM: {
1022 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
1023 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1024
952 std::string condition = 1025 std::string condition =
953 GetPredicateCondition(instr.alu.fmnmx.pred, instr.alu.fmnmx.negate_pred != 0); 1026 GetPredicateCondition(instr.alu.fmnmx.pred, instr.alu.fmnmx.negate_pred != 0);
954 std::string parameters = op_a + ',' + op_b; 1027 std::string parameters = op_a + ',' + op_b;
@@ -962,7 +1035,7 @@ private:
962 case OpCode::Id::RRO_R: 1035 case OpCode::Id::RRO_R:
963 case OpCode::Id::RRO_IMM: { 1036 case OpCode::Id::RRO_IMM: {
964 // Currently RRO is only implemented as a register move. 1037 // Currently RRO is only implemented as a register move.
965 // Usage of `abs_b` and `negate_b` here should also be correct. 1038 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
966 regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1); 1039 regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1);
967 LOG_WARNING(HW_GPU, "RRO instruction is incomplete"); 1040 LOG_WARNING(HW_GPU, "RRO instruction is incomplete");
968 break; 1041 break;
@@ -1099,7 +1172,9 @@ private:
1099 if (instr.alu.lop32i.invert_b) 1172 if (instr.alu.lop32i.invert_b)
1100 op_b = "~(" + op_b + ')'; 1173 op_b = "~(" + op_b + ')';
1101 1174
1102 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b); 1175 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
1176 Tegra::Shader::PredicateResultMode::None,
1177 Tegra::Shader::Pred::UnusedIndex);
1103 break; 1178 break;
1104 } 1179 }
1105 default: { 1180 default: {
@@ -1165,16 +1240,14 @@ private:
1165 case OpCode::Id::LOP_C: 1240 case OpCode::Id::LOP_C:
1166 case OpCode::Id::LOP_R: 1241 case OpCode::Id::LOP_R:
1167 case OpCode::Id::LOP_IMM: { 1242 case OpCode::Id::LOP_IMM: {
1168 ASSERT_MSG(!instr.alu.lop.unk44, "Unimplemented");
1169 ASSERT_MSG(instr.alu.lop.pred48 == Pred::UnusedIndex, "Unimplemented");
1170
1171 if (instr.alu.lop.invert_a) 1243 if (instr.alu.lop.invert_a)
1172 op_a = "~(" + op_a + ')'; 1244 op_a = "~(" + op_a + ')';
1173 1245
1174 if (instr.alu.lop.invert_b) 1246 if (instr.alu.lop.invert_b)
1175 op_b = "~(" + op_b + ')'; 1247 op_b = "~(" + op_b + ')';
1176 1248
1177 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b); 1249 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
1250 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
1178 break; 1251 break;
1179 } 1252 }
1180 case OpCode::Id::IMNMX_C: 1253 case OpCode::Id::IMNMX_C:
@@ -1239,8 +1312,6 @@ private:
1239 break; 1312 break;
1240 } 1313 }
1241 case OpCode::Type::Conversion: { 1314 case OpCode::Type::Conversion: {
1242 ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented");
1243
1244 switch (opcode->GetId()) { 1315 switch (opcode->GetId()) {
1245 case OpCode::Id::I2I_R: { 1316 case OpCode::Id::I2I_R: {
1246 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 1317 ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
@@ -1437,10 +1508,29 @@ private:
1437 break; 1508 break;
1438 } 1509 }
1439 case OpCode::Id::TEX: { 1510 case OpCode::Id::TEX: {
1440 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1511 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
1441 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 1512 std::string coord{};
1442 const std::string sampler = GetSampler(instr.sampler); 1513
1443 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 1514 switch (instr.tex.texture_type) {
1515 case Tegra::Shader::TextureType::Texture2D: {
1516 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1517 std::string y = regs.GetRegisterAsFloat(instr.gpr20);
1518 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
1519 break;
1520 }
1521 case Tegra::Shader::TextureType::Texture3D: {
1522 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1523 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1524 std::string z = regs.GetRegisterAsFloat(instr.gpr20);
1525 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
1526 break;
1527 }
1528 default:
1529 UNIMPLEMENTED();
1530 }
1531
1532 const std::string sampler =
1533 GetSampler(instr.sampler, instr.tex.texture_type, instr.tex.array);
1444 // Add an extra scope and declare the texture coords inside to prevent 1534 // Add an extra scope and declare the texture coords inside to prevent
1445 // overwriting them in case they are used as outputs of the texs instruction. 1535 // overwriting them in case they are used as outputs of the texs instruction.
1446 shader.AddLine("{"); 1536 shader.AddLine("{");
@@ -1462,24 +1552,115 @@ private:
1462 break; 1552 break;
1463 } 1553 }
1464 case OpCode::Id::TEXS: { 1554 case OpCode::Id::TEXS: {
1465 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1555 std::string coord{};
1466 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); 1556
1467 const std::string sampler = GetSampler(instr.sampler); 1557 switch (instr.texs.GetTextureType()) {
1468 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 1558 case Tegra::Shader::TextureType::Texture2D: {
1559 if (instr.texs.IsArrayTexture()) {
1560 std::string index = regs.GetRegisterAsInteger(instr.gpr8);
1561 std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1562 std::string y = regs.GetRegisterAsFloat(instr.gpr20);
1563 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
1564 } else {
1565 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1566 std::string y = regs.GetRegisterAsFloat(instr.gpr20);
1567 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
1568 }
1569 break;
1570 }
1571 case Tegra::Shader::TextureType::TextureCube: {
1572 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1573 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1574 std::string z = regs.GetRegisterAsFloat(instr.gpr20);
1575 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
1576 break;
1577 }
1578 default:
1579 UNIMPLEMENTED();
1580 }
1581 const std::string sampler = GetSampler(instr.sampler, instr.texs.GetTextureType(),
1582 instr.texs.IsArrayTexture());
1469 1583
1470 const std::string texture = "texture(" + sampler + ", coords)"; 1584 const std::string texture = "texture(" + sampler + ", coords)";
1471 WriteTexsInstruction(instr, coord, texture); 1585 WriteTexsInstruction(instr, coord, texture);
1472 break; 1586 break;
1473 } 1587 }
1474 case OpCode::Id::TLDS: { 1588 case OpCode::Id::TLDS: {
1475 const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1589 ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D);
1476 const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20); 1590 ASSERT(instr.tlds.IsArrayTexture() == false);
1477 const std::string sampler = GetSampler(instr.sampler); 1591 std::string coord{};
1478 const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");"; 1592
1593 switch (instr.tlds.GetTextureType()) {
1594 case Tegra::Shader::TextureType::Texture2D: {
1595 if (instr.tlds.IsArrayTexture()) {
1596 UNIMPLEMENTED();
1597 } else {
1598 std::string x = regs.GetRegisterAsInteger(instr.gpr8);
1599 std::string y = regs.GetRegisterAsInteger(instr.gpr20);
1600 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
1601 }
1602 break;
1603 }
1604 default:
1605 UNIMPLEMENTED();
1606 }
1607 const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(),
1608 instr.tlds.IsArrayTexture());
1479 const std::string texture = "texelFetch(" + sampler + ", coords, 0)"; 1609 const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
1480 WriteTexsInstruction(instr, coord, texture); 1610 WriteTexsInstruction(instr, coord, texture);
1481 break; 1611 break;
1482 } 1612 }
1613 case OpCode::Id::TLD4: {
1614 ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
1615 ASSERT(instr.tld4.array == 0);
1616 std::string coord{};
1617
1618 switch (instr.tld4.texture_type) {
1619 case Tegra::Shader::TextureType::Texture2D: {
1620 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1621 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1622 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
1623 break;
1624 }
1625 default:
1626 UNIMPLEMENTED();
1627 }
1628
1629 const std::string sampler =
1630 GetSampler(instr.sampler, instr.tld4.texture_type, instr.tld4.array);
1631 // Add an extra scope and declare the texture coords inside to prevent
1632 // overwriting them in case they are used as outputs of the texs instruction.
1633 shader.AddLine("{");
1634 ++shader.scope;
1635 shader.AddLine(coord);
1636 const std::string texture = "textureGather(" + sampler + ", coords, " +
1637 std::to_string(instr.tld4.component) + ')';
1638
1639 size_t dest_elem{};
1640 for (size_t elem = 0; elem < 4; ++elem) {
1641 if (!instr.tex.IsComponentEnabled(elem)) {
1642 // Skip disabled components
1643 continue;
1644 }
1645 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
1646 ++dest_elem;
1647 }
1648 --shader.scope;
1649 shader.AddLine("}");
1650 break;
1651 }
1652 case OpCode::Id::TLD4S: {
1653 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1654 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
1655 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
1656 const std::string sampler =
1657 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
1658 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
1659 const std::string texture = "textureGather(" + sampler + ", coords, " +
1660 std::to_string(instr.tld4s.component) + ')';
1661 WriteTexsInstruction(instr, coord, texture);
1662 break;
1663 }
1483 default: { 1664 default: {
1484 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); 1665 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName());
1485 UNREACHABLE(); 1666 UNREACHABLE();
@@ -1843,13 +2024,13 @@ private:
1843 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); 2024 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported");
1844 2025
1845 u32 target = offset + instr.bra.GetBranchTarget(); 2026 u32 target = offset + instr.bra.GetBranchTarget();
1846 shader.AddLine("ssy_target = " + std::to_string(target) + "u;"); 2027 EmitPushToSSYStack(target);
1847 break; 2028 break;
1848 } 2029 }
1849 case OpCode::Id::SYNC: { 2030 case OpCode::Id::SYNC: {
1850 // The SYNC opcode jumps to the address previously set by the SSY opcode 2031 // The SYNC opcode jumps to the address previously set by the SSY opcode
1851 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 2032 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
1852 shader.AddLine("{ jmp_to = ssy_target; break; }"); 2033 EmitPopFromSSYStack();
1853 break; 2034 break;
1854 } 2035 }
1855 case OpCode::Id::DEPBAR: { 2036 case OpCode::Id::DEPBAR: {
@@ -1920,7 +2101,13 @@ private:
1920 } else { 2101 } else {
1921 labels.insert(subroutine.begin); 2102 labels.insert(subroutine.begin);
1922 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); 2103 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
1923 shader.AddLine("uint ssy_target = 0u;"); 2104
2105 // TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems
2106 // unlikely that shaders will use 20 nested SSYs.
2107 constexpr u32 SSY_STACK_SIZE = 20;
2108 shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];");
2109 shader.AddLine("uint ssy_stack_top = 0u;");
2110
1924 shader.AddLine("while (true) {"); 2111 shader.AddLine("while (true) {");
1925 ++shader.scope; 2112 ++shader.scope;
1926 2113
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 4729ce0fc..db48da645 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -11,6 +11,7 @@
11#include <vector> 11#include <vector>
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/hash.h" 13#include "common/hash.h"
14#include "video_core/engines/shader_bytecode.h"
14 15
15namespace GLShader { 16namespace GLShader {
16 17
@@ -72,8 +73,9 @@ class SamplerEntry {
72 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 73 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
73 74
74public: 75public:
75 SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index) 76 SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index,
76 : offset(offset), stage(stage), sampler_index(index) {} 77 Tegra::Shader::TextureType type, bool is_array)
78 : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {}
77 79
78 size_t GetOffset() const { 80 size_t GetOffset() const {
79 return offset; 81 return offset;
@@ -88,8 +90,41 @@ public:
88 } 90 }
89 91
90 std::string GetName() const { 92 std::string GetName() const {
91 return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' + 93 return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '_' +
92 std::to_string(sampler_index) + ']'; 94 std::to_string(sampler_index);
95 }
96
97 std::string GetTypeString() const {
98 using Tegra::Shader::TextureType;
99 std::string glsl_type;
100
101 switch (type) {
102 case TextureType::Texture1D:
103 glsl_type = "sampler1D";
104 break;
105 case TextureType::Texture2D:
106 glsl_type = "sampler2D";
107 break;
108 case TextureType::Texture3D:
109 glsl_type = "sampler3D";
110 break;
111 case TextureType::TextureCube:
112 glsl_type = "samplerCube";
113 break;
114 default:
115 UNIMPLEMENTED();
116 }
117 if (is_array)
118 glsl_type += "Array";
119 return glsl_type;
120 }
121
122 Tegra::Shader::TextureType GetType() const {
123 return type;
124 }
125
126 bool IsArray() const {
127 return is_array;
93 } 128 }
94 129
95 static std::string GetArrayName(Maxwell::ShaderStage stage) { 130 static std::string GetArrayName(Maxwell::ShaderStage stage) {
@@ -100,11 +135,14 @@ private:
100 static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { 135 static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
101 "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", 136 "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
102 }; 137 };
138
103 /// Offset in TSC memory from which to read the sampler object, as specified by the sampling 139 /// Offset in TSC memory from which to read the sampler object, as specified by the sampling
104 /// instruction. 140 /// instruction.
105 size_t offset; 141 size_t offset;
106 Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. 142 Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
107 size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. 143 size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
144 Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
145 bool is_array; ///< Whether the texture is being sampled as an array texture or not.
108}; 146};
109 147
110struct ShaderEntries { 148struct ShaderEntries {
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 8f719fdd8..5d91a0c2f 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -147,6 +147,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
147 // GL_CLAMP_TO_BORDER to get the border color of the texture, and then sample the edge to 147 // GL_CLAMP_TO_BORDER to get the border color of the texture, and then sample the edge to
148 // manually mix them. However the shader part of this is not yet implemented. 148 // manually mix them. However the shader part of this is not yet implemented.
149 return GL_CLAMP_TO_BORDER; 149 return GL_CLAMP_TO_BORDER;
150 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
151 return GL_MIRROR_CLAMP_TO_EDGE;
150 } 152 }
151 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 153 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
152 UNREACHABLE(); 154 UNREACHABLE();
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index f867118d9..bc4b93033 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -438,7 +438,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
438 438
439 std::vector<u8> icon; 439 std::vector<u8> icon;
440 std::string name; 440 std::string name;
441 u64 program_id; 441 u64 program_id = 0;
442 loader->ReadProgramId(program_id); 442 loader->ReadProgramId(program_id);
443 443
444 const auto& control = 444 const auto& control =
@@ -509,7 +509,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
509 std::vector<u8> icon; 509 std::vector<u8> icon;
510 const auto res1 = loader->ReadIcon(icon); 510 const auto res1 = loader->ReadIcon(icon);
511 511
512 u64 program_id; 512 u64 program_id = 0;
513 const auto res2 = loader->ReadProgramId(program_id); 513 const auto res2 = loader->ReadProgramId(program_id);
514 514
515 std::string name = " "; 515 std::string name = " ";
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2df65023a..11d2331df 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -377,6 +377,8 @@ bool GMainWindow::SupportsRequiredGLExtensions() {
377 unsupported_ext.append("ARB_vertex_attrib_binding"); 377 unsupported_ext.append("ARB_vertex_attrib_binding");
378 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) 378 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
379 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); 379 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
380 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
381 unsupported_ext.append("ARB_texture_mirror_clamp_to_edge");
380 382
381 // Extensions required to support some texture formats. 383 // Extensions required to support some texture formats.
382 if (!GLAD_GL_EXT_texture_compression_s3tc) 384 if (!GLAD_GL_EXT_texture_compression_s3tc)
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e2945b6cf..351dd9225 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -89,6 +89,8 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
89 unsupported_ext.push_back("ARB_vertex_attrib_binding"); 89 unsupported_ext.push_back("ARB_vertex_attrib_binding");
90 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) 90 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
91 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); 91 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
92 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
93 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
92 94
93 // Extensions required to support some texture formats. 95 // Extensions required to support some texture formats.
94 if (!GLAD_GL_EXT_texture_compression_s3tc) 96 if (!GLAD_GL_EXT_texture_compression_s3tc)