Compare commits
59 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bae4e6c2f5 | ||
|
|
fff2de8224 | ||
|
|
f3bb52c0a9 | ||
|
|
ab350b8975 | ||
|
|
42a00877a2 | ||
|
|
9619964e8c | ||
|
|
cfb7fd395c | ||
|
|
b62a8ca43e | ||
|
|
79504f1a39 | ||
|
|
25cd5d9dda | ||
|
|
2624b1eae6 | ||
|
|
ca5ed50655 | ||
|
|
baff9ffcac | ||
|
|
66ac7cf730 | ||
|
|
53fbf8e206 | ||
|
|
3ea3de4ecd | ||
|
|
3b8a8cf825 | ||
|
|
354811e556 | ||
|
|
acbae572d3 | ||
|
|
b1fa647f28 | ||
|
|
2d2e235bcf | ||
|
|
f6bb905182 | ||
|
|
551882e512 | ||
|
|
ed9ae5a977 | ||
|
|
999fc2fece | ||
|
|
bea9ed2548 | ||
|
|
00573fb2c9 | ||
|
|
56c6a5def8 | ||
|
|
0c885249df | ||
|
|
3472c00b3b | ||
|
|
0530dd6ea1 | ||
|
|
d8a90e6536 | ||
|
|
cbaf1bc711 | ||
|
|
bbb5623f52 | ||
|
|
a91acd5365 | ||
|
|
fde8102a41 | ||
|
|
ada9b7fb77 | ||
|
|
f49ffdd648 | ||
|
|
1b829fbd7a | ||
|
|
31a76410e8 | ||
|
|
c02464f64e | ||
|
|
4539073ce1 | ||
|
|
6ac97405df | ||
|
|
836ec9176a | ||
|
|
e23289d869 | ||
|
|
d09456fc41 | ||
|
|
fe97d34156 | ||
|
|
dbb1cbce67 | ||
|
|
9f972b7d01 | ||
|
|
fe86109877 | ||
|
|
969a4cc4da | ||
|
|
34ec64233a | ||
|
|
167d36ec3c | ||
|
|
c8135b3c18 | ||
|
|
6b13d08822 | ||
|
|
04bb47f57f | ||
|
|
09f884b7bd | ||
|
|
cf946312ca | ||
|
|
1146049de0 |
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 82417da780...0e1112b7df
@@ -172,7 +172,6 @@ add_library(common STATIC
|
||||
virtual_buffer.h
|
||||
wall_clock.cpp
|
||||
wall_clock.h
|
||||
web_result.h
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
)
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Common {
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Check if type is like an STL container
|
||||
template <typename T>
|
||||
concept IsSTLContainer = requires(T t) {
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Common {
|
||||
DynamicLibrary::DynamicLibrary() = default;
|
||||
|
||||
DynamicLibrary::DynamicLibrary(const char* filename) {
|
||||
Open(filename);
|
||||
void(Open(filename));
|
||||
}
|
||||
|
||||
DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
|
||||
|
||||
@@ -909,10 +909,10 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
IOFile::IOFile() {}
|
||||
IOFile::IOFile() = default;
|
||||
|
||||
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
|
||||
Open(filename, openmode, flags);
|
||||
void(Open(filename, openmode, flags));
|
||||
}
|
||||
|
||||
IOFile::~IOFile() {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#endif
|
||||
|
||||
namespace Telemetry {
|
||||
namespace Common::Telemetry {
|
||||
|
||||
void FieldCollection::Accept(VisitorInterface& visitor) const {
|
||||
for (const auto& field : fields) {
|
||||
@@ -88,4 +88,4 @@ void AppendOSInfo(FieldCollection& fc) {
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Telemetry
|
||||
} // namespace Common::Telemetry
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Telemetry {
|
||||
namespace Common::Telemetry {
|
||||
|
||||
/// Field type, used for grouping fields together in the final submitted telemetry log
|
||||
enum class FieldType : u8 {
|
||||
@@ -196,4 +196,4 @@ void AppendCPUInfo(FieldCollection& fc);
|
||||
/// such as platform name, etc.
|
||||
void AppendOSInfo(FieldCollection& fc);
|
||||
|
||||
} // namespace Telemetry
|
||||
} // namespace Common::Telemetry
|
||||
|
||||
@@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
@@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
|
||||
config.unsafe_optimizations = true;
|
||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
@@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
|
||||
config.unsafe_optimizations = true;
|
||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -269,14 +269,14 @@ struct System::Impl {
|
||||
// Log last frame performance stats if game was loded
|
||||
if (perf_stats) {
|
||||
const auto perf_results = GetAndResetPerfStats();
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance,
|
||||
"Shutdown_EmulationSpeed",
|
||||
constexpr auto performance = Common::Telemetry::FieldType::Performance;
|
||||
|
||||
telemetry_session->AddField(performance, "Shutdown_EmulationSpeed",
|
||||
perf_results.emulation_speed * 100.0);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
|
||||
perf_results.game_fps);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
||||
telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps);
|
||||
telemetry_session->AddField(performance, "Shutdown_Frametime",
|
||||
perf_results.frametime * 1000.0);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
|
||||
telemetry_session->AddField(performance, "Mean_Frametime_MS",
|
||||
perf_stats->GetMeanFrametime());
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
constexpr u64 MAX_SLICE_LENGTH = 4000;
|
||||
constexpr s64 MAX_SLICE_LENGTH = 4000;
|
||||
|
||||
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
|
||||
return std::make_shared<EventType>(std::move(callback), std::move(name));
|
||||
@@ -37,10 +37,8 @@ struct CoreTiming::Event {
|
||||
}
|
||||
};
|
||||
|
||||
CoreTiming::CoreTiming() {
|
||||
clock =
|
||||
Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
|
||||
}
|
||||
CoreTiming::CoreTiming()
|
||||
: clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
|
||||
|
||||
CoreTiming::~CoreTiming() = default;
|
||||
|
||||
@@ -136,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
|
||||
void CoreTiming::AddTicks(u64 ticks) {
|
||||
this->ticks += ticks;
|
||||
downcount -= ticks;
|
||||
downcount -= static_cast<s64>(ticks);
|
||||
}
|
||||
|
||||
void CoreTiming::Idle() {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <limits>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uint128.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <chrono>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
|
||||
|
||||
for (std::size_t i = 0; i < size; i += sector_size) {
|
||||
SetIV(CalculateNintendoTweak(sector_id++));
|
||||
Transcode<u8, u8>(src + i, sector_size, dest + i, op);
|
||||
Transcode(src + i, sector_size, dest + i, op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
namespace {
|
||||
|
||||
constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
|
||||
constexpr u64 FULL_TICKET_SIZE = 0x400;
|
||||
@@ -49,7 +50,72 @@ constexpr std::array eticket_source_hashes{
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
|
||||
constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{
|
||||
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
|
||||
{"eticket_rsa_kek_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
|
||||
{"eticket_rsa_kekek_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
|
||||
{"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
|
||||
{"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
|
||||
{"rsa_oaep_kek_generation_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
|
||||
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
|
||||
{"aes_kek_generation_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
|
||||
{"aes_key_generation_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
|
||||
{"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
|
||||
{"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
|
||||
{"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
|
||||
{"key_area_key_application_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
|
||||
static_cast<u64>(KeyAreaKeyType::Application)}},
|
||||
{"key_area_key_ocean_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
|
||||
static_cast<u64>(KeyAreaKeyType::Ocean)}},
|
||||
{"key_area_key_system_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
|
||||
static_cast<u64>(KeyAreaKeyType::System)}},
|
||||
{"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
|
||||
{"keyblob_mac_key_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
|
||||
{"tsec_key", {S128KeyType::TSEC, 0, 0}},
|
||||
{"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
|
||||
{"sd_seed", {S128KeyType::SDSeed, 0, 0}},
|
||||
{"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"header_kek", {S128KeyType::HeaderKek, 0, 0}},
|
||||
{"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
|
||||
}};
|
||||
|
||||
auto Find128ByName(std::string_view name) {
|
||||
return std::find_if(s128_file_id.begin(), s128_file_id.end(),
|
||||
[&name](const auto& pair) { return pair.first == name; });
|
||||
}
|
||||
|
||||
constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{
|
||||
{"header_key", {S256KeyType::Header, 0, 0}},
|
||||
{"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
|
||||
{"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
|
||||
{"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
|
||||
{"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
|
||||
{"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
|
||||
}};
|
||||
|
||||
auto Find256ByName(std::string_view name) {
|
||||
return std::find_if(s256_file_id.begin(), s256_file_id.end(),
|
||||
[&name](const auto& pair) { return pair.first == name; });
|
||||
}
|
||||
|
||||
using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>;
|
||||
constexpr KeyArray KEYS_VARIABLE_LENGTH{{
|
||||
{{S128KeyType::Master, 0}, "master_key_"},
|
||||
{{S128KeyType::Package1, 0}, "package1_key_"},
|
||||
{{S128KeyType::Package2, 0}, "package2_key_"},
|
||||
@@ -57,14 +123,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
|
||||
{{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
|
||||
{{S128KeyType::Keyblob, 0}, "keyblob_key_"},
|
||||
{{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
|
||||
};
|
||||
}};
|
||||
|
||||
namespace {
|
||||
template <std::size_t Size>
|
||||
bool IsAllZeroArray(const std::array<u8, Size>& array) {
|
||||
return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; });
|
||||
}
|
||||
} // namespace
|
||||
} // Anonymous namespace
|
||||
|
||||
u64 GetSignatureTypeDataSize(SignatureType type) {
|
||||
switch (type) {
|
||||
@@ -564,13 +629,13 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
|
||||
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
||||
} else {
|
||||
out[0] = Common::ToLower(out[0]);
|
||||
if (s128_file_id.find(out[0]) != s128_file_id.end()) {
|
||||
const auto index = s128_file_id.at(out[0]);
|
||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
||||
const auto& index = iter128->second;
|
||||
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||
s128_keys[{index.type, index.field1, index.field2}] = key;
|
||||
} else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
|
||||
const auto index = s256_file_id.at(out[0]);
|
||||
Key256 key = Common::HexStringToArray<32>(out[1]);
|
||||
} else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) {
|
||||
const auto& index = iter256->second;
|
||||
const Key256 key = Common::HexStringToArray<32>(out[1]);
|
||||
s256_keys[{index.type, index.field1, index.field2}] = key;
|
||||
} else if (out[0].compare(0, 8, "keyblob_") == 0 &&
|
||||
out[0].compare(0, 9, "keyblob_k") != 0) {
|
||||
@@ -742,8 +807,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
}
|
||||
|
||||
const auto iter2 = std::find_if(
|
||||
s128_file_id.begin(), s128_file_id.end(),
|
||||
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
|
||||
s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
@@ -753,9 +817,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
|
||||
// Variable cases
|
||||
if (id == S128KeyType::KeyArea) {
|
||||
static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
|
||||
"key_area_key_ocean_{:02X}",
|
||||
"key_area_key_system_{:02X}"};
|
||||
static constexpr std::array<const char*, 3> kak_names = {
|
||||
"key_area_key_application_{:02X}",
|
||||
"key_area_key_ocean_{:02X}",
|
||||
"key_area_key_system_{:02X}",
|
||||
};
|
||||
WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
|
||||
} else if (id == S128KeyType::Master) {
|
||||
WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
|
||||
@@ -781,8 +847,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
|
||||
return;
|
||||
}
|
||||
const auto iter = std::find_if(
|
||||
s256_file_id.begin(), s256_file_id.end(),
|
||||
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
|
||||
s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
@@ -1245,58 +1310,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) {
|
||||
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
|
||||
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
|
||||
{"eticket_rsa_kek_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
|
||||
{"eticket_rsa_kekek_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
|
||||
{"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
|
||||
{"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
|
||||
{"rsa_oaep_kek_generation_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
|
||||
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
|
||||
{"aes_kek_generation_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
|
||||
{"aes_key_generation_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
|
||||
{"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
|
||||
{"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
|
||||
{"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
|
||||
{"key_area_key_application_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
|
||||
static_cast<u64>(KeyAreaKeyType::Application)}},
|
||||
{"key_area_key_ocean_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
|
||||
static_cast<u64>(KeyAreaKeyType::Ocean)}},
|
||||
{"key_area_key_system_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
|
||||
static_cast<u64>(KeyAreaKeyType::System)}},
|
||||
{"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
|
||||
{"keyblob_mac_key_source",
|
||||
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
|
||||
{"tsec_key", {S128KeyType::TSEC, 0, 0}},
|
||||
{"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
|
||||
{"sd_seed", {S128KeyType::SDSeed, 0, 0}},
|
||||
{"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
|
||||
{"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
|
||||
{"header_kek", {S128KeyType::HeaderKek, 0, 0}},
|
||||
{"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
|
||||
};
|
||||
|
||||
const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
|
||||
{"header_key", {S256KeyType::Header, 0, 0}},
|
||||
{"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
|
||||
{"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
|
||||
{"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
|
||||
{"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
|
||||
{"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
|
||||
};
|
||||
} // namespace Core::Crypto
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <string>
|
||||
|
||||
#include <variant>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -293,9 +292,6 @@ private:
|
||||
|
||||
void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
|
||||
void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
|
||||
|
||||
static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
|
||||
static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
|
||||
};
|
||||
|
||||
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
|
||||
|
||||
@@ -367,8 +367,8 @@ static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header
|
||||
Package2Header temp = header;
|
||||
AESCipher<Key128> cipher(key, Mode::CTR);
|
||||
cipher.SetIV(header.header_ctr);
|
||||
cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
|
||||
Op::Decrypt);
|
||||
cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature),
|
||||
&temp.header_ctr, Op::Decrypt);
|
||||
if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
|
||||
header = temp;
|
||||
return true;
|
||||
|
||||
@@ -72,8 +72,10 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
|
||||
|
||||
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
|
||||
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||
if (cache.find(path) != cache.end()) {
|
||||
auto weak = cache[path];
|
||||
|
||||
if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) {
|
||||
const auto& weak = weak_iter->second;
|
||||
|
||||
if (!weak.expired()) {
|
||||
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
|
||||
}
|
||||
@@ -84,7 +86,7 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
|
||||
}
|
||||
|
||||
auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
|
||||
cache[path] = backing;
|
||||
cache.insert_or_assign(path, backing);
|
||||
|
||||
// Cannot use make_shared as RealVfsFile constructor is private
|
||||
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
|
||||
@@ -116,11 +118,12 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_
|
||||
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
|
||||
const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
|
||||
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
|
||||
const auto cached_file_iter = cache.find(old_path);
|
||||
|
||||
if (cache.find(old_path) != cache.end()) {
|
||||
auto file = cache[old_path].lock();
|
||||
if (cached_file_iter != cache.cend()) {
|
||||
auto file = cached_file_iter->second.lock();
|
||||
|
||||
if (!cache[old_path].expired()) {
|
||||
if (!cached_file_iter->second.expired()) {
|
||||
file->Close();
|
||||
}
|
||||
|
||||
@@ -131,7 +134,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
|
||||
cache.erase(old_path);
|
||||
file->Open(new_path, "r+b");
|
||||
cache[new_path] = file;
|
||||
cache.insert_or_assign(new_path, std::move(file));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
@@ -142,12 +145,15 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
|
||||
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
|
||||
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||
if (cache.find(path) != cache.end()) {
|
||||
if (!cache[path].expired()) {
|
||||
cache[path].lock()->Close();
|
||||
const auto cached_iter = cache.find(path);
|
||||
|
||||
if (cached_iter != cache.cend()) {
|
||||
if (!cached_iter->second.expired()) {
|
||||
cached_iter->second.lock()->Close();
|
||||
}
|
||||
cache.erase(path);
|
||||
}
|
||||
|
||||
return FS::Delete(path);
|
||||
}
|
||||
|
||||
@@ -192,21 +198,25 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
|
||||
}
|
||||
|
||||
for (auto& kv : cache) {
|
||||
// Path in cache starts with old_path
|
||||
if (kv.first.rfind(old_path, 0) == 0) {
|
||||
const auto file_old_path =
|
||||
FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
|
||||
const auto file_new_path =
|
||||
FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
|
||||
FS::DirectorySeparator::PlatformDefault);
|
||||
auto cached = cache[file_old_path];
|
||||
if (!cached.expired()) {
|
||||
auto file = cached.lock();
|
||||
file->Open(file_new_path, "r+b");
|
||||
cache.erase(file_old_path);
|
||||
cache[file_new_path] = file;
|
||||
}
|
||||
// If the path in the cache doesn't start with old_path, then bail on this file.
|
||||
if (kv.first.rfind(old_path, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto file_old_path =
|
||||
FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
|
||||
auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
|
||||
FS::DirectorySeparator::PlatformDefault);
|
||||
const auto& cached = cache[file_old_path];
|
||||
|
||||
if (cached.expired()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto file = cached.lock();
|
||||
file->Open(file_new_path, "r+b");
|
||||
cache.erase(file_old_path);
|
||||
cache.insert_or_assign(std::move(file_new_path), std::move(file));
|
||||
}
|
||||
|
||||
return OpenDirectory(new_path, Mode::ReadWrite);
|
||||
@@ -214,15 +224,21 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
|
||||
|
||||
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
|
||||
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||
|
||||
for (auto& kv : cache) {
|
||||
// Path in cache starts with old_path
|
||||
if (kv.first.rfind(path, 0) == 0) {
|
||||
if (!cache[kv.first].expired()) {
|
||||
cache[kv.first].lock()->Close();
|
||||
}
|
||||
cache.erase(kv.first);
|
||||
// If the path in the cache doesn't start with path, then bail on this file.
|
||||
if (kv.first.rfind(path, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& entry = cache[kv.first];
|
||||
if (!entry.expired()) {
|
||||
entry.lock()->Close();
|
||||
}
|
||||
|
||||
cache.erase(kv.first);
|
||||
}
|
||||
|
||||
return FS::DeleteDirRecursively(path);
|
||||
}
|
||||
|
||||
@@ -260,14 +276,14 @@ bool RealVfsFile::IsReadable() const {
|
||||
}
|
||||
|
||||
std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||
if (!backing->Seek(offset, SEEK_SET)) {
|
||||
if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
|
||||
return 0;
|
||||
}
|
||||
return backing->ReadBytes(data, length);
|
||||
}
|
||||
|
||||
std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
|
||||
if (!backing->Seek(offset, SEEK_SET)) {
|
||||
if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
|
||||
return 0;
|
||||
}
|
||||
return backing->WriteBytes(data, length);
|
||||
|
||||
@@ -359,7 +359,8 @@ enum class GPUAccuracy : u32 {
|
||||
|
||||
enum class CPUAccuracy {
|
||||
Accurate = 0,
|
||||
DebugMode = 1,
|
||||
Unsafe = 1,
|
||||
DebugMode = 2,
|
||||
};
|
||||
|
||||
extern bool configuring_global;
|
||||
@@ -419,6 +420,9 @@ struct Values {
|
||||
bool cpuopt_misc_ir;
|
||||
bool cpuopt_reduce_misalign_checks;
|
||||
|
||||
bool cpuopt_unsafe_unfuse_fma;
|
||||
bool cpuopt_unsafe_reduce_fp_error;
|
||||
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend;
|
||||
bool renderer_debug;
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace Telemetry = Common::Telemetry;
|
||||
|
||||
static u64 GenerateTelemetryId() {
|
||||
u64 telemetry_id{};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
* @param value Value for the field to add.
|
||||
*/
|
||||
template <typename T>
|
||||
void AddField(Telemetry::FieldType type, const char* name, T value) {
|
||||
void AddField(Common::Telemetry::FieldType type, const char* name, T value) {
|
||||
field_collection.AddField(type, name, std::move(value));
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ public:
|
||||
bool SubmitTestcase();
|
||||
|
||||
private:
|
||||
Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
|
||||
/// Tracks all added fields for the session
|
||||
Common::Telemetry::FieldCollection field_collection;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,15 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
@@ -34,7 +34,6 @@ void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 metho
|
||||
this->parameters = std::make_unique<u32[]>(num_parameters);
|
||||
}
|
||||
std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32));
|
||||
this->num_parameters = num_parameters;
|
||||
|
||||
// Execute the code until we hit an exit condition.
|
||||
bool keep_executing = true;
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_fence_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed)
|
||||
: VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
|
||||
GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) : FenceBase(payload, is_stubbed) {}
|
||||
|
||||
GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
|
||||
: VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {}
|
||||
: FenceBase(address, payload, is_stubbed) {}
|
||||
|
||||
GLInnerFence::~GLInnerFence() = default;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/fence_manager.h"
|
||||
|
||||
@@ -177,15 +177,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
|
||||
}
|
||||
|
||||
if (device.UseAsynchronousShaders()) {
|
||||
// Max worker threads we should allow
|
||||
constexpr u32 MAX_THREADS = 4;
|
||||
// Deduce how many threads we can use
|
||||
const u32 threads_used = std::thread::hardware_concurrency() / 4;
|
||||
// Always allow at least 1 thread regardless of our settings
|
||||
const auto max_worker_count = std::max(1U, threads_used);
|
||||
// Don't use more than MAX_THREADS
|
||||
const auto worker_count = std::min(max_worker_count, MAX_THREADS);
|
||||
async_shaders.AllocateWorkers(worker_count);
|
||||
async_shaders.AllocateWorkers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,20 +214,20 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
|
||||
// Skip games without title id
|
||||
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Common::FS::IOFile file(GetTransferablePath(), "rb");
|
||||
if (!file.IsOpen()) {
|
||||
LOG_INFO(Render_OpenGL, "No transferable shader cache found");
|
||||
is_usable = true;
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u32 version{};
|
||||
if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
|
||||
LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (version < NativeVersion) {
|
||||
@@ -235,12 +235,12 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
|
||||
file.Close();
|
||||
InvalidateTransferable();
|
||||
is_usable = true;
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
if (version > NativeVersion) {
|
||||
LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version "
|
||||
"of the emulator, skipping");
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Version is valid, load the shaders
|
||||
@@ -249,7 +249,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
|
||||
ShaderDiskCacheEntry& entry = entries.emplace_back();
|
||||
if (!entry.Load(file)) {
|
||||
LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,12 +290,12 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
|
||||
ShaderCacheVersionHash file_hash{};
|
||||
if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) {
|
||||
precompiled_cache_virtual_file_offset = 0;
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
if (GetShaderCacheVersionHash() != file_hash) {
|
||||
LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
|
||||
precompiled_cache_virtual_file_offset = 0;
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<ShaderDiskCachePrecompiled> entries;
|
||||
@@ -305,15 +305,16 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
|
||||
if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
|
||||
!LoadObjectFromPrecompiled(entry.binary_format) ||
|
||||
!LoadObjectFromPrecompiled(binary_size)) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
entry.binary.resize(binary_size);
|
||||
if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
|
||||
return std::move(entries);
|
||||
}
|
||||
|
||||
void ShaderDiskCacheOpenGL::InvalidateTransferable() {
|
||||
|
||||
@@ -403,7 +403,7 @@ void CachedSurface::DecorateSurfaceName() {
|
||||
LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName());
|
||||
}
|
||||
|
||||
void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) {
|
||||
void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix) {
|
||||
LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
Tegra::Texture::SwizzleSource z_source,
|
||||
Tegra::Texture::SwizzleSource w_source);
|
||||
|
||||
void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
|
||||
void DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix);
|
||||
|
||||
void MarkAsModified(u64 tick) {
|
||||
surface.MarkAsModified(true, tick);
|
||||
|
||||
@@ -509,9 +509,10 @@ void RendererOpenGL::AddTelemetryFields() {
|
||||
LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
|
||||
|
||||
auto& telemetry_session = system.TelemetrySession();
|
||||
telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor);
|
||||
telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
|
||||
telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version);
|
||||
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
|
||||
telemetry_session.AddField(user_system, "GPU_Vendor", gpu_vendor);
|
||||
telemetry_session.AddField(user_system, "GPU_Model", gpu_model);
|
||||
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", gl_version);
|
||||
}
|
||||
|
||||
void RendererOpenGL::CreateRasterizer() {
|
||||
|
||||
@@ -439,7 +439,7 @@ void RendererVulkan::Report() const {
|
||||
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
|
||||
|
||||
auto& telemetry_session = system.TelemetrySession();
|
||||
constexpr auto field = Telemetry::FieldType::UserSystem;
|
||||
constexpr auto field = Common::Telemetry::FieldType::UserSystem;
|
||||
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
|
||||
telemetry_session.AddField(field, "GPU_Model", model_name);
|
||||
telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
|
||||
|
||||
@@ -382,6 +382,8 @@ bool VKDevice::Create() {
|
||||
|
||||
graphics_queue = logical.GetQueue(graphics_family);
|
||||
present_queue = logical.GetQueue(present_family);
|
||||
|
||||
use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,6 +202,11 @@ public:
|
||||
return reported_extensions;
|
||||
}
|
||||
|
||||
/// Returns true if the setting for async shader compilation is enabled.
|
||||
bool UseAsynchronousShaders() const {
|
||||
return use_asynchronous_shaders;
|
||||
}
|
||||
|
||||
/// Checks if the physical device is suitable.
|
||||
static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface);
|
||||
|
||||
@@ -252,6 +257,9 @@ private:
|
||||
bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
|
||||
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
|
||||
|
||||
// Asynchronous Graphics Pipeline setting
|
||||
bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
|
||||
|
||||
// Telemetry parameters
|
||||
std::string vendor_name; ///< Device's driver name.
|
||||
std::vector<std::string> reported_extensions; ///< Reported Vulkan extensions.
|
||||
|
||||
@@ -29,7 +29,7 @@ void InnerFence::Queue() {
|
||||
}
|
||||
ASSERT(!event);
|
||||
|
||||
event = device.GetLogical().CreateEvent();
|
||||
event = device.GetLogical().CreateNewEvent();
|
||||
ticks = scheduler.Ticks();
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
@@ -78,15 +78,14 @@ VKGraphicsPipeline::VKGraphicsPipeline(const VKDevice& device, VKScheduler& sche
|
||||
const GraphicsPipelineCacheKey& key,
|
||||
vk::Span<VkDescriptorSetLayoutBinding> bindings,
|
||||
const SPIRVProgram& program)
|
||||
: device{device}, scheduler{scheduler}, fixed_state{key.fixed_state}, hash{key.Hash()},
|
||||
: device{device}, scheduler{scheduler}, cache_key{key}, hash{cache_key.Hash()},
|
||||
descriptor_set_layout{CreateDescriptorSetLayout(bindings)},
|
||||
descriptor_allocator{descriptor_pool, *descriptor_set_layout},
|
||||
update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()},
|
||||
descriptor_template{CreateDescriptorUpdateTemplate(program)}, modules{CreateShaderModules(
|
||||
program)},
|
||||
renderpass{renderpass_cache.GetRenderPass(key.renderpass_params)}, pipeline{CreatePipeline(
|
||||
key.renderpass_params,
|
||||
program)} {}
|
||||
renderpass{renderpass_cache.GetRenderPass(cache_key.renderpass_params)},
|
||||
pipeline{CreatePipeline(cache_key.renderpass_params, program)} {}
|
||||
|
||||
VKGraphicsPipeline::~VKGraphicsPipeline() = default;
|
||||
|
||||
@@ -181,7 +180,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
|
||||
|
||||
vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
|
||||
const SPIRVProgram& program) const {
|
||||
const auto& state = fixed_state;
|
||||
const auto& state = cache_key.fixed_state;
|
||||
const auto& viewport_swizzles = state.viewport_swizzles;
|
||||
|
||||
FixedPipelineState::DynamicState dynamic;
|
||||
|
||||
@@ -19,7 +19,27 @@ namespace Vulkan {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
struct GraphicsPipelineCacheKey;
|
||||
struct GraphicsPipelineCacheKey {
|
||||
RenderPassParams renderpass_params;
|
||||
u32 padding;
|
||||
std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
|
||||
FixedPipelineState fixed_state;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::size_t Size() const noexcept {
|
||||
return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
|
||||
static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
|
||||
static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
|
||||
|
||||
class VKDescriptorPool;
|
||||
class VKDevice;
|
||||
@@ -54,6 +74,10 @@ public:
|
||||
return renderpass;
|
||||
}
|
||||
|
||||
GraphicsPipelineCacheKey GetCacheKey() const {
|
||||
return cache_key;
|
||||
}
|
||||
|
||||
private:
|
||||
vk::DescriptorSetLayout CreateDescriptorSetLayout(
|
||||
vk::Span<VkDescriptorSetLayoutBinding> bindings) const;
|
||||
@@ -70,7 +94,7 @@ private:
|
||||
|
||||
const VKDevice& device;
|
||||
VKScheduler& scheduler;
|
||||
const FixedPipelineState fixed_state;
|
||||
const GraphicsPipelineCacheKey cache_key;
|
||||
const u64 hash;
|
||||
|
||||
vk::DescriptorSetLayout descriptor_set_layout;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "video_core/shader/compiler_settings.h"
|
||||
#include "video_core/shader/memory_util.h"
|
||||
#include "video_core/shader_cache.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@@ -205,24 +206,43 @@ std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
|
||||
return last_shaders = shaders;
|
||||
}
|
||||
|
||||
VKGraphicsPipeline& VKPipelineCache::GetGraphicsPipeline(const GraphicsPipelineCacheKey& key) {
|
||||
VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
|
||||
const GraphicsPipelineCacheKey& key, VideoCommon::Shader::AsyncShaders& async_shaders) {
|
||||
MICROPROFILE_SCOPE(Vulkan_PipelineCache);
|
||||
|
||||
if (last_graphics_pipeline && last_graphics_key == key) {
|
||||
return *last_graphics_pipeline;
|
||||
return last_graphics_pipeline;
|
||||
}
|
||||
last_graphics_key = key;
|
||||
|
||||
if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(system.GPU())) {
|
||||
std::unique_lock lock{pipeline_cache};
|
||||
const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
|
||||
if (is_cache_miss) {
|
||||
system.GPU().ShaderNotify().MarkSharderBuilding();
|
||||
LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
|
||||
const auto [program, bindings] = DecompileShaders(key.fixed_state);
|
||||
async_shaders.QueueVulkanShader(this, device, scheduler, descriptor_pool,
|
||||
update_descriptor_queue, renderpass_cache, bindings,
|
||||
program, key);
|
||||
}
|
||||
last_graphics_pipeline = pair->second.get();
|
||||
return last_graphics_pipeline;
|
||||
}
|
||||
|
||||
const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
|
||||
auto& entry = pair->second;
|
||||
if (is_cache_miss) {
|
||||
system.GPU().ShaderNotify().MarkSharderBuilding();
|
||||
LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
|
||||
const auto [program, bindings] = DecompileShaders(key);
|
||||
const auto [program, bindings] = DecompileShaders(key.fixed_state);
|
||||
entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool,
|
||||
update_descriptor_queue, renderpass_cache, key,
|
||||
bindings, program);
|
||||
system.GPU().ShaderNotify().MarkShaderComplete();
|
||||
}
|
||||
return *(last_graphics_pipeline = entry.get());
|
||||
last_graphics_pipeline = entry.get();
|
||||
return last_graphics_pipeline;
|
||||
}
|
||||
|
||||
VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCacheKey& key) {
|
||||
@@ -277,6 +297,12 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
|
||||
return *entry;
|
||||
}
|
||||
|
||||
void VKPipelineCache::EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline) {
|
||||
system.GPU().ShaderNotify().MarkShaderComplete();
|
||||
std::unique_lock lock{pipeline_cache};
|
||||
graphics_cache.at(pipeline->GetCacheKey()) = std::move(pipeline);
|
||||
}
|
||||
|
||||
void VKPipelineCache::OnShaderRemoval(Shader* shader) {
|
||||
bool finished = false;
|
||||
const auto Finish = [&] {
|
||||
@@ -312,8 +338,7 @@ void VKPipelineCache::OnShaderRemoval(Shader* shader) {
|
||||
}
|
||||
|
||||
std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>>
|
||||
VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
|
||||
const auto& fixed_state = key.fixed_state;
|
||||
VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
|
||||
auto& memory_manager = system.GPU().MemoryManager();
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
||||
#include "video_core/renderer_vulkan/wrapper.h"
|
||||
#include "video_core/shader/async_shaders.h"
|
||||
#include "video_core/shader/memory_util.h"
|
||||
#include "video_core/shader/registry.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
@@ -43,28 +44,6 @@ class VKUpdateDescriptorQueue;
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
struct GraphicsPipelineCacheKey {
|
||||
RenderPassParams renderpass_params;
|
||||
u32 padding;
|
||||
std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
|
||||
FixedPipelineState fixed_state;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::size_t Size() const noexcept {
|
||||
return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
|
||||
static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
|
||||
static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
|
||||
|
||||
struct ComputePipelineCacheKey {
|
||||
GPUVAddr shader;
|
||||
u32 shared_memory_size;
|
||||
@@ -152,16 +131,19 @@ public:
|
||||
|
||||
std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders();
|
||||
|
||||
VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key);
|
||||
VKGraphicsPipeline* GetGraphicsPipeline(const GraphicsPipelineCacheKey& key,
|
||||
VideoCommon::Shader::AsyncShaders& async_shaders);
|
||||
|
||||
VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key);
|
||||
|
||||
void EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline);
|
||||
|
||||
protected:
|
||||
void OnShaderRemoval(Shader* shader) final;
|
||||
|
||||
private:
|
||||
std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders(
|
||||
const GraphicsPipelineCacheKey& key);
|
||||
const FixedPipelineState& fixed_state);
|
||||
|
||||
Core::System& system;
|
||||
const VKDevice& device;
|
||||
@@ -178,6 +160,7 @@ private:
|
||||
GraphicsPipelineCacheKey last_graphics_key;
|
||||
VKGraphicsPipeline* last_graphics_pipeline = nullptr;
|
||||
|
||||
std::mutex pipeline_cache;
|
||||
std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<VKGraphicsPipeline>>
|
||||
graphics_cache;
|
||||
std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<VKComputePipeline>> compute_cache;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
@@ -400,8 +401,12 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
|
||||
buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
|
||||
sampler_cache(device),
|
||||
fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache),
|
||||
query_cache(system, *this, device, scheduler), wfi_event{device.GetLogical().CreateEvent()} {
|
||||
query_cache(system, *this, device, scheduler),
|
||||
wfi_event{device.GetLogical().CreateNewEvent()}, async_shaders{renderer} {
|
||||
scheduler.SetQueryCache(query_cache);
|
||||
if (device.UseAsynchronousShaders()) {
|
||||
async_shaders.AllocateWorkers();
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerVulkan::~RasterizerVulkan() = default;
|
||||
@@ -413,6 +418,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
SCOPE_EXIT({ system.GPU().TickWork(); });
|
||||
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
GraphicsPipelineCacheKey key;
|
||||
key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
|
||||
@@ -439,10 +446,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
key.renderpass_params = GetRenderPassParams(texceptions);
|
||||
key.padding = 0;
|
||||
|
||||
auto& pipeline = pipeline_cache.GetGraphicsPipeline(key);
|
||||
scheduler.BindGraphicsPipeline(pipeline.GetHandle());
|
||||
auto* pipeline = pipeline_cache.GetGraphicsPipeline(key, async_shaders);
|
||||
if (pipeline == nullptr || pipeline->GetHandle() == VK_NULL_HANDLE) {
|
||||
// Async graphics pipeline was not ready.
|
||||
return;
|
||||
}
|
||||
|
||||
const auto renderpass = pipeline.GetRenderPass();
|
||||
scheduler.BindGraphicsPipeline(pipeline->GetHandle());
|
||||
|
||||
const auto renderpass = pipeline->GetRenderPass();
|
||||
const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass);
|
||||
scheduler.RequestRenderpass(renderpass, framebuffer, render_area);
|
||||
|
||||
@@ -452,8 +464,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
|
||||
BeginTransformFeedback();
|
||||
|
||||
const auto pipeline_layout = pipeline.GetLayout();
|
||||
const auto descriptor_set = pipeline.CommitDescriptorSet();
|
||||
const auto pipeline_layout = pipeline->GetLayout();
|
||||
const auto descriptor_set = pipeline->CommitDescriptorSet();
|
||||
scheduler.Record([pipeline_layout, descriptor_set, draw_params](vk::CommandBuffer cmdbuf) {
|
||||
if (descriptor_set) {
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
||||
@@ -463,8 +475,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
});
|
||||
|
||||
EndTransformFeedback();
|
||||
|
||||
system.GPU().TickWork();
|
||||
}
|
||||
|
||||
void RasterizerVulkan::Clear() {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/renderer_vulkan/wrapper.h"
|
||||
#include "video_core/shader/async_shaders.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -136,6 +137,14 @@ public:
|
||||
u32 pixel_stride) override;
|
||||
void SetupDirtyFlags() override;
|
||||
|
||||
VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
|
||||
return async_shaders;
|
||||
}
|
||||
|
||||
const VideoCommon::Shader::AsyncShaders& GetAsyncShaders() const {
|
||||
return async_shaders;
|
||||
}
|
||||
|
||||
/// Maximum supported size that a constbuffer can have in bytes.
|
||||
static constexpr std::size_t MaxConstbufferSize = 0x10000;
|
||||
static_assert(MaxConstbufferSize % (4 * sizeof(float)) == 0,
|
||||
@@ -297,6 +306,7 @@ private:
|
||||
vk::Buffer default_buffer;
|
||||
VKMemoryCommit default_buffer_commit;
|
||||
vk::Event wfi_event;
|
||||
VideoCommon::Shader::AsyncShaders async_shaders;
|
||||
|
||||
std::array<View, Maxwell::NumRenderTargets> color_attachments;
|
||||
View zeta_attachment;
|
||||
|
||||
@@ -644,7 +644,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
|
||||
return ShaderModule(object, handle, *dld);
|
||||
}
|
||||
|
||||
Event Device::CreateEvent() const {
|
||||
Event Device::CreateNewEvent() const {
|
||||
static constexpr VkEventCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -786,7 +786,7 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
|
||||
VK_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return properties;
|
||||
return std::move(properties);
|
||||
}
|
||||
|
||||
std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
|
||||
|
||||
@@ -721,7 +721,7 @@ public:
|
||||
|
||||
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
|
||||
|
||||
Event CreateEvent() const;
|
||||
Event CreateNewEvent() const;
|
||||
|
||||
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
@@ -20,9 +19,18 @@ AsyncShaders::~AsyncShaders() {
|
||||
KillWorkers();
|
||||
}
|
||||
|
||||
void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
|
||||
// If we're already have workers queued or don't want to queue workers, ignore
|
||||
if (num_workers == worker_threads.size() || num_workers == 0) {
|
||||
void AsyncShaders::AllocateWorkers() {
|
||||
// Max worker threads we should allow
|
||||
constexpr u32 MAX_THREADS = 4;
|
||||
// Deduce how many threads we can use
|
||||
const u32 threads_used = std::thread::hardware_concurrency() / 4;
|
||||
// Always allow at least 1 thread regardless of our settings
|
||||
const auto max_worker_count = std::max(1U, threads_used);
|
||||
// Don't use more than MAX_THREADS
|
||||
const auto num_workers = std::min(max_worker_count, MAX_THREADS);
|
||||
|
||||
// If we already have workers queued, ignore
|
||||
if (num_workers == worker_threads.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,8 +42,8 @@ void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
|
||||
// Create workers
|
||||
for (std::size_t i = 0; i < num_workers; i++) {
|
||||
context_list.push_back(emu_window.CreateSharedContext());
|
||||
worker_threads.push_back(std::move(
|
||||
std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get())));
|
||||
worker_threads.push_back(
|
||||
std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,24 +119,50 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
|
||||
VideoCommon::Shader::CompilerSettings compiler_settings,
|
||||
const VideoCommon::Shader::Registry& registry,
|
||||
VAddr cpu_addr) {
|
||||
WorkerParams params{device.UseAssemblyShaders() ? AsyncShaders::Backend::GLASM
|
||||
: AsyncShaders::Backend::OpenGL,
|
||||
device,
|
||||
shader_type,
|
||||
uid,
|
||||
std::move(code),
|
||||
std::move(code_b),
|
||||
main_offset,
|
||||
compiler_settings,
|
||||
registry,
|
||||
cpu_addr};
|
||||
WorkerParams params{
|
||||
.backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
|
||||
.device = &device,
|
||||
.shader_type = shader_type,
|
||||
.uid = uid,
|
||||
.code = std::move(code),
|
||||
.code_b = std::move(code_b),
|
||||
.main_offset = main_offset,
|
||||
.compiler_settings = compiler_settings,
|
||||
.registry = registry,
|
||||
.cpu_address = cpu_addr,
|
||||
};
|
||||
std::unique_lock lock(queue_mutex);
|
||||
pending_queue.push_back(std::move(params));
|
||||
pending_queue.push(std::move(params));
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
|
||||
const Vulkan::VKDevice& device, Vulkan::VKScheduler& scheduler,
|
||||
Vulkan::VKDescriptorPool& descriptor_pool,
|
||||
Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
Vulkan::VKRenderPassCache& renderpass_cache,
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings,
|
||||
Vulkan::SPIRVProgram program,
|
||||
Vulkan::GraphicsPipelineCacheKey key) {
|
||||
WorkerParams params{
|
||||
.backend = Backend::Vulkan,
|
||||
.pp_cache = pp_cache,
|
||||
.vk_device = &device,
|
||||
.scheduler = &scheduler,
|
||||
.descriptor_pool = &descriptor_pool,
|
||||
.update_descriptor_queue = &update_descriptor_queue,
|
||||
.renderpass_cache = &renderpass_cache,
|
||||
.bindings = bindings,
|
||||
.program = program,
|
||||
.key = key,
|
||||
};
|
||||
|
||||
std::unique_lock lock(queue_mutex);
|
||||
pending_queue.push(std::move(params));
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) {
|
||||
using namespace std::chrono_literals;
|
||||
while (!is_thread_exiting.load(std::memory_order_relaxed)) {
|
||||
std::unique_lock lock{queue_mutex};
|
||||
cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; });
|
||||
@@ -144,18 +178,17 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
|
||||
if (pending_queue.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pull work from queue
|
||||
WorkerParams work = std::move(pending_queue.front());
|
||||
pending_queue.pop_front();
|
||||
|
||||
pending_queue.pop();
|
||||
lock.unlock();
|
||||
|
||||
if (work.backend == AsyncShaders::Backend::OpenGL ||
|
||||
work.backend == AsyncShaders::Backend::GLASM) {
|
||||
const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, work.registry);
|
||||
if (work.backend == Backend::OpenGL || work.backend == Backend::GLASM) {
|
||||
const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, *work.registry);
|
||||
const auto scope = context->Acquire();
|
||||
auto program =
|
||||
OpenGL::BuildShader(work.device, work.shader_type, work.uid, ir, work.registry);
|
||||
OpenGL::BuildShader(*work.device, work.shader_type, work.uid, ir, *work.registry);
|
||||
Result result{};
|
||||
result.backend = work.backend;
|
||||
result.cpu_address = work.cpu_address;
|
||||
@@ -164,9 +197,9 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
|
||||
result.code_b = std::move(work.code_b);
|
||||
result.shader_type = work.shader_type;
|
||||
|
||||
if (work.backend == AsyncShaders::Backend::OpenGL) {
|
||||
if (work.backend == Backend::OpenGL) {
|
||||
result.program.opengl = std::move(program->source_program);
|
||||
} else if (work.backend == AsyncShaders::Backend::GLASM) {
|
||||
} else if (work.backend == Backend::GLASM) {
|
||||
result.program.glasm = std::move(program->assembly_program);
|
||||
}
|
||||
|
||||
@@ -174,6 +207,13 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
|
||||
std::unique_lock complete_lock(completed_mutex);
|
||||
finished_work.push_back(std::move(result));
|
||||
}
|
||||
} else if (work.backend == Backend::Vulkan) {
|
||||
auto pipeline = std::make_unique<Vulkan::VKGraphicsPipeline>(
|
||||
*work.vk_device, *work.scheduler, *work.descriptor_pool,
|
||||
*work.update_descriptor_queue, *work.renderpass_cache, work.key, work.bindings,
|
||||
work.program);
|
||||
|
||||
work.pp_cache->EmplacePipeline(std::move(pipeline));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
@@ -24,6 +28,10 @@ namespace Tegra {
|
||||
class GPU;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
class VKPipelineCache;
|
||||
}
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
class AsyncShaders {
|
||||
@@ -31,6 +39,7 @@ public:
|
||||
enum class Backend {
|
||||
OpenGL,
|
||||
GLASM,
|
||||
Vulkan,
|
||||
};
|
||||
|
||||
struct ResultPrograms {
|
||||
@@ -52,7 +61,7 @@ public:
|
||||
~AsyncShaders();
|
||||
|
||||
/// Start up shader worker threads
|
||||
void AllocateWorkers(std::size_t num_workers);
|
||||
void AllocateWorkers();
|
||||
|
||||
/// Clear the shader queue and kill all worker threads
|
||||
void FreeWorkers();
|
||||
@@ -76,6 +85,14 @@ public:
|
||||
VideoCommon::Shader::CompilerSettings compiler_settings,
|
||||
const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
|
||||
|
||||
void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device,
|
||||
Vulkan::VKScheduler& scheduler,
|
||||
Vulkan::VKDescriptorPool& descriptor_pool,
|
||||
Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
Vulkan::VKRenderPassCache& renderpass_cache,
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings,
|
||||
Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key);
|
||||
|
||||
private:
|
||||
void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
|
||||
|
||||
@@ -83,16 +100,28 @@ private:
|
||||
bool HasWorkQueued();
|
||||
|
||||
struct WorkerParams {
|
||||
AsyncShaders::Backend backend;
|
||||
OpenGL::Device device;
|
||||
Backend backend;
|
||||
// For OGL
|
||||
const OpenGL::Device* device;
|
||||
Tegra::Engines::ShaderType shader_type;
|
||||
u64 uid;
|
||||
std::vector<u64> code;
|
||||
std::vector<u64> code_b;
|
||||
u32 main_offset;
|
||||
VideoCommon::Shader::CompilerSettings compiler_settings;
|
||||
VideoCommon::Shader::Registry registry;
|
||||
std::optional<VideoCommon::Shader::Registry> registry;
|
||||
VAddr cpu_address;
|
||||
|
||||
// For Vulkan
|
||||
Vulkan::VKPipelineCache* pp_cache;
|
||||
const Vulkan::VKDevice* vk_device;
|
||||
Vulkan::VKScheduler* scheduler;
|
||||
Vulkan::VKDescriptorPool* descriptor_pool;
|
||||
Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue;
|
||||
Vulkan::VKRenderPassCache* renderpass_cache;
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
Vulkan::SPIRVProgram program;
|
||||
Vulkan::GraphicsPipelineCacheKey key;
|
||||
};
|
||||
|
||||
std::condition_variable cv;
|
||||
@@ -101,7 +130,7 @@ private:
|
||||
std::atomic<bool> is_thread_exiting{};
|
||||
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
|
||||
std::vector<std::thread> worker_threads;
|
||||
std::deque<WorkerParams> pending_queue;
|
||||
std::queue<WorkerParams> pending_queue;
|
||||
std::vector<AsyncShaders::Result> finished_work;
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ add_library(web_service STATIC
|
||||
verify_login.h
|
||||
web_backend.cpp
|
||||
web_backend.h
|
||||
web_result.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(web_service)
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/web_result.h"
|
||||
#include "web_service/telemetry_json.h"
|
||||
#include "web_service/web_backend.h"
|
||||
#include "web_service/web_result.h"
|
||||
|
||||
namespace WebService {
|
||||
|
||||
namespace Telemetry = Common::Telemetry;
|
||||
|
||||
struct TelemetryJson::Impl {
|
||||
Impl(std::string host, std::string username, std::string token)
|
||||
: host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
|
||||
@@ -123,7 +125,7 @@ bool TelemetryJson::SubmitTestcase() {
|
||||
Client client(impl->host, impl->username, impl->token);
|
||||
auto value = client.PostJson("/gamedb/testcase", content, false);
|
||||
|
||||
return value.result_code == Common::WebResult::Code::Success;
|
||||
return value.result_code == WebResult::Code::Success;
|
||||
}
|
||||
|
||||
} // namespace WebService
|
||||
|
||||
@@ -14,25 +14,25 @@ namespace WebService {
|
||||
* Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the
|
||||
* yuzu web service
|
||||
*/
|
||||
class TelemetryJson : public Telemetry::VisitorInterface {
|
||||
class TelemetryJson : public Common::Telemetry::VisitorInterface {
|
||||
public:
|
||||
TelemetryJson(std::string host, std::string username, std::string token);
|
||||
~TelemetryJson() override;
|
||||
|
||||
void Visit(const Telemetry::Field<bool>& field) override;
|
||||
void Visit(const Telemetry::Field<double>& field) override;
|
||||
void Visit(const Telemetry::Field<float>& field) override;
|
||||
void Visit(const Telemetry::Field<u8>& field) override;
|
||||
void Visit(const Telemetry::Field<u16>& field) override;
|
||||
void Visit(const Telemetry::Field<u32>& field) override;
|
||||
void Visit(const Telemetry::Field<u64>& field) override;
|
||||
void Visit(const Telemetry::Field<s8>& field) override;
|
||||
void Visit(const Telemetry::Field<s16>& field) override;
|
||||
void Visit(const Telemetry::Field<s32>& field) override;
|
||||
void Visit(const Telemetry::Field<s64>& field) override;
|
||||
void Visit(const Telemetry::Field<std::string>& field) override;
|
||||
void Visit(const Telemetry::Field<const char*>& field) override;
|
||||
void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<bool>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<double>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<float>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<u8>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<u16>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<u32>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<u64>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<s8>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<s16>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<s32>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<s64>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<std::string>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<const char*>& field) override;
|
||||
void Visit(const Common::Telemetry::Field<std::chrono::microseconds>& field) override;
|
||||
|
||||
void Complete() override;
|
||||
bool SubmitTestcase() override;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "common/web_result.h"
|
||||
#include "web_service/verify_login.h"
|
||||
#include "web_service/web_backend.h"
|
||||
#include "web_service/web_result.h"
|
||||
|
||||
namespace WebService {
|
||||
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include <LUrlParser.h>
|
||||
#include <fmt/format.h>
|
||||
#include <httplib.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/web_result.h"
|
||||
#include "web_service/web_backend.h"
|
||||
#include "web_service/web_result.h"
|
||||
|
||||
namespace WebService {
|
||||
|
||||
@@ -33,17 +34,16 @@ struct Client::Impl {
|
||||
}
|
||||
|
||||
/// A generic function handles POST, GET and DELETE request together
|
||||
Common::WebResult GenericRequest(const std::string& method, const std::string& path,
|
||||
const std::string& data, bool allow_anonymous,
|
||||
const std::string& accept) {
|
||||
WebResult GenericRequest(const std::string& method, const std::string& path,
|
||||
const std::string& data, bool allow_anonymous,
|
||||
const std::string& accept) {
|
||||
if (jwt.empty()) {
|
||||
UpdateJWT();
|
||||
}
|
||||
|
||||
if (jwt.empty() && !allow_anonymous) {
|
||||
LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
|
||||
return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
|
||||
"Credentials needed", ""};
|
||||
return WebResult{WebResult::Code::CredentialsMissing, "Credentials needed", ""};
|
||||
}
|
||||
|
||||
auto result = GenericRequest(method, path, data, accept, jwt);
|
||||
@@ -62,10 +62,10 @@ struct Client::Impl {
|
||||
* username + token is used if jwt is empty but username and token are
|
||||
* not empty anonymous if all of jwt, username and token are empty
|
||||
*/
|
||||
Common::WebResult GenericRequest(const std::string& method, const std::string& path,
|
||||
const std::string& data, const std::string& accept,
|
||||
const std::string& jwt = "", const std::string& username = "",
|
||||
const std::string& token = "") {
|
||||
WebResult GenericRequest(const std::string& method, const std::string& path,
|
||||
const std::string& data, const std::string& accept,
|
||||
const std::string& jwt = "", const std::string& username = "",
|
||||
const std::string& token = "") {
|
||||
if (cli == nullptr) {
|
||||
auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
|
||||
int port;
|
||||
@@ -81,12 +81,12 @@ struct Client::Impl {
|
||||
cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port);
|
||||
} else {
|
||||
LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
|
||||
return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme", ""};
|
||||
return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
|
||||
}
|
||||
}
|
||||
if (cli == nullptr) {
|
||||
LOG_ERROR(WebService, "Invalid URL {}", host + path);
|
||||
return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL", ""};
|
||||
return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
|
||||
}
|
||||
cli->set_timeout_sec(TIMEOUT_SECONDS);
|
||||
|
||||
@@ -106,7 +106,7 @@ struct Client::Impl {
|
||||
std::string(API_VERSION.begin(), API_VERSION.end()));
|
||||
if (method != "GET") {
|
||||
params.emplace(std::string("Content-Type"), std::string("application/json"));
|
||||
};
|
||||
}
|
||||
|
||||
httplib::Request request;
|
||||
request.method = method;
|
||||
@@ -118,29 +118,28 @@ struct Client::Impl {
|
||||
|
||||
if (!cli->send(request, response)) {
|
||||
LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
|
||||
return Common::WebResult{Common::WebResult::Code::LibError, "Null response", ""};
|
||||
return WebResult{WebResult::Code::LibError, "Null response", ""};
|
||||
}
|
||||
|
||||
if (response.status >= 400) {
|
||||
LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
|
||||
response.status);
|
||||
return Common::WebResult{Common::WebResult::Code::HttpError,
|
||||
std::to_string(response.status), ""};
|
||||
return WebResult{WebResult::Code::HttpError, std::to_string(response.status), ""};
|
||||
}
|
||||
|
||||
auto content_type = response.headers.find("content-type");
|
||||
|
||||
if (content_type == response.headers.end()) {
|
||||
LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
|
||||
return Common::WebResult{Common::WebResult::Code::WrongContent, "", ""};
|
||||
return WebResult{WebResult::Code::WrongContent, "", ""};
|
||||
}
|
||||
|
||||
if (content_type->second.find(accept) == std::string::npos) {
|
||||
LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
|
||||
content_type->second);
|
||||
return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content", ""};
|
||||
return WebResult{WebResult::Code::WrongContent, "Wrong content", ""};
|
||||
}
|
||||
return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
|
||||
return WebResult{WebResult::Code::Success, "", response.body};
|
||||
}
|
||||
|
||||
// Retrieve a new JWT from given username and token
|
||||
@@ -150,7 +149,7 @@ struct Client::Impl {
|
||||
}
|
||||
|
||||
auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token);
|
||||
if (result.result_code != Common::WebResult::Code::Success) {
|
||||
if (result.result_code != WebResult::Code::Success) {
|
||||
LOG_ERROR(WebService, "UpdateJWT failed");
|
||||
} else {
|
||||
std::lock_guard lock{jwt_cache.mutex};
|
||||
@@ -180,29 +179,28 @@ Client::Client(std::string host, std::string username, std::string token)
|
||||
|
||||
Client::~Client() = default;
|
||||
|
||||
Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous) {
|
||||
WebResult Client::PostJson(const std::string& path, const std::string& data, bool allow_anonymous) {
|
||||
return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
|
||||
WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
|
||||
return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json");
|
||||
}
|
||||
|
||||
Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous) {
|
||||
WebResult Client::DeleteJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous) {
|
||||
return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
|
||||
WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
|
||||
return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
|
||||
WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
|
||||
return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png");
|
||||
}
|
||||
|
||||
Common::WebResult Client::GetExternalJWT(const std::string& audience) {
|
||||
WebResult Client::GetExternalJWT(const std::string& audience) {
|
||||
return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false,
|
||||
"text/html");
|
||||
}
|
||||
|
||||
@@ -7,12 +7,10 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
struct WebResult;
|
||||
}
|
||||
|
||||
namespace WebService {
|
||||
|
||||
struct WebResult;
|
||||
|
||||
class Client {
|
||||
public:
|
||||
Client(std::string host, std::string username, std::string token);
|
||||
@@ -25,8 +23,7 @@ public:
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult PostJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous);
|
||||
WebResult PostJson(const std::string& path, const std::string& data, bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Gets JSON from the specified path.
|
||||
@@ -34,7 +31,7 @@ public:
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
|
||||
WebResult GetJson(const std::string& path, bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Deletes JSON to the specified path.
|
||||
@@ -43,8 +40,7 @@ public:
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult DeleteJson(const std::string& path, const std::string& data,
|
||||
bool allow_anonymous);
|
||||
WebResult DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Gets a plain string from the specified path.
|
||||
@@ -52,7 +48,7 @@ public:
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult GetPlain(const std::string& path, bool allow_anonymous);
|
||||
WebResult GetPlain(const std::string& path, bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Gets an PNG image from the specified path.
|
||||
@@ -60,14 +56,14 @@ public:
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult GetImage(const std::string& path, bool allow_anonymous);
|
||||
WebResult GetImage(const std::string& path, bool allow_anonymous);
|
||||
|
||||
/**
|
||||
* Requests an external JWT for the specific audience provided.
|
||||
* @param audience the audience of the JWT requested.
|
||||
* @return the result of the request.
|
||||
*/
|
||||
Common::WebResult GetExternalJWT(const std::string& audience);
|
||||
WebResult GetExternalJWT(const std::string& audience);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
namespace WebService {
|
||||
struct WebResult {
|
||||
enum class Code : u32 {
|
||||
Success,
|
||||
@@ -22,4 +22,4 @@ struct WebResult {
|
||||
std::string result_string;
|
||||
std::string returned_data;
|
||||
};
|
||||
} // namespace Common
|
||||
} // namespace WebService
|
||||
@@ -54,7 +54,8 @@ void CompatDB::Submit() {
|
||||
back();
|
||||
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
|
||||
Core::System::GetInstance().TelemetrySession().AddField(
|
||||
Telemetry::FieldType::UserFeedback, "Compatibility", compatibility->checkedId());
|
||||
Common::Telemetry::FieldType::UserFeedback, "Compatibility",
|
||||
compatibility->checkedId());
|
||||
|
||||
button(NextButton)->setEnabled(false);
|
||||
button(NextButton)->setText(tr("Submitting"));
|
||||
|
||||
@@ -635,6 +635,11 @@ void Config::ReadCpuValues() {
|
||||
ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
|
||||
Settings::values.cpuopt_reduce_misalign_checks =
|
||||
ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
|
||||
|
||||
Settings::values.cpuopt_unsafe_unfuse_fma =
|
||||
ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();
|
||||
Settings::values.cpuopt_unsafe_reduce_fp_error =
|
||||
ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool();
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -1132,6 +1137,11 @@ void Config::SaveCpuValues() {
|
||||
WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
|
||||
Settings::values.cpuopt_reduce_misalign_checks, true);
|
||||
|
||||
WriteSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"),
|
||||
Settings::values.cpuopt_unsafe_unfuse_fma, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
|
||||
Settings::values.cpuopt_unsafe_reduce_fp_error, true);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
@@ -72,18 +72,18 @@ void ConfigurationShared::SetPerGameSetting(
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetHighlight(QWidget* widget, const std::string& name, bool highlighted) {
|
||||
void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
|
||||
if (highlighted) {
|
||||
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
|
||||
.arg(QString::fromStdString(name)));
|
||||
.arg(widget->objectName()));
|
||||
} else {
|
||||
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
|
||||
.arg(QString::fromStdString(name)));
|
||||
.arg(widget->objectName()));
|
||||
}
|
||||
widget->show();
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name,
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
|
||||
const Settings::Setting<bool>& setting,
|
||||
CheckState& tracker) {
|
||||
if (setting.UsingGlobal()) {
|
||||
@@ -91,45 +91,39 @@ void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::str
|
||||
} else {
|
||||
tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
|
||||
}
|
||||
SetHighlight(checkbox, name, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox,
|
||||
[checkbox, name, setting, &tracker]() {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(setting.GetValue(true));
|
||||
}
|
||||
SetHighlight(checkbox, name, tracker != CheckState::Global);
|
||||
});
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(setting.GetValue(true));
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name,
|
||||
bool global, bool state, bool global_state,
|
||||
CheckState& tracker) {
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
|
||||
bool global_state, CheckState& tracker) {
|
||||
if (global) {
|
||||
tracker = CheckState::Global;
|
||||
} else {
|
||||
tracker = (state == global_state) ? CheckState::On : CheckState::Off;
|
||||
}
|
||||
SetHighlight(checkbox, name, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox,
|
||||
[checkbox, name, global_state, &tracker]() {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(global_state);
|
||||
}
|
||||
SetHighlight(checkbox, name, tracker != CheckState::Global);
|
||||
});
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(global_state);
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target,
|
||||
const std::string& target_name, int global) {
|
||||
void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
|
||||
InsertGlobalItem(combobox, global);
|
||||
QObject::connect(combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), target,
|
||||
[target, target_name](int index) {
|
||||
ConfigurationShared::SetHighlight(target, target_name, index != 0);
|
||||
});
|
||||
QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
|
||||
[target](int index) { SetHighlight(target, index != 0); });
|
||||
}
|
||||
|
||||
void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
|
||||
|
||||
@@ -39,13 +39,12 @@ void SetPerGameSetting(QComboBox* combobox,
|
||||
void SetPerGameSetting(QComboBox* combobox,
|
||||
const Settings::Setting<Settings::GPUAccuracy>* setting);
|
||||
|
||||
void SetHighlight(QWidget* widget, const std::string& name, bool highlighted);
|
||||
void SetColoredTristate(QCheckBox* checkbox, const std::string& name,
|
||||
const Settings::Setting<bool>& setting, CheckState& tracker);
|
||||
void SetColoredTristate(QCheckBox* checkbox, const std::string& name, bool global, bool state,
|
||||
bool global_state, CheckState& tracker);
|
||||
void SetColoredComboBox(QComboBox* combobox, QWidget* target, const std::string& target_name,
|
||||
int global);
|
||||
void SetHighlight(QWidget* widget, bool highlighted);
|
||||
void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting,
|
||||
CheckState& tracker);
|
||||
void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
|
||||
CheckState& tracker);
|
||||
void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
|
||||
|
||||
void InsertGlobalItem(QComboBox* combobox, int global_index);
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ void ConfigureAudio::SetConfiguration() {
|
||||
ui->volume_combo_box->setCurrentIndex(1);
|
||||
ui->volume_slider->setEnabled(true);
|
||||
}
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout",
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout,
|
||||
!Settings::values.volume.UsingGlobal());
|
||||
}
|
||||
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||
@@ -173,14 +173,13 @@ void ConfigureAudio::SetupPerGameUI() {
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching, "toggle_audio_stretching",
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching,
|
||||
Settings::values.enable_audio_stretching,
|
||||
enable_audio_stretching);
|
||||
connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
|
||||
this, [this](int index) {
|
||||
ui->volume_slider->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout", index == 1);
|
||||
});
|
||||
connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
|
||||
ui->volume_slider->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
|
||||
});
|
||||
|
||||
ui->output_sink_combo_box->setVisible(false);
|
||||
ui->output_sink_label->setVisible(false);
|
||||
|
||||
@@ -19,6 +19,8 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config
|
||||
|
||||
connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
|
||||
&ConfigureCpu::AccuracyUpdated);
|
||||
connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureCpu::UpdateGroup);
|
||||
}
|
||||
|
||||
ConfigureCpu::~ConfigureCpu() = default;
|
||||
@@ -28,6 +30,12 @@ void ConfigureCpu::SetConfiguration() {
|
||||
|
||||
ui->accuracy->setEnabled(runtime_lock);
|
||||
ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
|
||||
UpdateGroup(static_cast<int>(Settings::values.cpu_accuracy));
|
||||
|
||||
ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock);
|
||||
ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);
|
||||
ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
|
||||
ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error);
|
||||
}
|
||||
|
||||
void ConfigureCpu::AccuracyUpdated(int index) {
|
||||
@@ -38,14 +46,21 @@ void ConfigureCpu::AccuracyUpdated(int index) {
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (result == QMessageBox::No) {
|
||||
ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
|
||||
return;
|
||||
UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureCpu::UpdateGroup(int index) {
|
||||
ui->unsafe_group->setVisible(static_cast<Settings::CPUAccuracy>(index) ==
|
||||
Settings::CPUAccuracy::Unsafe);
|
||||
}
|
||||
|
||||
void ConfigureCpu::ApplyConfiguration() {
|
||||
Settings::values.cpu_accuracy =
|
||||
static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
|
||||
Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();
|
||||
Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureCpu::changeEvent(QEvent* event) {
|
||||
|
||||
@@ -26,6 +26,7 @@ private:
|
||||
void RetranslateUI();
|
||||
|
||||
void AccuracyUpdated(int index);
|
||||
void UpdateGroup(int index);
|
||||
|
||||
void SetConfiguration();
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
<string>Accurate</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Unsafe</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enable Debug Mode</string>
|
||||
@@ -62,6 +67,53 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="unsafe_group">
|
||||
<property name="title">
|
||||
<string>Unsafe CPU Optimization Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel">
|
||||
<property name="wordWrap">
|
||||
<bool>1</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These settings reduce accuracy for speed.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma">
|
||||
<property name="text">
|
||||
<string>Unfuse FMA (improve performance on CPUs without FMA)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>
|
||||
<div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div>
|
||||
</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error">
|
||||
<property name="text">
|
||||
<string>Faster FRSQRTE and FRECPE</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>
|
||||
<div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div>
|
||||
</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -105,10 +105,10 @@ void ConfigureGeneral::SetupPerGameUI() {
|
||||
ui->toggle_background_pause->setVisible(false);
|
||||
ui->toggle_hide_mouse->setVisible(false);
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit, "toggle_frame_limit",
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit,
|
||||
Settings::values.use_frame_limit, use_frame_limit);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_multi_core, "use_multi_core",
|
||||
Settings::values.use_multi_core, use_multi_core);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
|
||||
use_multi_core);
|
||||
|
||||
connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() {
|
||||
ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() &&
|
||||
|
||||
@@ -34,9 +34,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
|
||||
UpdateDeviceComboBox();
|
||||
if (!Settings::configuring_global) {
|
||||
ConfigurationShared::SetHighlight(ui->api_layout, "api_layout",
|
||||
ui->api->currentIndex() !=
|
||||
ConfigurationShared::USE_GLOBAL_INDEX);
|
||||
ConfigurationShared::SetHighlight(
|
||||
ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
|
||||
}
|
||||
});
|
||||
connect(ui->device, qOverload<int>(&QComboBox::activated), this,
|
||||
@@ -80,17 +79,16 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
|
||||
} else {
|
||||
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
|
||||
ConfigurationShared::SetHighlight(ui->api_layout, "api_layout",
|
||||
ConfigurationShared::SetHighlight(ui->api_layout,
|
||||
!Settings::values.renderer_backend.UsingGlobal());
|
||||
ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
|
||||
&Settings::values.aspect_ratio);
|
||||
|
||||
ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
|
||||
ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->ar_label, "ar_label",
|
||||
ConfigurationShared::SetHighlight(ui->ar_label,
|
||||
!Settings::values.aspect_ratio.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout",
|
||||
!Settings::values.bg_red.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
|
||||
}
|
||||
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
|
||||
@@ -248,20 +246,18 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
return;
|
||||
}
|
||||
|
||||
connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
|
||||
[this](int index) {
|
||||
ui->bg_button->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout", index == 1);
|
||||
});
|
||||
connect(ui->bg_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
|
||||
ui->bg_button->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
|
||||
});
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->use_disk_shader_cache, "use_disk_shader_cache",
|
||||
Settings::values.use_disk_shader_cache,
|
||||
use_disk_shader_cache);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->use_asynchronous_gpu_emulation, "use_asynchronous_gpu_emulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation, use_asynchronous_gpu_emulation);
|
||||
ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
|
||||
Settings::values.use_asynchronous_gpu_emulation,
|
||||
use_asynchronous_gpu_emulation);
|
||||
|
||||
ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, "ar_label",
|
||||
ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
|
||||
Settings::values.aspect_ratio.GetValue(true));
|
||||
ConfigurationShared::InsertGlobalItem(
|
||||
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
|
||||
|
||||
@@ -41,9 +41,9 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
|
||||
ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
|
||||
ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
|
||||
&Settings::values.max_anisotropy);
|
||||
ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, "label_gpu_accuracy",
|
||||
ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
|
||||
!Settings::values.gpu_accuracy.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->af_label, "af_label",
|
||||
ConfigurationShared::SetHighlight(ui->af_label,
|
||||
!Settings::values.max_anisotropy.UsingGlobal());
|
||||
}
|
||||
}
|
||||
@@ -131,20 +131,18 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->use_vsync, "use_vsync", Settings::values.use_vsync,
|
||||
use_vsync);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_assembly_shaders, "use_assembly_shaders",
|
||||
Settings::values.use_assembly_shaders,
|
||||
use_assembly_shaders);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->use_asynchronous_shaders, "use_asynchronous_shaders",
|
||||
Settings::values.use_asynchronous_shaders, use_asynchronous_shaders);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time",
|
||||
ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
|
||||
Settings::values.use_asynchronous_shaders,
|
||||
use_asynchronous_shaders);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
|
||||
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy",
|
||||
ui->gpu_accuracy, ui->label_gpu_accuracy,
|
||||
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->anisotropic_filtering_combobox, ui->af_label, "af_label",
|
||||
ui->anisotropic_filtering_combobox, ui->af_label,
|
||||
static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use asynchronous shader building (experimental, OpenGL or Assembly shaders only)</string>
|
||||
<string>Use asynchronous shader building (experimental)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -154,7 +154,7 @@ void ConfigureHotkeys::ClearAll() {
|
||||
const QStandardItem* parent = model->item(r, 0);
|
||||
|
||||
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
|
||||
model->item(r, 0)->child(r2, 1)->setText(tr(""));
|
||||
model->item(r, 0)->child(r2, 1)->setText(QString{});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
|
||||
model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText));
|
||||
}
|
||||
});
|
||||
connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, tr("")); });
|
||||
connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, QString{}); });
|
||||
|
||||
context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
|
||||
}
|
||||
|
||||
@@ -90,13 +90,13 @@ void ConfigureSystem::SetConfiguration() {
|
||||
&Settings::values.time_zone_index);
|
||||
ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
|
||||
|
||||
ConfigurationShared::SetHighlight(ui->label_language, "label_language",
|
||||
ConfigurationShared::SetHighlight(ui->label_language,
|
||||
!Settings::values.language_index.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->label_region, "label_region",
|
||||
ConfigurationShared::SetHighlight(ui->label_region,
|
||||
!Settings::values.region_index.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->label_timezone, "label_timezone",
|
||||
ConfigurationShared::SetHighlight(ui->label_timezone,
|
||||
!Settings::values.time_zone_index.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->label_sound, "label_sound",
|
||||
ConfigurationShared::SetHighlight(ui->label_sound,
|
||||
!Settings::values.sound_index.UsingGlobal());
|
||||
}
|
||||
}
|
||||
@@ -224,22 +224,20 @@ void ConfigureSystem::SetupPerGameUI() {
|
||||
}
|
||||
|
||||
ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language,
|
||||
"label_language",
|
||||
Settings::values.language_index.GetValue(true));
|
||||
ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region, "label_region",
|
||||
ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region,
|
||||
Settings::values.region_index.GetValue(true));
|
||||
ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone,
|
||||
"label_timezone",
|
||||
Settings::values.time_zone_index.GetValue(true));
|
||||
ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound, "label_sound",
|
||||
ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound,
|
||||
Settings::values.sound_index.GetValue(true));
|
||||
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->rng_seed_checkbox, "rng_seed_checkbox", Settings::values.rng_seed.UsingGlobal(),
|
||||
ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(),
|
||||
Settings::values.rng_seed.GetValue().has_value(),
|
||||
Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->custom_rtc_checkbox, "custom_rtc_checkbox", Settings::values.custom_rtc.UsingGlobal(),
|
||||
ui->custom_rtc_checkbox, Settings::values.custom_rtc.UsingGlobal(),
|
||||
Settings::values.custom_rtc.GetValue().has_value(),
|
||||
Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc);
|
||||
}
|
||||
|
||||
@@ -109,8 +109,7 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
|
||||
MicroProfileSetDisplayMode(1); // Timers screen
|
||||
MicroProfileInitUI();
|
||||
|
||||
connect(&update_timer, &QTimer::timeout, this,
|
||||
static_cast<void (MicroProfileWidget::*)()>(&MicroProfileWidget::update));
|
||||
connect(&update_timer, &QTimer::timeout, this, qOverload<>(&MicroProfileWidget::update));
|
||||
}
|
||||
|
||||
void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
|
||||
|
||||
@@ -406,7 +406,7 @@ bool GameList::isEmpty() const {
|
||||
type == GameListItemType::SysNandDir)) {
|
||||
item_model->invisibleRootItem()->removeRow(child->row());
|
||||
i--;
|
||||
};
|
||||
}
|
||||
}
|
||||
return !item_model->invisibleRootItem()->hasChildren();
|
||||
}
|
||||
@@ -502,10 +502,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
|
||||
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
|
||||
|
||||
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
|
||||
emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
|
||||
});
|
||||
connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
|
||||
emit OpenFolderRequested(GameListOpenTarget::ModData, path);
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
|
||||
});
|
||||
connect(open_transferable_shader_cache, &QAction::triggered,
|
||||
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
|
||||
|
||||
@@ -84,7 +84,8 @@ public:
|
||||
signals:
|
||||
void GameChosen(QString game_path);
|
||||
void ShouldCancelWorker();
|
||||
void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
|
||||
void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
|
||||
const std::string& game_path);
|
||||
void OpenTransferableShaderCacheRequested(u64 program_id);
|
||||
void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
|
||||
void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
|
||||
|
||||
@@ -374,7 +374,7 @@ void GameListWorker::run() {
|
||||
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
|
||||
game_dir.deep_scan ? 256 : 0, game_list_dir);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
emit Finished(watch_list);
|
||||
}
|
||||
|
||||
@@ -894,6 +894,8 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
|
||||
connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
|
||||
connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
|
||||
connect(ui.action_Configure_Current_Game, &QAction::triggered, this,
|
||||
&GMainWindow::OnConfigurePerGame);
|
||||
|
||||
// View
|
||||
connect(ui.action_Single_Window_Mode, &QAction::triggered, this,
|
||||
@@ -1039,7 +1041,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
}
|
||||
game_path = filename;
|
||||
|
||||
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "Qt");
|
||||
system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1167,6 +1169,7 @@ void GMainWindow::ShutdownGame() {
|
||||
ui.action_Pause->setEnabled(false);
|
||||
ui.action_Stop->setEnabled(false);
|
||||
ui.action_Restart->setEnabled(false);
|
||||
ui.action_Configure_Current_Game->setEnabled(false);
|
||||
ui.action_Report_Compatibility->setEnabled(false);
|
||||
ui.action_Load_Amiibo->setEnabled(false);
|
||||
ui.action_Capture_Screenshot->setEnabled(false);
|
||||
@@ -1239,20 +1242,29 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
|
||||
BootGame(game_path);
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) {
|
||||
void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
|
||||
const std::string& game_path) {
|
||||
std::string path;
|
||||
QString open_target;
|
||||
|
||||
const auto v_file = Core::GetGameFileFromPath(vfs, game_path);
|
||||
const auto loader = Loader::GetLoader(v_file);
|
||||
FileSys::NACP control{};
|
||||
u64 program_id{};
|
||||
const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] {
|
||||
FileSys::PatchManager pm{program_id};
|
||||
const auto control = pm.GetControlMetadata().first;
|
||||
if (control != nullptr) {
|
||||
return std::make_pair(control->GetDefaultNormalSaveSize(),
|
||||
control->GetDeviceSaveDataSize());
|
||||
} else {
|
||||
const auto file = Core::GetGameFileFromPath(vfs, game_path);
|
||||
const auto loader = Loader::GetLoader(file);
|
||||
|
||||
loader->ReadControlData(control);
|
||||
loader->ReadProgramId(program_id);
|
||||
FileSys::NACP nacp{};
|
||||
loader->ReadControlData(nacp);
|
||||
return std::make_pair(nacp.GetDefaultNormalSaveSize(), nacp.GetDeviceSaveDataSize());
|
||||
}
|
||||
}();
|
||||
|
||||
const bool has_user_save{control.GetDefaultNormalSaveSize() > 0};
|
||||
const bool has_device_save{control.GetDeviceSaveDataSize() > 0};
|
||||
const bool has_user_save{user_save_size > 0};
|
||||
const bool has_device_save{device_save_size > 0};
|
||||
|
||||
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
|
||||
|
||||
@@ -1720,26 +1732,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurePerGame dialog(this, title_id);
|
||||
dialog.LoadFromFile(v_file);
|
||||
auto result = dialog.exec();
|
||||
if (result == QDialog::Accepted) {
|
||||
dialog.ApplyConfiguration();
|
||||
|
||||
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
|
||||
if (reload) {
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
}
|
||||
|
||||
// Do not cause the global config to write local settings into the config file
|
||||
Settings::RestoreGlobalState();
|
||||
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
config->Save();
|
||||
}
|
||||
} else {
|
||||
Settings::RestoreGlobalState();
|
||||
}
|
||||
OpenPerGameConfiguration(title_id, file);
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuLoadFile() {
|
||||
@@ -2068,6 +2061,7 @@ void GMainWindow::OnStartGame() {
|
||||
ui.action_Pause->setEnabled(true);
|
||||
ui.action_Stop->setEnabled(true);
|
||||
ui.action_Restart->setEnabled(true);
|
||||
ui.action_Configure_Current_Game->setEnabled(true);
|
||||
ui.action_Report_Compatibility->setEnabled(true);
|
||||
|
||||
discord_rpc->Update();
|
||||
@@ -2257,6 +2251,36 @@ void GMainWindow::OnConfigure() {
|
||||
UpdateStatusButtons();
|
||||
}
|
||||
|
||||
void GMainWindow::OnConfigurePerGame() {
|
||||
const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
OpenPerGameConfiguration(title_id, game_path.toStdString());
|
||||
}
|
||||
|
||||
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
|
||||
const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
|
||||
|
||||
ConfigurePerGame dialog(this, title_id);
|
||||
dialog.LoadFromFile(v_file);
|
||||
auto result = dialog.exec();
|
||||
if (result == QDialog::Accepted) {
|
||||
dialog.ApplyConfiguration();
|
||||
|
||||
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
|
||||
if (reload) {
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
}
|
||||
|
||||
// Do not cause the global config to write local settings into the config file
|
||||
Settings::RestoreGlobalState();
|
||||
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
config->Save();
|
||||
}
|
||||
} else {
|
||||
Settings::RestoreGlobalState();
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadAmiibo() {
|
||||
const QString extensions{QStringLiteral("*.bin")};
|
||||
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
|
||||
|
||||
@@ -198,7 +198,8 @@ private slots:
|
||||
void OnOpenFAQ();
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
|
||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
|
||||
const std::string& game_path);
|
||||
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
||||
void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
|
||||
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
|
||||
@@ -216,6 +217,7 @@ private slots:
|
||||
void OnMenuInstallToNAND();
|
||||
void OnMenuRecentFile();
|
||||
void OnConfigure();
|
||||
void OnConfigurePerGame();
|
||||
void OnLoadAmiibo();
|
||||
void OnOpenYuzuFolder();
|
||||
void OnAbout();
|
||||
@@ -249,6 +251,7 @@ private:
|
||||
void ShowMouseCursor();
|
||||
void OpenURL(const QUrl& url);
|
||||
void LoadTranslation();
|
||||
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
|
||||
|
||||
Ui::MainWindow ui;
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
<addaction name="action_Restart"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Configure"/>
|
||||
<addaction name="action_Configure_Current_Game"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_View">
|
||||
<property name="title">
|
||||
@@ -287,6 +288,14 @@
|
||||
<string>Capture Screenshot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Configure_Current_Game">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Configure Current Game..</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -29,14 +29,14 @@ extern const Themes themes;
|
||||
|
||||
struct GameDir {
|
||||
QString path;
|
||||
bool deep_scan;
|
||||
bool expanded;
|
||||
bool deep_scan = false;
|
||||
bool expanded = false;
|
||||
bool operator==(const GameDir& rhs) const {
|
||||
return path == rhs.path;
|
||||
};
|
||||
}
|
||||
bool operator!=(const GameDir& rhs) const {
|
||||
return !operator==(rhs);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct Values {
|
||||
|
||||
@@ -229,7 +229,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
|
||||
system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");
|
||||
|
||||
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
|
||||
system.GPU().Start();
|
||||
|
||||
@@ -251,7 +251,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback);
|
||||
|
||||
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
|
||||
system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend",
|
||||
"SDLHideTester");
|
||||
|
||||
system.GPU().Start();
|
||||
system.Renderer().Rasterizer().LoadDiskResources();
|
||||
|
||||
Reference in New Issue
Block a user