diff options
Diffstat (limited to 'src')
20 files changed, 265 insertions, 47 deletions
diff --git a/src/common/uint128.h b/src/common/uint128.h index 83560a9ce..4780b2f9d 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h | |||
| @@ -98,4 +98,24 @@ namespace Common { | |||
| 98 | #endif | 98 | #endif |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | // This function divides a u128 by a u32 value and produces two u64 values: | ||
| 102 | // the result of division and the remainder | ||
| 103 | [[nodiscard]] static inline std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) { | ||
| 104 | u64 remainder = dividend[0] % divisor; | ||
| 105 | u64 accum = dividend[0] / divisor; | ||
| 106 | if (dividend[1] == 0) | ||
| 107 | return {accum, remainder}; | ||
| 108 | // We ignore dividend[1] / divisor as that overflows | ||
| 109 | const u64 first_segment = (dividend[1] % divisor) << 32; | ||
| 110 | accum += (first_segment / divisor) << 32; | ||
| 111 | const u64 second_segment = (first_segment % divisor) << 32; | ||
| 112 | accum += (second_segment / divisor); | ||
| 113 | remainder += second_segment % divisor; | ||
| 114 | if (remainder >= divisor) { | ||
| 115 | accum++; | ||
| 116 | remainder -= divisor; | ||
| 117 | } | ||
| 118 | return {accum, remainder}; | ||
| 119 | } | ||
| 120 | |||
| 101 | } // namespace Common | 121 | } // namespace Common |
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 1545993bd..49830b8ab 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -20,9 +20,7 @@ using base_time_point = std::chrono::time_point<base_timer>; | |||
| 20 | class StandardWallClock final : public WallClock { | 20 | class StandardWallClock final : public WallClock { |
| 21 | public: | 21 | public: |
| 22 | explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) | 22 | explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) |
| 23 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false), | 23 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false) { |
| 24 | emulated_clock_factor{GetFixedPoint64Factor(emulated_clock_frequency, 1000000000)}, | ||
| 25 | emulated_cpu_factor{GetFixedPoint64Factor(emulated_cpu_frequency, 1000000000)} { | ||
| 26 | start_time = base_timer::now(); | 24 | start_time = base_timer::now(); |
| 27 | } | 25 | } |
| 28 | 26 | ||
| @@ -45,11 +43,16 @@ public: | |||
| 45 | } | 43 | } |
| 46 | 44 | ||
| 47 | u64 GetClockCycles() override { | 45 | u64 GetClockCycles() override { |
| 48 | return MultiplyHigh(GetTimeNS().count(), emulated_clock_factor); | 46 | std::chrono::nanoseconds time_now = GetTimeNS(); |
| 47 | const u128 temporary = | ||
| 48 | Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); | ||
| 49 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | u64 GetCPUCycles() override { | 52 | u64 GetCPUCycles() override { |
| 52 | return MultiplyHigh(GetTimeNS().count(), emulated_cpu_factor); | 53 | std::chrono::nanoseconds time_now = GetTimeNS(); |
| 54 | const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); | ||
| 55 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 53 | } | 56 | } |
| 54 | 57 | ||
| 55 | void Pause([[maybe_unused]] bool is_paused) override { | 58 | void Pause([[maybe_unused]] bool is_paused) override { |
| @@ -58,8 +61,6 @@ public: | |||
| 58 | 61 | ||
| 59 | private: | 62 | private: |
| 60 | base_time_point start_time; | 63 | base_time_point start_time; |
| 61 | const u64 emulated_clock_factor; | ||
| 62 | const u64 emulated_cpu_factor; | ||
| 63 | }; | 64 | }; |
| 64 | 65 | ||
| 65 | #ifdef ARCHITECTURE_x86_64 | 66 | #ifdef ARCHITECTURE_x86_64 |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 30f5e1128..de6305e2a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -308,6 +308,9 @@ struct System::Impl { | |||
| 308 | // Close all CPU/threading state | 308 | // Close all CPU/threading state |
| 309 | cpu_manager.Shutdown(); | 309 | cpu_manager.Shutdown(); |
| 310 | 310 | ||
| 311 | // Release the Time Manager's resources | ||
| 312 | time_manager.Shutdown(); | ||
| 313 | |||
| 311 | // Shutdown kernel and core timing | 314 | // Shutdown kernel and core timing |
| 312 | core_timing.Shutdown(); | 315 | core_timing.Shutdown(); |
| 313 | kernel.Shutdown(); | 316 | kernel.Shutdown(); |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 453695545..331cf3a60 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -101,8 +101,6 @@ struct KernelCore::Impl { | |||
| 101 | 101 | ||
| 102 | current_process = nullptr; | 102 | current_process = nullptr; |
| 103 | 103 | ||
| 104 | system_resource_limit = nullptr; | ||
| 105 | |||
| 106 | global_handle_table.Clear(); | 104 | global_handle_table.Clear(); |
| 107 | 105 | ||
| 108 | preemption_event = nullptr; | 106 | preemption_event = nullptr; |
| @@ -111,6 +109,13 @@ struct KernelCore::Impl { | |||
| 111 | 109 | ||
| 112 | exclusive_monitor.reset(); | 110 | exclusive_monitor.reset(); |
| 113 | 111 | ||
| 112 | hid_shared_mem = nullptr; | ||
| 113 | font_shared_mem = nullptr; | ||
| 114 | irs_shared_mem = nullptr; | ||
| 115 | time_shared_mem = nullptr; | ||
| 116 | |||
| 117 | system_resource_limit = nullptr; | ||
| 118 | |||
| 114 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others | 119 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others |
| 115 | next_host_thread_id = Core::Hardware::NUM_CPU_CORES; | 120 | next_host_thread_id = Core::Hardware::NUM_CPU_CORES; |
| 116 | } | 121 | } |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 3ec0e1eca..615e20a54 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -508,7 +508,7 @@ public: | |||
| 508 | {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, | 508 | {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, |
| 509 | {2, nullptr, "EnsureIdTokenCacheAsync"}, | 509 | {2, nullptr, "EnsureIdTokenCacheAsync"}, |
| 510 | {3, nullptr, "LoadIdTokenCache"}, | 510 | {3, nullptr, "LoadIdTokenCache"}, |
| 511 | {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, | 511 | {130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"}, |
| 512 | {150, nullptr, "CreateAuthorizationRequest"}, | 512 | {150, nullptr, "CreateAuthorizationRequest"}, |
| 513 | {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"}, | 513 | {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"}, |
| 514 | {170, nullptr, "LoadNetworkServiceLicenseKindAsync"}, | 514 | {170, nullptr, "LoadNetworkServiceLicenseKindAsync"}, |
| @@ -534,6 +534,22 @@ private: | |||
| 534 | rb.PushRaw<u64>(user_id.GetNintendoID()); | 534 | rb.PushRaw<u64>(user_id.GetNintendoID()); |
| 535 | } | 535 | } |
| 536 | 536 | ||
| 537 | void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) { | ||
| 538 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 539 | |||
| 540 | std::vector<u8> nas_user_base_for_application(0x68); | ||
| 541 | ctx.WriteBuffer(nas_user_base_for_application, 0); | ||
| 542 | |||
| 543 | if (ctx.CanWriteBuffer(1)) { | ||
| 544 | std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1)); | ||
| 545 | ctx.WriteBuffer(unknown_out_buffer, 1); | ||
| 546 | } | ||
| 547 | |||
| 548 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 549 | rb.Push(RESULT_SUCCESS); | ||
| 550 | rb.PushRaw<u64>(user_id.GetNintendoID()); | ||
| 551 | } | ||
| 552 | |||
| 537 | void StoreOpenContext(Kernel::HLERequestContext& ctx) { | 553 | void StoreOpenContext(Kernel::HLERequestContext& ctx) { |
| 538 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 554 | LOG_WARNING(Service_ACC, "(STUBBED) called"); |
| 539 | IPC::ResponseBuilder rb{ctx, 2}; | 555 | IPC::ResponseBuilder rb{ctx, 2}; |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index ffc3dfdc3..ba27bbb05 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -273,8 +273,8 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} { | |||
| 273 | {204, &Hid::PermitVibration, "PermitVibration"}, | 273 | {204, &Hid::PermitVibration, "PermitVibration"}, |
| 274 | {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"}, | 274 | {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"}, |
| 275 | {206, &Hid::SendVibrationValues, "SendVibrationValues"}, | 275 | {206, &Hid::SendVibrationValues, "SendVibrationValues"}, |
| 276 | {207, nullptr, "SendVibrationGcErmCommand"}, | 276 | {207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"}, |
| 277 | {208, nullptr, "GetActualVibrationGcErmCommand"}, | 277 | {208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"}, |
| 278 | {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, | 278 | {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, |
| 279 | {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, | 279 | {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, |
| 280 | {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, | 280 | {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, |
| @@ -1093,7 +1093,22 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { | |||
| 1093 | 1093 | ||
| 1094 | VibrationDeviceInfo vibration_device_info; | 1094 | VibrationDeviceInfo vibration_device_info; |
| 1095 | 1095 | ||
| 1096 | vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; | 1096 | switch (vibration_device_handle.npad_type) { |
| 1097 | case Controller_NPad::NpadType::ProController: | ||
| 1098 | case Controller_NPad::NpadType::Handheld: | ||
| 1099 | case Controller_NPad::NpadType::JoyconDual: | ||
| 1100 | case Controller_NPad::NpadType::JoyconLeft: | ||
| 1101 | case Controller_NPad::NpadType::JoyconRight: | ||
| 1102 | default: | ||
| 1103 | vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; | ||
| 1104 | break; | ||
| 1105 | case Controller_NPad::NpadType::GameCube: | ||
| 1106 | vibration_device_info.type = VibrationDeviceType::GcErm; | ||
| 1107 | break; | ||
| 1108 | case Controller_NPad::NpadType::Pokeball: | ||
| 1109 | vibration_device_info.type = VibrationDeviceType::Unknown; | ||
| 1110 | break; | ||
| 1111 | } | ||
| 1097 | 1112 | ||
| 1098 | switch (vibration_device_handle.device_index) { | 1113 | switch (vibration_device_handle.device_index) { |
| 1099 | case Controller_NPad::DeviceIndex::Left: | 1114 | case Controller_NPad::DeviceIndex::Left: |
| @@ -1215,6 +1230,108 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | |||
| 1215 | rb.Push(RESULT_SUCCESS); | 1230 | rb.Push(RESULT_SUCCESS); |
| 1216 | } | 1231 | } |
| 1217 | 1232 | ||
| 1233 | void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | ||
| 1234 | IPC::RequestParser rp{ctx}; | ||
| 1235 | struct Parameters { | ||
| 1236 | Controller_NPad::DeviceHandle vibration_device_handle; | ||
| 1237 | u64 applet_resource_user_id; | ||
| 1238 | VibrationGcErmCommand gc_erm_command; | ||
| 1239 | }; | ||
| 1240 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1241 | |||
| 1242 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1243 | |||
| 1244 | /** | ||
| 1245 | * Note: This uses yuzu-specific behavior such that the StopHard command produces | ||
| 1246 | * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, | ||
| 1247 | * in order to differentiate between Stop and StopHard commands. | ||
| 1248 | * This is done to reuse the controller vibration functions made for regular controllers. | ||
| 1249 | */ | ||
| 1250 | const auto vibration_value = [parameters] { | ||
| 1251 | switch (parameters.gc_erm_command) { | ||
| 1252 | case VibrationGcErmCommand::Stop: | ||
| 1253 | return Controller_NPad::VibrationValue{ | ||
| 1254 | .amp_low = 0.0f, | ||
| 1255 | .freq_low = 160.0f, | ||
| 1256 | .amp_high = 0.0f, | ||
| 1257 | .freq_high = 320.0f, | ||
| 1258 | }; | ||
| 1259 | case VibrationGcErmCommand::Start: | ||
| 1260 | return Controller_NPad::VibrationValue{ | ||
| 1261 | .amp_low = 1.0f, | ||
| 1262 | .freq_low = 160.0f, | ||
| 1263 | .amp_high = 1.0f, | ||
| 1264 | .freq_high = 320.0f, | ||
| 1265 | }; | ||
| 1266 | case VibrationGcErmCommand::StopHard: | ||
| 1267 | return Controller_NPad::VibrationValue{ | ||
| 1268 | .amp_low = 0.0f, | ||
| 1269 | .freq_low = 0.0f, | ||
| 1270 | .amp_high = 0.0f, | ||
| 1271 | .freq_high = 0.0f, | ||
| 1272 | }; | ||
| 1273 | default: | ||
| 1274 | return Controller_NPad::DEFAULT_VIBRATION_VALUE; | ||
| 1275 | } | ||
| 1276 | }(); | ||
| 1277 | |||
| 1278 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1279 | .VibrateController(parameters.vibration_device_handle, vibration_value); | ||
| 1280 | |||
| 1281 | LOG_DEBUG(Service_HID, | ||
| 1282 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " | ||
| 1283 | "gc_erm_command={}", | ||
| 1284 | parameters.vibration_device_handle.npad_type, | ||
| 1285 | parameters.vibration_device_handle.npad_id, | ||
| 1286 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, | ||
| 1287 | parameters.gc_erm_command); | ||
| 1288 | |||
| 1289 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1290 | rb.Push(RESULT_SUCCESS); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | ||
| 1294 | IPC::RequestParser rp{ctx}; | ||
| 1295 | struct Parameters { | ||
| 1296 | Controller_NPad::DeviceHandle vibration_device_handle; | ||
| 1297 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1298 | u64 applet_resource_user_id; | ||
| 1299 | }; | ||
| 1300 | |||
| 1301 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1302 | |||
| 1303 | const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1304 | .GetLastVibration(parameters.vibration_device_handle); | ||
| 1305 | |||
| 1306 | const auto gc_erm_command = [last_vibration] { | ||
| 1307 | if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) { | ||
| 1308 | return VibrationGcErmCommand::Start; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | /** | ||
| 1312 | * Note: This uses yuzu-specific behavior such that the StopHard command produces | ||
| 1313 | * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function | ||
| 1314 | * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. | ||
| 1315 | * This is done to reuse the controller vibration functions made for regular controllers. | ||
| 1316 | */ | ||
| 1317 | if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) { | ||
| 1318 | return VibrationGcErmCommand::StopHard; | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | return VibrationGcErmCommand::Stop; | ||
| 1322 | }(); | ||
| 1323 | |||
| 1324 | LOG_DEBUG(Service_HID, | ||
| 1325 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1326 | parameters.vibration_device_handle.npad_type, | ||
| 1327 | parameters.vibration_device_handle.npad_id, | ||
| 1328 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1329 | |||
| 1330 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1331 | rb.Push(RESULT_SUCCESS); | ||
| 1332 | rb.PushEnum(gc_erm_command); | ||
| 1333 | } | ||
| 1334 | |||
| 1218 | void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { | 1335 | void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { |
| 1219 | IPC::RequestParser rp{ctx}; | 1336 | IPC::RequestParser rp{ctx}; |
| 1220 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1337 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 06ddcf3e4..36ed228c8 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -136,6 +136,8 @@ private: | |||
| 136 | void PermitVibration(Kernel::HLERequestContext& ctx); | 136 | void PermitVibration(Kernel::HLERequestContext& ctx); |
| 137 | void IsVibrationPermitted(Kernel::HLERequestContext& ctx); | 137 | void IsVibrationPermitted(Kernel::HLERequestContext& ctx); |
| 138 | void SendVibrationValues(Kernel::HLERequestContext& ctx); | 138 | void SendVibrationValues(Kernel::HLERequestContext& ctx); |
| 139 | void SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx); | ||
| 140 | void GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx); | ||
| 139 | void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); | 141 | void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); |
| 140 | void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); | 142 | void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); |
| 141 | void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx); | 143 | void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx); |
| @@ -154,7 +156,9 @@ private: | |||
| 154 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); | 156 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); |
| 155 | 157 | ||
| 156 | enum class VibrationDeviceType : u32 { | 158 | enum class VibrationDeviceType : u32 { |
| 159 | Unknown = 0, | ||
| 157 | LinearResonantActuator = 1, | 160 | LinearResonantActuator = 1, |
| 161 | GcErm = 2, | ||
| 158 | }; | 162 | }; |
| 159 | 163 | ||
| 160 | enum class VibrationDevicePosition : u32 { | 164 | enum class VibrationDevicePosition : u32 { |
| @@ -163,6 +167,12 @@ private: | |||
| 163 | Right = 2, | 167 | Right = 2, |
| 164 | }; | 168 | }; |
| 165 | 169 | ||
| 170 | enum class VibrationGcErmCommand : u64 { | ||
| 171 | Stop = 0, | ||
| 172 | Start = 1, | ||
| 173 | StopHard = 2, | ||
| 174 | }; | ||
| 175 | |||
| 166 | struct VibrationDeviceInfo { | 176 | struct VibrationDeviceInfo { |
| 167 | VibrationDeviceType type{}; | 177 | VibrationDeviceType type{}; |
| 168 | VibrationDevicePosition position{}; | 178 | VibrationDevicePosition position{}; |
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp index 858623e2b..1f7309f6b 100644 --- a/src/core/hle/service/time/time_manager.cpp +++ b/src/core/hle/service/time/time_manager.cpp | |||
| @@ -279,6 +279,10 @@ const SharedMemory& TimeManager::GetSharedMemory() const { | |||
| 279 | return impl->shared_memory; | 279 | return impl->shared_memory; |
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | void TimeManager::Shutdown() { | ||
| 283 | impl.reset(); | ||
| 284 | } | ||
| 285 | |||
| 282 | void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { | 286 | void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { |
| 283 | impl->UpdateLocalSystemClockTime(system, posix_time); | 287 | impl->UpdateLocalSystemClockTime(system, posix_time); |
| 284 | } | 288 | } |
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h index 993c7c288..4db8cc0e1 100644 --- a/src/core/hle/service/time/time_manager.h +++ b/src/core/hle/service/time/time_manager.h | |||
| @@ -61,6 +61,8 @@ public: | |||
| 61 | 61 | ||
| 62 | const SharedMemory& GetSharedMemory() const; | 62 | const SharedMemory& GetSharedMemory() const; |
| 63 | 63 | ||
| 64 | void Shutdown(); | ||
| 65 | |||
| 64 | void SetupTimeZoneManager(std::string location_name, | 66 | void SetupTimeZoneManager(std::string location_name, |
| 65 | Clock::SteadyClockTimePoint time_zone_updated_time_point, | 67 | Clock::SteadyClockTimePoint time_zone_updated_time_point, |
| 66 | std::size_t total_location_name_count, u128 time_zone_rule_version, | 68 | std::size_t total_location_name_count, u128 time_zone_rule_version, |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 48d5c4a5e..1ae5f1d62 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -239,6 +239,7 @@ Device::Device() { | |||
| 239 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; | 239 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; |
| 240 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; | 240 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; |
| 241 | has_debugging_tool_attached = IsDebugToolAttached(extensions); | 241 | has_debugging_tool_attached = IsDebugToolAttached(extensions); |
| 242 | has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float"); | ||
| 242 | 243 | ||
| 243 | // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive | 244 | // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive |
| 244 | // uniform buffers as "push constants" | 245 | // uniform buffers as "push constants" |
| @@ -275,6 +276,7 @@ Device::Device(std::nullptr_t) { | |||
| 275 | has_image_load_formatted = true; | 276 | has_image_load_formatted = true; |
| 276 | has_texture_shadow_lod = true; | 277 | has_texture_shadow_lod = true; |
| 277 | has_variable_aoffi = true; | 278 | has_variable_aoffi = true; |
| 279 | has_depth_buffer_float = true; | ||
| 278 | } | 280 | } |
| 279 | 281 | ||
| 280 | bool Device::TestVariableAoffi() { | 282 | bool Device::TestVariableAoffi() { |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index ee053776d..f24bd0c7b 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -122,6 +122,10 @@ public: | |||
| 122 | return use_driver_cache; | 122 | return use_driver_cache; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | bool HasDepthBufferFloat() const { | ||
| 126 | return has_depth_buffer_float; | ||
| 127 | } | ||
| 128 | |||
| 125 | private: | 129 | private: |
| 126 | static bool TestVariableAoffi(); | 130 | static bool TestVariableAoffi(); |
| 127 | static bool TestPreciseBug(); | 131 | static bool TestPreciseBug(); |
| @@ -150,6 +154,7 @@ private: | |||
| 150 | bool use_assembly_shaders{}; | 154 | bool use_assembly_shaders{}; |
| 151 | bool use_asynchronous_shaders{}; | 155 | bool use_asynchronous_shaders{}; |
| 152 | bool use_driver_cache{}; | 156 | bool use_driver_cache{}; |
| 157 | bool has_depth_buffer_float{}; | ||
| 153 | }; | 158 | }; |
| 154 | 159 | ||
| 155 | } // namespace OpenGL | 160 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 418644108..4610fd160 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -889,7 +889,11 @@ void RasterizerOpenGL::SyncViewport() { | |||
| 889 | const GLdouble reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne; | 889 | const GLdouble reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne; |
| 890 | const GLdouble near_depth = src.translate_z - src.scale_z * reduce_z; | 890 | const GLdouble near_depth = src.translate_z - src.scale_z * reduce_z; |
| 891 | const GLdouble far_depth = src.translate_z + src.scale_z; | 891 | const GLdouble far_depth = src.translate_z + src.scale_z; |
| 892 | glDepthRangeIndexed(static_cast<GLuint>(i), near_depth, far_depth); | 892 | if (device.HasDepthBufferFloat()) { |
| 893 | glDepthRangeIndexeddNV(static_cast<GLuint>(i), near_depth, far_depth); | ||
| 894 | } else { | ||
| 895 | glDepthRangeIndexed(static_cast<GLuint>(i), near_depth, far_depth); | ||
| 896 | } | ||
| 893 | 897 | ||
| 894 | if (!GLAD_GL_NV_viewport_swizzle) { | 898 | if (!GLAD_GL_NV_viewport_swizzle) { |
| 895 | continue; | 899 | continue; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 529570ff0..5cf7cd151 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -335,6 +335,10 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop | |||
| 335 | const VideoCore::DiskResourceLoadCallback& callback) { | 335 | const VideoCore::DiskResourceLoadCallback& callback) { |
| 336 | disk_cache.BindTitleID(title_id); | 336 | disk_cache.BindTitleID(title_id); |
| 337 | const std::optional transferable = disk_cache.LoadTransferable(); | 337 | const std::optional transferable = disk_cache.LoadTransferable(); |
| 338 | |||
| 339 | LOG_INFO(Render_OpenGL, "Total Shader Count: {}", | ||
| 340 | transferable.has_value() ? transferable->size() : 0); | ||
| 341 | |||
| 338 | if (!transferable) { | 342 | if (!transferable) { |
| 339 | return; | 343 | return; |
| 340 | } | 344 | } |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 848eedd66..668633e7b 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -201,10 +201,6 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, | |||
| 201 | }); | 201 | }); |
| 202 | } | 202 | } |
| 203 | 203 | ||
| 204 | void BufferCacheRuntime::BindBuffer(VkBuffer buffer, u32 offset, u32 size) { | ||
| 205 | update_descriptor_queue.AddBuffer(buffer, offset, size); | ||
| 206 | } | ||
| 207 | |||
| 208 | void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) { | 204 | void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) { |
| 209 | if (num_indices <= current_num_indices) { | 205 | if (num_indices <= current_num_indices) { |
| 210 | return; | 206 | return; |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 041e6515c..982e92191 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 9 | #include "video_core/renderer_vulkan/vk_compute_pass.h" | 9 | #include "video_core/renderer_vulkan/vk_compute_pass.h" |
| 10 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | 10 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" |
| 11 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | ||
| 11 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | 12 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 13 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 13 | 14 | ||
| @@ -16,7 +17,6 @@ namespace Vulkan { | |||
| 16 | class Device; | 17 | class Device; |
| 17 | class VKDescriptorPool; | 18 | class VKDescriptorPool; |
| 18 | class VKScheduler; | 19 | class VKScheduler; |
| 19 | class VKUpdateDescriptorQueue; | ||
| 20 | 20 | ||
| 21 | class BufferCacheRuntime; | 21 | class BufferCacheRuntime; |
| 22 | 22 | ||
| @@ -86,7 +86,9 @@ public: | |||
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | private: | 88 | private: |
| 89 | void BindBuffer(VkBuffer buffer, u32 offset, u32 size); | 89 | void BindBuffer(VkBuffer buffer, u32 offset, u32 size) { |
| 90 | update_descriptor_queue.AddBuffer(buffer, offset, size); | ||
| 91 | } | ||
| 90 | 92 | ||
| 91 | void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle); | 93 | void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle); |
| 92 | 94 | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 40e2e0d38..c6846d886 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -1845,13 +1845,21 @@ private: | |||
| 1845 | 1845 | ||
| 1846 | Expression TextureGather(Operation operation) { | 1846 | Expression TextureGather(Operation operation) { |
| 1847 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | 1847 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); |
| 1848 | UNIMPLEMENTED_IF(!meta.aoffi.empty()); | ||
| 1849 | 1848 | ||
| 1850 | const Id coords = GetCoordinates(operation, Type::Float); | 1849 | const Id coords = GetCoordinates(operation, Type::Float); |
| 1850 | |||
| 1851 | spv::ImageOperandsMask mask = spv::ImageOperandsMask::MaskNone; | ||
| 1852 | std::vector<Id> operands; | ||
| 1851 | Id texture{}; | 1853 | Id texture{}; |
| 1854 | |||
| 1855 | if (!meta.aoffi.empty()) { | ||
| 1856 | mask = mask | spv::ImageOperandsMask::Offset; | ||
| 1857 | operands.push_back(GetOffsetCoordinates(operation)); | ||
| 1858 | } | ||
| 1859 | |||
| 1852 | if (meta.sampler.is_shadow) { | 1860 | if (meta.sampler.is_shadow) { |
| 1853 | texture = OpImageDrefGather(t_float4, GetTextureSampler(operation), coords, | 1861 | texture = OpImageDrefGather(t_float4, GetTextureSampler(operation), coords, |
| 1854 | AsFloat(Visit(meta.depth_compare))); | 1862 | AsFloat(Visit(meta.depth_compare)), mask, operands); |
| 1855 | } else { | 1863 | } else { |
| 1856 | u32 component_value = 0; | 1864 | u32 component_value = 0; |
| 1857 | if (meta.component) { | 1865 | if (meta.component) { |
| @@ -1860,7 +1868,7 @@ private: | |||
| 1860 | component_value = component->GetValue(); | 1868 | component_value = component->GetValue(); |
| 1861 | } | 1869 | } |
| 1862 | texture = OpImageGather(t_float4, GetTextureSampler(operation), coords, | 1870 | texture = OpImageGather(t_float4, GetTextureSampler(operation), coords, |
| 1863 | Constant(t_uint, component_value)); | 1871 | Constant(t_uint, component_value), mask, operands); |
| 1864 | } | 1872 | } |
| 1865 | return GetTextureElement(operation, texture, Type::Float); | 1873 | return GetTextureElement(operation, texture, Type::Float); |
| 1866 | } | 1874 | } |
| @@ -1928,13 +1936,22 @@ private: | |||
| 1928 | 1936 | ||
| 1929 | const Id image = GetTextureImage(operation); | 1937 | const Id image = GetTextureImage(operation); |
| 1930 | const Id coords = GetCoordinates(operation, Type::Int); | 1938 | const Id coords = GetCoordinates(operation, Type::Int); |
| 1939 | |||
| 1940 | spv::ImageOperandsMask mask = spv::ImageOperandsMask::MaskNone; | ||
| 1941 | std::vector<Id> operands; | ||
| 1931 | Id fetch; | 1942 | Id fetch; |
| 1943 | |||
| 1932 | if (meta.lod && !meta.sampler.is_buffer) { | 1944 | if (meta.lod && !meta.sampler.is_buffer) { |
| 1933 | fetch = OpImageFetch(t_float4, image, coords, spv::ImageOperandsMask::Lod, | 1945 | mask = mask | spv::ImageOperandsMask::Lod; |
| 1934 | AsInt(Visit(meta.lod))); | 1946 | operands.push_back(AsInt(Visit(meta.lod))); |
| 1935 | } else { | 1947 | } |
| 1936 | fetch = OpImageFetch(t_float4, image, coords); | 1948 | |
| 1949 | if (!meta.aoffi.empty()) { | ||
| 1950 | mask = mask | spv::ImageOperandsMask::Offset; | ||
| 1951 | operands.push_back(GetOffsetCoordinates(operation)); | ||
| 1937 | } | 1952 | } |
| 1953 | |||
| 1954 | fetch = OpImageFetch(t_float4, image, coords, mask, operands); | ||
| 1938 | return GetTextureElement(operation, fetch, Type::Float); | 1955 | return GetTextureElement(operation, fetch, Type::Float); |
| 1939 | } | 1956 | } |
| 1940 | 1957 | ||
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp index f99273c6a..dc45fdcb1 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp | |||
| @@ -20,20 +20,20 @@ VKUpdateDescriptorQueue::VKUpdateDescriptorQueue(const Device& device_, VKSchedu | |||
| 20 | VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; | 20 | VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; |
| 21 | 21 | ||
| 22 | void VKUpdateDescriptorQueue::TickFrame() { | 22 | void VKUpdateDescriptorQueue::TickFrame() { |
| 23 | payload.clear(); | 23 | payload_cursor = payload.data(); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | void VKUpdateDescriptorQueue::Acquire() { | 26 | void VKUpdateDescriptorQueue::Acquire() { |
| 27 | // Minimum number of entries required. | 27 | // Minimum number of entries required. |
| 28 | // This is the maximum number of entries a single draw call migth use. | 28 | // This is the maximum number of entries a single draw call migth use. |
| 29 | static constexpr std::size_t MIN_ENTRIES = 0x400; | 29 | static constexpr size_t MIN_ENTRIES = 0x400; |
| 30 | 30 | ||
| 31 | if (payload.size() + MIN_ENTRIES >= payload.max_size()) { | 31 | if (std::distance(payload.data(), payload_cursor) + MIN_ENTRIES >= payload.max_size()) { |
| 32 | LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); | 32 | LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); |
| 33 | scheduler.WaitWorker(); | 33 | scheduler.WaitWorker(); |
| 34 | payload.clear(); | 34 | payload_cursor = payload.data(); |
| 35 | } | 35 | } |
| 36 | upload_start = &*payload.end(); | 36 | upload_start = payload_cursor; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, | 39 | void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, |
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index e214f7195..d35e77c44 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h | |||
| @@ -4,8 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <variant> | 7 | #include <array> |
| 8 | #include <boost/container/static_vector.hpp> | ||
| 9 | 8 | ||
| 10 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 11 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| @@ -16,13 +15,15 @@ class Device; | |||
| 16 | class VKScheduler; | 15 | class VKScheduler; |
| 17 | 16 | ||
| 18 | struct DescriptorUpdateEntry { | 17 | struct DescriptorUpdateEntry { |
| 19 | DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} | 18 | struct Empty {}; |
| 20 | 19 | ||
| 20 | DescriptorUpdateEntry() = default; | ||
| 21 | DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} | ||
| 21 | DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} | 22 | DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} |
| 22 | |||
| 23 | DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} | 23 | DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} |
| 24 | 24 | ||
| 25 | union { | 25 | union { |
| 26 | Empty empty{}; | ||
| 26 | VkDescriptorImageInfo image; | 27 | VkDescriptorImageInfo image; |
| 27 | VkDescriptorBufferInfo buffer; | 28 | VkDescriptorBufferInfo buffer; |
| 28 | VkBufferView texel_buffer; | 29 | VkBufferView texel_buffer; |
| @@ -41,39 +42,40 @@ public: | |||
| 41 | void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); | 42 | void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); |
| 42 | 43 | ||
| 43 | void AddSampledImage(VkImageView image_view, VkSampler sampler) { | 44 | void AddSampledImage(VkImageView image_view, VkSampler sampler) { |
| 44 | payload.emplace_back(VkDescriptorImageInfo{ | 45 | *(payload_cursor++) = VkDescriptorImageInfo{ |
| 45 | .sampler = sampler, | 46 | .sampler = sampler, |
| 46 | .imageView = image_view, | 47 | .imageView = image_view, |
| 47 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | 48 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, |
| 48 | }); | 49 | }; |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | void AddImage(VkImageView image_view) { | 52 | void AddImage(VkImageView image_view) { |
| 52 | payload.emplace_back(VkDescriptorImageInfo{ | 53 | *(payload_cursor++) = VkDescriptorImageInfo{ |
| 53 | .sampler = VK_NULL_HANDLE, | 54 | .sampler = VK_NULL_HANDLE, |
| 54 | .imageView = image_view, | 55 | .imageView = image_view, |
| 55 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | 56 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, |
| 56 | }); | 57 | }; |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | void AddBuffer(VkBuffer buffer, u64 offset, size_t size) { | 60 | void AddBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size) { |
| 60 | payload.emplace_back(VkDescriptorBufferInfo{ | 61 | *(payload_cursor++) = VkDescriptorBufferInfo{ |
| 61 | .buffer = buffer, | 62 | .buffer = buffer, |
| 62 | .offset = offset, | 63 | .offset = offset, |
| 63 | .range = size, | 64 | .range = size, |
| 64 | }); | 65 | }; |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | void AddTexelBuffer(VkBufferView texel_buffer) { | 68 | void AddTexelBuffer(VkBufferView texel_buffer) { |
| 68 | payload.emplace_back(texel_buffer); | 69 | *(payload_cursor++) = texel_buffer; |
| 69 | } | 70 | } |
| 70 | 71 | ||
| 71 | private: | 72 | private: |
| 72 | const Device& device; | 73 | const Device& device; |
| 73 | VKScheduler& scheduler; | 74 | VKScheduler& scheduler; |
| 74 | 75 | ||
| 76 | DescriptorUpdateEntry* payload_cursor = nullptr; | ||
| 75 | const DescriptorUpdateEntry* upload_start = nullptr; | 77 | const DescriptorUpdateEntry* upload_start = nullptr; |
| 76 | boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload; | 78 | std::array<DescriptorUpdateEntry, 0x10000> payload; |
| 77 | }; | 79 | }; |
| 78 | 80 | ||
| 79 | } // namespace Vulkan | 81 | } // namespace Vulkan |
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index 50f4e7d35..7728f600e 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp | |||
| @@ -330,6 +330,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 330 | case StoreType::Bits32: | 330 | case StoreType::Bits32: |
| 331 | (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); | 331 | (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); |
| 332 | break; | 332 | break; |
| 333 | case StoreType::Unsigned16: | ||
| 333 | case StoreType::Signed16: { | 334 | case StoreType::Signed16: { |
| 334 | Node address = GetAddress(0); | 335 | Node address = GetAddress(0); |
| 335 | Node memory = (this->*get_memory)(address); | 336 | Node memory = (this->*get_memory)(address); |
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index 833fa2a39..c69681e8d 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp | |||
| @@ -806,6 +806,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is | |||
| 806 | 806 | ||
| 807 | const std::size_t type_coord_count = GetCoordCount(texture_type); | 807 | const std::size_t type_coord_count = GetCoordCount(texture_type); |
| 808 | const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL; | 808 | const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL; |
| 809 | const bool aoffi_enabled = instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI); | ||
| 809 | 810 | ||
| 810 | // If enabled arrays index is always stored in the gpr8 field | 811 | // If enabled arrays index is always stored in the gpr8 field |
| 811 | const u64 array_register = instr.gpr8.Value(); | 812 | const u64 array_register = instr.gpr8.Value(); |
| @@ -820,17 +821,23 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is | |||
| 820 | std::vector<Node> coords; | 821 | std::vector<Node> coords; |
| 821 | for (std::size_t i = 0; i < type_coord_count; ++i) { | 822 | for (std::size_t i = 0; i < type_coord_count; ++i) { |
| 822 | const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1); | 823 | const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1); |
| 823 | coords.push_back(GetRegister(last ? last_coord_register : coord_register + i)); | 824 | coords.push_back( |
| 825 | GetRegister(last && !aoffi_enabled ? last_coord_register : coord_register + i)); | ||
| 824 | } | 826 | } |
| 825 | 827 | ||
| 826 | const Node array = is_array ? GetRegister(array_register) : nullptr; | 828 | const Node array = is_array ? GetRegister(array_register) : nullptr; |
| 827 | // When lod is used always is in gpr20 | 829 | // When lod is used always is in gpr20 |
| 828 | const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0); | 830 | const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0); |
| 829 | 831 | ||
| 832 | std::vector<Node> aoffi; | ||
| 833 | if (aoffi_enabled) { | ||
| 834 | aoffi = GetAoffiCoordinates(GetRegister(instr.gpr20), type_coord_count, false); | ||
| 835 | } | ||
| 836 | |||
| 830 | Node4 values; | 837 | Node4 values; |
| 831 | for (u32 element = 0; element < values.size(); ++element) { | 838 | for (u32 element = 0; element < values.size(); ++element) { |
| 832 | auto coords_copy = coords; | 839 | auto coords_copy = coords; |
| 833 | MetaTexture meta{*sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}}; | 840 | MetaTexture meta{*sampler, array, {}, aoffi, {}, {}, {}, lod, {}, element, {}}; |
| 834 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 841 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 835 | } | 842 | } |
| 836 | return values; | 843 | return values; |