mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-06-09 00:37:50 +00:00
Compare commits
14 Commits
f08b27f204
...
93cffebc5d
Author | SHA1 | Date | |
---|---|---|---|
![]() |
93cffebc5d | ||
![]() |
e3aa5f0763 | ||
![]() |
1d0d123447 | ||
![]() |
4138d691ae | ||
![]() |
d9b0fd2805 | ||
![]() |
d78e6fe165 | ||
![]() |
41b0c09486 | ||
![]() |
b4eee80103 | ||
![]() |
6b8533d012 | ||
![]() |
d3fc0c89e4 | ||
![]() |
e3de3ada05 | ||
![]() |
7208b64a94 | ||
![]() |
ca10f2fdf1 | ||
![]() |
013c162cf1 |
@ -483,6 +483,9 @@ add_library(core
|
|||||||
NetworkCaptureLogger.h
|
NetworkCaptureLogger.h
|
||||||
PatchEngine.cpp
|
PatchEngine.cpp
|
||||||
PatchEngine.h
|
PatchEngine.h
|
||||||
|
PerformanceSample.h
|
||||||
|
PerformanceSampleAggregator.cpp
|
||||||
|
PerformanceSampleAggregator.h
|
||||||
PowerPC/BreakPoints.cpp
|
PowerPC/BreakPoints.cpp
|
||||||
PowerPC/BreakPoints.h
|
PowerPC/BreakPoints.h
|
||||||
PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp
|
PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
@ -17,7 +16,6 @@
|
|||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
#include <objc/message.h>
|
#include <objc/message.h>
|
||||||
#elif defined(ANDROID)
|
#elif defined(ANDROID)
|
||||||
#include <functional>
|
|
||||||
#include "Common/AndroidAnalytics.h"
|
#include "Common/AndroidAnalytics.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -28,24 +26,21 @@
|
|||||||
#include "Common/Crypto/SHA1.h"
|
#include "Common/Crypto/SHA1.h"
|
||||||
#include "Common/EnumUtils.h"
|
#include "Common/EnumUtils.h"
|
||||||
#include "Common/Random.h"
|
#include "Common/Random.h"
|
||||||
#include "Common/Timer.h"
|
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
|
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/HW/GCPad.h"
|
#include "Core/HW/GCPad.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
|
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
#include "InputCommon/GCAdapter.h"
|
#include "InputCommon/GCAdapter.h"
|
||||||
#include "InputCommon/InputConfig.h"
|
#include "InputCommon/InputConfig.h"
|
||||||
|
|
||||||
#include "VideoCommon/VideoBackendBase.h"
|
#include "VideoCommon/VideoBackendBase.h"
|
||||||
#include "VideoCommon/VideoConfig.h"
|
#include "VideoCommon/VideoConfig.h"
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
constexpr char ANALYTICS_ENDPOINT[] = "https://analytics.dolphin-emu.org/report";
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#if defined(ANDROID)
|
#if defined(ANDROID)
|
||||||
static std::function<std::string(std::string)> s_get_val_func;
|
static std::function<std::string(std::string)> s_get_val_func;
|
||||||
void DolphinAnalytics::AndroidSetGetValFunc(std::function<std::string(std::string)> func)
|
void DolphinAnalytics::AndroidSetGetValFunc(std::function<std::string(std::string)> func)
|
||||||
@ -54,6 +49,100 @@ void DolphinAnalytics::AndroidSetGetValFunc(std::function<std::string(std::strin
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void AddVersionInformationToReportBuilder(Common::AnalyticsReportBuilder* builder)
|
||||||
|
{
|
||||||
|
builder->AddData("version-desc", Common::GetScmDescStr());
|
||||||
|
builder->AddData("version-hash", Common::GetScmRevGitStr());
|
||||||
|
builder->AddData("version-branch", Common::GetScmBranchStr());
|
||||||
|
builder->AddData("version-dist", Common::GetScmDistributorStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddAutoUpdateInformationToReportBuilder(Common::AnalyticsReportBuilder* builder)
|
||||||
|
{
|
||||||
|
builder->AddData("update-track", Config::Get(Config::MAIN_AUTOUPDATE_UPDATE_TRACK));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCPUInformationToReportBuilder(Common::AnalyticsReportBuilder* builder)
|
||||||
|
{
|
||||||
|
builder->AddData("cpu-summary", cpu_info.Summarize());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
void AddWindowsInformationToReportBuilder(Common::AnalyticsReportBuilder* builder)
|
||||||
|
{
|
||||||
|
const auto winver = WindowsRegistry::GetOSVersion();
|
||||||
|
builder->AddData("win-ver-major", static_cast<u32>(winver.dwMajorVersion));
|
||||||
|
builder->AddData("win-ver-minor", static_cast<u32>(winver.dwMinorVersion));
|
||||||
|
builder->AddData("win-ver-build", static_cast<u32>(winver.dwBuildNumber));
|
||||||
|
}
|
||||||
|
#elif defined(ANDROID)
|
||||||
|
void AddAndroidInformationToReportBuilder(Common::AnalyticsReportBuilder* builder)
|
||||||
|
{
|
||||||
|
builder->AddData("android-manufacturer", s_get_val_func("DEVICE_MANUFACTURER"));
|
||||||
|
builder->AddData("android-model", s_get_val_func("DEVICE_MODEL"));
|
||||||
|
builder->AddData("android-version", s_get_val_func("DEVICE_OS"));
|
||||||
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
void AddMacOSInformationToReportBuilder(Common::AnalyticsReportBuilder* builder)
|
||||||
|
{
|
||||||
|
// id processInfo = [NSProcessInfo processInfo]
|
||||||
|
id processInfo = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(
|
||||||
|
objc_getClass("NSProcessInfo"), sel_getUid("processInfo"));
|
||||||
|
if (processInfo)
|
||||||
|
{
|
||||||
|
struct OSVersion // NSOperatingSystemVersion
|
||||||
|
{
|
||||||
|
s64 major_version; // NSInteger majorVersion
|
||||||
|
s64 minor_version; // NSInteger minorVersion
|
||||||
|
s64 patch_version; // NSInteger patchVersion
|
||||||
|
};
|
||||||
|
// On x86_64, we need to explicitly call objc_msgSend_stret for a struct.
|
||||||
|
#ifdef _M_ARM_64
|
||||||
|
#define msgSend objc_msgSend
|
||||||
|
#else
|
||||||
|
#define msgSend objc_msgSend_stret
|
||||||
|
#endif
|
||||||
|
// NSOperatingSystemVersion version = [processInfo operatingSystemVersion]
|
||||||
|
OSVersion version = reinterpret_cast<OSVersion (*)(id, SEL)>(msgSend)(
|
||||||
|
processInfo, sel_getUid("operatingSystemVersion"));
|
||||||
|
#undef msgSend
|
||||||
|
builder->AddData("osx-ver-major", version.major_version);
|
||||||
|
builder->AddData("osx-ver-minor", version.minor_version);
|
||||||
|
builder->AddData("osx-ver-bugfix", version.patch_version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void AddPlatformInformationToReportBuilder(Common::AnalyticsReportBuilder* builder)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
builder->AddData("os-type", "windows");
|
||||||
|
AddWindowsInformationToReportBuilder(builder);
|
||||||
|
#elif defined(ANDROID)
|
||||||
|
builder->AddData("os-type", "android");
|
||||||
|
AddAndroidInformationToReportBuilder(builder);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
builder->AddData("os-type", "osx");
|
||||||
|
AddMacOSInformationToReportBuilder(builder);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
builder->AddData("os-type", "linux");
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
builder->AddData("os-type", "freebsd");
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
builder->AddData("os-type", "openbsd");
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
builder->AddData("os-type", "netbsd");
|
||||||
|
#elif defined(__HAIKU__)
|
||||||
|
builder->AddData("os-type", "haiku");
|
||||||
|
#else
|
||||||
|
builder->AddData("os-type", "unknown");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Under arm64, we need to call objc_msgSend to receive a struct.
|
||||||
DolphinAnalytics::DolphinAnalytics()
|
DolphinAnalytics::DolphinAnalytics()
|
||||||
{
|
{
|
||||||
ReloadConfig();
|
ReloadConfig();
|
||||||
@ -68,16 +157,17 @@ DolphinAnalytics& DolphinAnalytics::Instance()
|
|||||||
|
|
||||||
void DolphinAnalytics::ReloadConfig()
|
void DolphinAnalytics::ReloadConfig()
|
||||||
{
|
{
|
||||||
std::lock_guard lk{m_reporter_mutex};
|
const std::lock_guard lk{m_reporter_mutex};
|
||||||
|
|
||||||
// Install the HTTP backend if analytics support is enabled.
|
// Install the HTTP backend if analytics support is enabled.
|
||||||
std::unique_ptr<Common::AnalyticsReportingBackend> new_backend;
|
std::unique_ptr<Common::AnalyticsReportingBackend> new_backend;
|
||||||
if (Config::Get(Config::MAIN_ANALYTICS_ENABLED))
|
if (Config::Get(Config::MAIN_ANALYTICS_ENABLED))
|
||||||
{
|
{
|
||||||
|
constexpr char analytics_endpoint[] = "https://analytics.dolphin-emu.org/report";
|
||||||
#if defined(ANDROID)
|
#if defined(ANDROID)
|
||||||
new_backend = std::make_unique<Common::AndroidAnalyticsBackend>(ANALYTICS_ENDPOINT);
|
new_backend = std::make_unique<Common::AndroidAnalyticsBackend>(analytics_endpoint);
|
||||||
#else
|
#else
|
||||||
new_backend = std::make_unique<Common::HttpAnalyticsBackend>(ANALYTICS_ENDPOINT);
|
new_backend = std::make_unique<Common::HttpAnalyticsBackend>(analytics_endpoint);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
m_reporter.SetBackend(std::move(new_backend));
|
m_reporter.SetBackend(std::move(new_backend));
|
||||||
@ -134,7 +224,7 @@ void DolphinAnalytics::ReportGameStart()
|
|||||||
|
|
||||||
// Reset per-game state.
|
// Reset per-game state.
|
||||||
m_reported_quirks.fill(false);
|
m_reported_quirks.fill(false);
|
||||||
InitializePerformanceSampling();
|
m_sample_aggregator.InitializePerformanceSampling();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep in sync with enum class GameQuirk definition.
|
// Keep in sync with enum class GameQuirk definition.
|
||||||
@ -173,12 +263,12 @@ constexpr std::array GAME_QUIRKS_NAMES{
|
|||||||
"invalid-texture-coordinate-component-format",
|
"invalid-texture-coordinate-component-format",
|
||||||
"invalid-color-component-format",
|
"invalid-color-component-format",
|
||||||
};
|
};
|
||||||
static_assert(GAME_QUIRKS_NAMES.size() == static_cast<u32>(GameQuirk::COUNT),
|
static_assert(GAME_QUIRKS_NAMES.size() == static_cast<u32>(GameQuirk::Count),
|
||||||
"Game quirks names and enum definition are out of sync.");
|
"Game quirks names and enum definition are out of sync.");
|
||||||
|
|
||||||
void DolphinAnalytics::ReportGameQuirk(GameQuirk quirk)
|
void DolphinAnalytics::ReportGameQuirk(GameQuirk quirk)
|
||||||
{
|
{
|
||||||
u32 quirk_idx = static_cast<u32>(quirk);
|
const u32 quirk_idx = static_cast<u32>(quirk);
|
||||||
|
|
||||||
// Only report once per run.
|
// Only report once per run.
|
||||||
if (m_reported_quirks[quirk_idx])
|
if (m_reported_quirks[quirk_idx])
|
||||||
@ -193,140 +283,34 @@ void DolphinAnalytics::ReportGameQuirk(GameQuirk quirk)
|
|||||||
|
|
||||||
void DolphinAnalytics::ReportPerformanceInfo(PerformanceSample&& sample)
|
void DolphinAnalytics::ReportPerformanceInfo(PerformanceSample&& sample)
|
||||||
{
|
{
|
||||||
if (ShouldStartPerformanceSampling())
|
m_sample_aggregator.AddSampleIfSamplingInProgress(std::move(sample));
|
||||||
|
const std::optional<PerformanceSampleAggregator::CompletedReport> report_optional =
|
||||||
|
m_sample_aggregator.PopReportIfComplete();
|
||||||
|
if (!report_optional)
|
||||||
{
|
{
|
||||||
m_sampling_performance_info = true;
|
return;
|
||||||
}
|
}
|
||||||
|
const PerformanceSampleAggregator::CompletedReport& report = *report_optional;
|
||||||
|
|
||||||
if (m_sampling_performance_info)
|
// The per game builder should already exist -- there is no way we can be reporting performance
|
||||||
{
|
// info without a game start event having been generated.
|
||||||
m_performance_samples.emplace_back(std::move(sample));
|
Common::AnalyticsReportBuilder builder(m_per_game_builder);
|
||||||
}
|
builder.AddData("type", "performance");
|
||||||
|
builder.AddData("speed", report.speed);
|
||||||
|
builder.AddData("prims", report.primitives);
|
||||||
|
builder.AddData("draw-calls", report.draw_calls);
|
||||||
|
|
||||||
if (m_performance_samples.size() >= NUM_PERFORMANCE_SAMPLES_PER_REPORT)
|
Send(builder);
|
||||||
{
|
|
||||||
std::vector<u32> speed_times_1000(m_performance_samples.size());
|
|
||||||
std::vector<u32> num_prims(m_performance_samples.size());
|
|
||||||
std::vector<u32> num_draw_calls(m_performance_samples.size());
|
|
||||||
for (size_t i = 0; i < m_performance_samples.size(); ++i)
|
|
||||||
{
|
|
||||||
speed_times_1000[i] = static_cast<u32>(m_performance_samples[i].speed_ratio * 1000);
|
|
||||||
num_prims[i] = m_performance_samples[i].num_prims;
|
|
||||||
num_draw_calls[i] = m_performance_samples[i].num_draw_calls;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The per game builder should already exist -- there is no way we can be reporting performance
|
|
||||||
// info without a game start event having been generated.
|
|
||||||
Common::AnalyticsReportBuilder builder(m_per_game_builder);
|
|
||||||
builder.AddData("type", "performance");
|
|
||||||
builder.AddData("speed", speed_times_1000);
|
|
||||||
builder.AddData("prims", num_prims);
|
|
||||||
builder.AddData("draw-calls", num_draw_calls);
|
|
||||||
|
|
||||||
Send(builder);
|
|
||||||
|
|
||||||
// Clear up and stop sampling until next time ShouldStartPerformanceSampling() says so.
|
|
||||||
m_performance_samples.clear();
|
|
||||||
m_sampling_performance_info = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DolphinAnalytics::InitializePerformanceSampling()
|
|
||||||
{
|
|
||||||
m_performance_samples.clear();
|
|
||||||
m_sampling_performance_info = false;
|
|
||||||
|
|
||||||
u64 wait_us =
|
|
||||||
PERFORMANCE_SAMPLING_INITIAL_WAIT_TIME_SECS * 1000000 +
|
|
||||||
Common::Random::GenerateValue<u64>() % (PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS * 1000000);
|
|
||||||
m_sampling_next_start_us = Common::Timer::NowUs() + wait_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DolphinAnalytics::ShouldStartPerformanceSampling()
|
|
||||||
{
|
|
||||||
if (Common::Timer::NowUs() < m_sampling_next_start_us)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
u64 wait_us =
|
|
||||||
PERFORMANCE_SAMPLING_INTERVAL_SECS * 1000000 +
|
|
||||||
Common::Random::GenerateValue<u64>() % (PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS * 1000000);
|
|
||||||
m_sampling_next_start_us = Common::Timer::NowUs() + wait_us;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DolphinAnalytics::MakeBaseBuilder()
|
void DolphinAnalytics::MakeBaseBuilder()
|
||||||
{
|
{
|
||||||
Common::AnalyticsReportBuilder builder;
|
m_base_builder = Common::AnalyticsReportBuilder();
|
||||||
|
|
||||||
// Version information.
|
AddVersionInformationToReportBuilder(&m_base_builder);
|
||||||
builder.AddData("version-desc", Common::GetScmDescStr());
|
AddAutoUpdateInformationToReportBuilder(&m_base_builder);
|
||||||
builder.AddData("version-hash", Common::GetScmRevGitStr());
|
AddCPUInformationToReportBuilder(&m_base_builder);
|
||||||
builder.AddData("version-branch", Common::GetScmBranchStr());
|
AddPlatformInformationToReportBuilder(&m_base_builder);
|
||||||
builder.AddData("version-dist", Common::GetScmDistributorStr());
|
|
||||||
|
|
||||||
// Auto-Update information.
|
|
||||||
builder.AddData("update-track", Config::Get(Config::MAIN_AUTOUPDATE_UPDATE_TRACK));
|
|
||||||
|
|
||||||
// CPU information.
|
|
||||||
builder.AddData("cpu-summary", cpu_info.Summarize());
|
|
||||||
|
|
||||||
// OS information.
|
|
||||||
#if defined(_WIN32)
|
|
||||||
builder.AddData("os-type", "windows");
|
|
||||||
|
|
||||||
const auto winver = WindowsRegistry::GetOSVersion();
|
|
||||||
builder.AddData("win-ver-major", static_cast<u32>(winver.dwMajorVersion));
|
|
||||||
builder.AddData("win-ver-minor", static_cast<u32>(winver.dwMinorVersion));
|
|
||||||
builder.AddData("win-ver-build", static_cast<u32>(winver.dwBuildNumber));
|
|
||||||
#elif defined(ANDROID)
|
|
||||||
builder.AddData("os-type", "android");
|
|
||||||
builder.AddData("android-manufacturer", s_get_val_func("DEVICE_MANUFACTURER"));
|
|
||||||
builder.AddData("android-model", s_get_val_func("DEVICE_MODEL"));
|
|
||||||
builder.AddData("android-version", s_get_val_func("DEVICE_OS"));
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
builder.AddData("os-type", "osx");
|
|
||||||
|
|
||||||
// id processInfo = [NSProcessInfo processInfo]
|
|
||||||
id processInfo = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(
|
|
||||||
objc_getClass("NSProcessInfo"), sel_getUid("processInfo"));
|
|
||||||
if (processInfo)
|
|
||||||
{
|
|
||||||
struct OSVersion // NSOperatingSystemVersion
|
|
||||||
{
|
|
||||||
s64 major_version; // NSInteger majorVersion
|
|
||||||
s64 minor_version; // NSInteger minorVersion
|
|
||||||
s64 patch_version; // NSInteger patchVersion
|
|
||||||
};
|
|
||||||
// Under arm64, we need to call objc_msgSend to receive a struct.
|
|
||||||
// On x86_64, we need to explicitly call objc_msgSend_stret for a struct.
|
|
||||||
#ifdef _M_ARM_64
|
|
||||||
#define msgSend objc_msgSend
|
|
||||||
#else
|
|
||||||
#define msgSend objc_msgSend_stret
|
|
||||||
#endif
|
|
||||||
// NSOperatingSystemVersion version = [processInfo operatingSystemVersion]
|
|
||||||
OSVersion version = reinterpret_cast<OSVersion (*)(id, SEL)>(msgSend)(
|
|
||||||
processInfo, sel_getUid("operatingSystemVersion"));
|
|
||||||
#undef msgSend
|
|
||||||
builder.AddData("osx-ver-major", version.major_version);
|
|
||||||
builder.AddData("osx-ver-minor", version.minor_version);
|
|
||||||
builder.AddData("osx-ver-bugfix", version.patch_version);
|
|
||||||
}
|
|
||||||
#elif defined(__linux__)
|
|
||||||
builder.AddData("os-type", "linux");
|
|
||||||
#elif defined(__FreeBSD__)
|
|
||||||
builder.AddData("os-type", "freebsd");
|
|
||||||
#elif defined(__OpenBSD__)
|
|
||||||
builder.AddData("os-type", "openbsd");
|
|
||||||
#elif defined(__NetBSD__)
|
|
||||||
builder.AddData("os-type", "netbsd");
|
|
||||||
#elif defined(__HAIKU__)
|
|
||||||
builder.AddData("os-type", "haiku");
|
|
||||||
#else
|
|
||||||
builder.AddData("os-type", "unknown");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_base_builder = builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* GetShaderCompilationMode(const VideoConfig& video_config)
|
static const char* GetShaderCompilationMode(const VideoConfig& video_config)
|
||||||
@ -348,12 +332,13 @@ static const char* GetShaderCompilationMode(const VideoConfig& video_config)
|
|||||||
void DolphinAnalytics::MakePerGameBuilder()
|
void DolphinAnalytics::MakePerGameBuilder()
|
||||||
{
|
{
|
||||||
Common::AnalyticsReportBuilder builder(m_base_builder);
|
Common::AnalyticsReportBuilder builder(m_base_builder);
|
||||||
|
const SConfig& config = SConfig::GetInstance();
|
||||||
|
|
||||||
// Gameid.
|
// Gameid.
|
||||||
builder.AddData("gameid", SConfig::GetInstance().GetGameID());
|
builder.AddData("gameid", config.GetGameID());
|
||||||
|
|
||||||
// Unique id bound to the gameid.
|
// Unique id bound to the gameid.
|
||||||
builder.AddData("id", MakeUniqueId(SConfig::GetInstance().GetGameID()));
|
builder.AddData("id", MakeUniqueId(config.GetGameID()));
|
||||||
|
|
||||||
// Configuration.
|
// Configuration.
|
||||||
builder.AddData("cfg-dsp-hle", Config::Get(Config::MAIN_DSP_HLE));
|
builder.AddData("cfg-dsp-hle", Config::Get(Config::MAIN_DSP_HLE));
|
||||||
|
@ -7,10 +7,11 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Common/Analytics.h"
|
#include "Common/Analytics.h"
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
|
#include "Core/PerformanceSample.h"
|
||||||
|
#include "Core/PerformanceSampleAggregator.h"
|
||||||
|
|
||||||
#if defined(ANDROID)
|
#if defined(ANDROID)
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -21,86 +22,86 @@
|
|||||||
enum class GameQuirk
|
enum class GameQuirk
|
||||||
{
|
{
|
||||||
// Several Wii DI commands that are rarely/never used and not implemented by Dolphin
|
// Several Wii DI commands that are rarely/never used and not implemented by Dolphin
|
||||||
USES_DVD_LOW_STOP_LASER,
|
UsesDVDLowStopLaser,
|
||||||
USES_DVD_LOW_OFFSET,
|
UsesDVDLowOffset,
|
||||||
USES_DVD_LOW_READ_DISK_BCA, // NSMBW known to use this
|
UsesDVDLowReadDiskBCA, // NSMBW known to use this
|
||||||
USES_DVD_LOW_REQUEST_DISC_STATUS,
|
UsesDVDLowRequestDiscStatus,
|
||||||
USES_DVD_LOW_REQUEST_RETRY_NUMBER,
|
UsesDVDLowRequestRetryNumber,
|
||||||
USES_DVD_LOW_SER_MEAS_CONTROL,
|
UsesDVDLowSerMeasControl,
|
||||||
|
|
||||||
// Dolphin only implements the simple DVDLowOpenPartition, not any of the variants where some
|
// Dolphin only implements the simple DVDLowOpenPartition, not any of the variants where some
|
||||||
// already-read data is provided
|
// already-read data is provided
|
||||||
USES_DIFFERENT_PARTITION_COMMAND,
|
UsesDifferentPartitionCommand,
|
||||||
|
|
||||||
// IOS has implementations for ioctls 0x85 and 0x89 and a stub for 0x87, but
|
// IOS has implementations for ioctls 0x85 and 0x89 and a stub for 0x87, but
|
||||||
// DVDLowMaskCoverInterrupt/DVDLowUnmaskCoverInterrupt/DVDLowUnmaskStatusInterrupts
|
// DVDLowMaskCoverInterrupt/DVDLowUnmaskCoverInterrupt/DVDLowUnmaskStatusInterrupts
|
||||||
// are all stubbed on the PPC side so they presumably will never be used.
|
// are all stubbed on the PPC side so they presumably will never be used.
|
||||||
// (DVDLowClearCoverInterrupt is used, though)
|
// (DVDLowClearCoverInterrupt is used, though)
|
||||||
USES_DI_INTERRUPT_MASK_COMMAND,
|
UsesDIInterruptMaskCommand,
|
||||||
|
|
||||||
// Some games configure a mismatched number of texture coordinates or colors between the transform
|
// Some games configure a mismatched number of texture coordinates or colors between the transform
|
||||||
// and TEV/BP stages of the rendering pipeline. Currently, Dolphin just skips over these objects
|
// and TEV/BP stages of the rendering pipeline. Currently, Dolphin just skips over these objects
|
||||||
// as the hardware renderers are not equipped to handle the case where the registers between
|
// as the hardware renderers are not equipped to handle the case where the registers between
|
||||||
// stages are mismatched.
|
// stages are mismatched.
|
||||||
MISMATCHED_GPU_TEXGENS_BETWEEN_XF_AND_BP,
|
MismatchedGPUTexgensBetweenXFAndBP,
|
||||||
MISMATCHED_GPU_COLORS_BETWEEN_XF_AND_BP,
|
MismatchedGPUColorsBetweenXFAndBP,
|
||||||
|
|
||||||
// The WD module can be configured to operate in six different modes.
|
// The WD module can be configured to operate in six different modes.
|
||||||
// In practice, only mode 1 (DS communications) and mode 3 (AOSS access point scanning)
|
// In practice, only mode 1 (DS communications) and mode 3 (AOSS access point scanning)
|
||||||
// are used by games and the system menu respectively.
|
// are used by games and the system menu respectively.
|
||||||
USES_UNCOMMON_WD_MODE,
|
UsesUncommonWDMode,
|
||||||
|
|
||||||
USES_WD_UNIMPLEMENTED_IOCTL,
|
UsesWDUnimplementedIoctl,
|
||||||
|
|
||||||
// Some games use invalid/unknown graphics commands (see e.g. bug 10931).
|
// Some games use invalid/unknown graphics commands (see e.g. bug 10931).
|
||||||
// These are different from unknown opcodes: it is known that a BP/CP/XF command is being used,
|
// These are different from unknown opcodes: it is known that a BP/CP/XF command is being used,
|
||||||
// but the command itself is not understood.
|
// but the command itself is not understood.
|
||||||
USES_UNKNOWN_BP_COMMAND,
|
UsesUnknownBPCommand,
|
||||||
USES_UNKNOWN_CP_COMMAND,
|
UsesUnknownCPCommand,
|
||||||
USES_UNKNOWN_XF_COMMAND,
|
UsesUnknownXFCommand,
|
||||||
// YAGCD and Dolphin's implementation disagree about what is valid in some cases
|
// YAGCD and Dolphin's implementation disagree about what is valid in some cases
|
||||||
USES_MAYBE_INVALID_CP_COMMAND,
|
UsesMaybeInvalidCPCommand,
|
||||||
// These commands are used by a few games (e.g. bug 12461), and seem to relate to perf queries.
|
// These commands are used by a few games (e.g. bug 12461), and seem to relate to perf queries.
|
||||||
// Track them separately.
|
// Track them separately.
|
||||||
USES_CP_PERF_COMMAND,
|
UsesCPPerfCommand,
|
||||||
|
|
||||||
// We don't implement all AX features yet.
|
// We don't implement all AX features yet.
|
||||||
USES_UNIMPLEMENTED_AX_COMMAND,
|
UsesUnimplementedAXCommand,
|
||||||
USES_AX_INITIAL_TIME_DELAY,
|
UsesAXInitialTimeDelay,
|
||||||
USES_AX_WIIMOTE_LOWPASS,
|
UsesAXWiimoteLowpass,
|
||||||
USES_AX_WIIMOTE_BIQUAD,
|
UsesAXWiimoteBiquad,
|
||||||
|
|
||||||
// We don't implement XFMEM_CLIPDISABLE yet.
|
// We don't implement XFMEM_CLIPDISABLE yet.
|
||||||
SETS_XF_CLIPDISABLE_BIT_0,
|
SetsXFClipdisableBit0,
|
||||||
SETS_XF_CLIPDISABLE_BIT_1,
|
SetsXFClipdisableBit1,
|
||||||
SETS_XF_CLIPDISABLE_BIT_2,
|
SetsXFClipdisableBit2,
|
||||||
|
|
||||||
// Similar to the XF-BP mismatch, CP and XF might be configured with different vertex formats.
|
// Similar to the XF-BP mismatch, CP and XF might be configured with different vertex formats.
|
||||||
// Real hardware seems to hang in this case, so games probably don't do this, but it would
|
// Real hardware seems to hang in this case, so games probably don't do this, but it would
|
||||||
// be good to know if anything does it.
|
// be good to know if anything does it.
|
||||||
MISMATCHED_GPU_COLORS_BETWEEN_CP_AND_XF,
|
MismatchedGPUColorsBetweenCPAndXF,
|
||||||
MISMATCHED_GPU_NORMALS_BETWEEN_CP_AND_XF,
|
MismatchedGPUNormalsBetweenCPAndXF,
|
||||||
MISMATCHED_GPU_TEX_COORDS_BETWEEN_CP_AND_XF,
|
MismatchedGPUTexCoordsBetweenCPAndXF,
|
||||||
// Both CP and XF have normally-identical matrix index information. We currently always
|
// Both CP and XF have normally-identical matrix index information. We currently always
|
||||||
// use the CP one in the hardware renderers and the XF one in the software renderer,
|
// use the CP one in the hardware renderers and the XF one in the software renderer,
|
||||||
// but testing is needed to find out which of these is actually used for what.
|
// but testing is needed to find out which of these is actually used for what.
|
||||||
MISMATCHED_GPU_MATRIX_INDICES_BETWEEN_CP_AND_XF,
|
MismatchedGPUMatrixIndicesBetweenCPAndXF,
|
||||||
|
|
||||||
// Only a few games use the Bounding Box feature. Note that every game initializes the bounding
|
// Only a few games use the Bounding Box feature. Note that every game initializes the bounding
|
||||||
// box registers (using BPMEM_CLEARBBOX1/BPMEM_CLEARBBOX2) on startup, as part of the SDK, but
|
// box registers (using BPMEM_CLEARBBOX1/BPMEM_CLEARBBOX2) on startup, as part of the SDK, but
|
||||||
// only a few read them (from PE_BBOX_LEFT etc.)
|
// only a few read them (from PE_BBOX_LEFT etc.)
|
||||||
READS_BOUNDING_BOX,
|
ReadsBoundingBox,
|
||||||
|
|
||||||
// A few games use invalid vertex component formats, but the two known cases (Fifa Street and
|
// A few games use invalid vertex component formats, but the two known cases (Fifa Street and
|
||||||
// Def Jam: Fight for New York, see https://bugs.dolphin-emu.org/issues/12719) only use invalid
|
// Def Jam: Fight for New York, see https://bugs.dolphin-emu.org/issues/12719) only use invalid
|
||||||
// normal formats and lighting is disabled in those cases, so it doesn't end up mattering.
|
// normal formats and lighting is disabled in those cases, so it doesn't end up mattering.
|
||||||
// It's possible other games use invalid formats, possibly on other vertex components.
|
// It's possible other games use invalid formats, possibly on other vertex components.
|
||||||
INVALID_POSITION_COMPONENT_FORMAT,
|
InvalidPositionComponentFormat,
|
||||||
INVALID_NORMAL_COMPONENT_FORMAT,
|
InvalidNormalComponentFormat,
|
||||||
INVALID_TEXTURE_COORDINATE_COMPONENT_FORMAT,
|
InvalidTextureCoordinateComponentFormat,
|
||||||
INVALID_COLOR_COMPONENT_FORMAT,
|
InvalidColorComponentFormat,
|
||||||
|
|
||||||
COUNT,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DolphinAnalytics
|
class DolphinAnalytics
|
||||||
@ -134,14 +135,12 @@ public:
|
|||||||
// Get the base builder for building a report
|
// Get the base builder for building a report
|
||||||
const Common::AnalyticsReportBuilder& BaseBuilder() const { return m_base_builder; }
|
const Common::AnalyticsReportBuilder& BaseBuilder() const { return m_base_builder; }
|
||||||
|
|
||||||
struct PerformanceSample
|
|
||||||
{
|
|
||||||
double speed_ratio; // See SystemTimers::GetEstimatedEmulationPerformance().
|
|
||||||
int num_prims;
|
|
||||||
int num_draw_calls;
|
|
||||||
};
|
|
||||||
// Reports performance information. This method performs its own throttling / aggregation --
|
// Reports performance information. This method performs its own throttling / aggregation --
|
||||||
// calling it does not guarantee when a report will actually be sent.
|
// calling it does not guarantee when a report will actually be sent.
|
||||||
|
// Performance reports are generated using data from 100 consecutive frames.
|
||||||
|
// Report starting times are randomized to obtain a wider range of sample data.
|
||||||
|
// The first report begins 5-8 minutes after a game is launched.
|
||||||
|
// Successive reports begin 30-33 minutes after the previous report finishes.
|
||||||
//
|
//
|
||||||
// This method is NOT thread-safe.
|
// This method is NOT thread-safe.
|
||||||
void ReportPerformanceInfo(PerformanceSample&& sample);
|
void ReportPerformanceInfo(PerformanceSample&& sample);
|
||||||
@ -169,24 +168,10 @@ private:
|
|||||||
// values created by MakeUniqueId.
|
// values created by MakeUniqueId.
|
||||||
std::string m_unique_id;
|
std::string m_unique_id;
|
||||||
|
|
||||||
// Performance sampling configuration constants.
|
PerformanceSampleAggregator m_sample_aggregator;
|
||||||
//
|
|
||||||
// 5min after startup + rand(0, 3min) jitter time, collect performance for 100 frames in a row.
|
|
||||||
// Repeat collection after 30min + rand(0, 3min).
|
|
||||||
static constexpr int NUM_PERFORMANCE_SAMPLES_PER_REPORT = 100;
|
|
||||||
static constexpr int PERFORMANCE_SAMPLING_INITIAL_WAIT_TIME_SECS = 300;
|
|
||||||
static constexpr int PERFORMANCE_SAMPLING_WAIT_TIME_JITTER_SECS = 180;
|
|
||||||
static constexpr int PERFORMANCE_SAMPLING_INTERVAL_SECS = 1800;
|
|
||||||
|
|
||||||
// Performance sampling state & internal helpers.
|
|
||||||
void InitializePerformanceSampling(); // Called on game start / title switch.
|
|
||||||
bool ShouldStartPerformanceSampling();
|
|
||||||
u64 m_sampling_next_start_us; // Next timestamp (in us) at which to trigger sampling.
|
|
||||||
bool m_sampling_performance_info = false; // Whether we are currently collecting samples.
|
|
||||||
std::vector<PerformanceSample> m_performance_samples;
|
|
||||||
|
|
||||||
// What quirks have already been reported about the current game.
|
// What quirks have already been reported about the current game.
|
||||||
std::array<bool, static_cast<size_t>(GameQuirk::COUNT)> m_reported_quirks;
|
std::array<bool, static_cast<size_t>(GameQuirk::Count)> m_reported_quirks;
|
||||||
|
|
||||||
// Builder that contains all non variable data that should be sent with all
|
// Builder that contains all non variable data that should be sent with all
|
||||||
// reports.
|
// reports.
|
||||||
|
@ -180,7 +180,7 @@ void AXUCode::HandleCommandList()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_UNK_08:
|
case CMD_UNK_08:
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNIMPLEMENTED_AX_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesUnimplementedAXCommand);
|
||||||
curr_idx += 10;
|
curr_idx += 10;
|
||||||
break; // TODO: check
|
break; // TODO: check
|
||||||
|
|
||||||
|
@ -536,7 +536,7 @@ void ProcessVoice(HLEAccelerator* accelerator, PB_TYPE& pb, const AXBuffers& buf
|
|||||||
if (pb.initial_time_delay.on)
|
if (pb.initial_time_delay.on)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_AX_INITIAL_TIME_DELAY);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesAXInitialTimeDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AX_WII
|
#ifdef AX_WII
|
||||||
@ -548,12 +548,12 @@ void ProcessVoice(HLEAccelerator* accelerator, PB_TYPE& pb, const AXBuffers& buf
|
|||||||
// Only one filter at most for Wiimotes.
|
// Only one filter at most for Wiimotes.
|
||||||
if (pb.remote_iir.on == 2)
|
if (pb.remote_iir.on == 2)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_AX_WIIMOTE_BIQUAD);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesAXWiimoteBiquad);
|
||||||
BiquadFilter(samples, count, pb.remote_iir.biquad);
|
BiquadFilter(samples, count, pb.remote_iir.biquad);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_AX_WIIMOTE_LOWPASS);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesAXWiimoteLowpass);
|
||||||
LowPassFilter(samples, count, pb.remote_iir.lpf);
|
LowPassFilter(samples, count, pb.remote_iir.lpf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,14 +885,14 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
|
|||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::StopLaser:
|
case DICommand::StopLaser:
|
||||||
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowStopLaser");
|
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowStopLaser");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_STOP_LASER);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDVDLowStopLaser);
|
||||||
SetDriveError(DriveError::InvalidCommand);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::Offset:
|
case DICommand::Offset:
|
||||||
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowOffset");
|
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowOffset");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_OFFSET);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDVDLowOffset);
|
||||||
SetDriveError(DriveError::InvalidCommand);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
@ -900,7 +900,7 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
|
|||||||
case DICommand::ReadBCA:
|
case DICommand::ReadBCA:
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(DVDINTERFACE, "DVDLowReadDiskBca - supplying dummy data to appease NSMBW");
|
WARN_LOG_FMT(DVDINTERFACE, "DVDLowReadDiskBca - supplying dummy data to appease NSMBW");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_READ_DISK_BCA);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDVDLowReadDiskBCA);
|
||||||
// NSMBW checks that the first 0x33 bytes of the BCA are 0, then it expects a 1.
|
// NSMBW checks that the first 0x33 bytes of the BCA are 0, then it expects a 1.
|
||||||
// Most (all?) other games have 0x34 0's at the start of the BCA, but don't actually
|
// Most (all?) other games have 0x34 0's at the start of the BCA, but don't actually
|
||||||
// read it. NSMBW doesn't care about the other 12 bytes (which contain manufacturing data?)
|
// read it. NSMBW doesn't care about the other 12 bytes (which contain manufacturing data?)
|
||||||
@ -915,14 +915,14 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
|
|||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::RequestDiscStatus:
|
case DICommand::RequestDiscStatus:
|
||||||
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowRequestDiscStatus");
|
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowRequestDiscStatus");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_DISC_STATUS);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDVDLowRequestDiscStatus);
|
||||||
SetDriveError(DriveError::InvalidCommand);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::RequestRetryNumber:
|
case DICommand::RequestRetryNumber:
|
||||||
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowRequestRetryNumber");
|
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowRequestRetryNumber");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_RETRY_NUMBER);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDVDLowRequestRetryNumber);
|
||||||
SetDriveError(DriveError::InvalidCommand);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
@ -935,7 +935,7 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
|
|||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::SerMeasControl:
|
case DICommand::SerMeasControl:
|
||||||
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowSerMeasControl");
|
ERROR_LOG_FMT(DVDINTERFACE, "DVDLowSerMeasControl");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_SER_MEAS_CONTROL);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDVDLowSerMeasControl);
|
||||||
SetDriveError(DriveError::InvalidCommand);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
|
@ -261,7 +261,7 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
|||||||
case DIIoctl::DVDLowMaskCoverInterrupt:
|
case DIIoctl::DVDLowMaskCoverInterrupt:
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowMaskCoverInterrupt");
|
INFO_LOG_FMT(IOS_DI, "DVDLowMaskCoverInterrupt");
|
||||||
system.GetDVDInterface().SetInterruptEnabled(DVD::DIInterruptType::CVRINT, false);
|
system.GetDVDInterface().SetInterruptEnabled(DVD::DIInterruptType::CVRINT, false);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DI_INTERRUPT_MASK_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDIInterruptMaskCommand);
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowClearCoverInterrupt:
|
case DIIoctl::DVDLowClearCoverInterrupt:
|
||||||
DEBUG_LOG_FMT(IOS_DI, "DVDLowClearCoverInterrupt");
|
DEBUG_LOG_FMT(IOS_DI, "DVDLowClearCoverInterrupt");
|
||||||
@ -269,7 +269,7 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
|||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowUnmaskStatusInterrupts:
|
case DIIoctl::DVDLowUnmaskStatusInterrupts:
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskStatusInterrupts");
|
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskStatusInterrupts");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DI_INTERRUPT_MASK_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDIInterruptMaskCommand);
|
||||||
// Dummied out
|
// Dummied out
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowGetCoverStatus:
|
case DIIoctl::DVDLowGetCoverStatus:
|
||||||
@ -282,7 +282,7 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
|||||||
case DIIoctl::DVDLowUnmaskCoverInterrupt:
|
case DIIoctl::DVDLowUnmaskCoverInterrupt:
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskCoverInterrupt");
|
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskCoverInterrupt");
|
||||||
system.GetDVDInterface().SetInterruptEnabled(DVD::DIInterruptType::CVRINT, true);
|
system.GetDVDInterface().SetInterruptEnabled(DVD::DIInterruptType::CVRINT, true);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DI_INTERRUPT_MASK_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDIInterruptMaskCommand);
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowReset:
|
case DIIoctl::DVDLowReset:
|
||||||
{
|
{
|
||||||
@ -319,7 +319,7 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
|||||||
}
|
}
|
||||||
case DIIoctl::DVDLowOpenPartition:
|
case DIIoctl::DVDLowOpenPartition:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartition as an ioctl - rejecting");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartition as an ioctl - rejecting");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
return DIResult::SecurityError;
|
return DIResult::SecurityError;
|
||||||
case DIIoctl::DVDLowClosePartition:
|
case DIIoctl::DVDLowClosePartition:
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowClosePartition");
|
INFO_LOG_FMT(IOS_DI, "DVDLowClosePartition");
|
||||||
@ -372,23 +372,23 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
|||||||
// Dolphin as games are unlikely to use them.
|
// Dolphin as games are unlikely to use them.
|
||||||
case DIIoctl::DVDLowGetNoDiscOpenPartitionParams:
|
case DIIoctl::DVDLowGetNoDiscOpenPartitionParams:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscOpenPartitionParams as an ioctl - rejecting");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscOpenPartitionParams as an ioctl - rejecting");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
return DIResult::SecurityError;
|
return DIResult::SecurityError;
|
||||||
case DIIoctl::DVDLowNoDiscOpenPartition:
|
case DIIoctl::DVDLowNoDiscOpenPartition:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowNoDiscOpenPartition as an ioctl - rejecting");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowNoDiscOpenPartition as an ioctl - rejecting");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
return DIResult::SecurityError;
|
return DIResult::SecurityError;
|
||||||
case DIIoctl::DVDLowGetNoDiscBufferSizes:
|
case DIIoctl::DVDLowGetNoDiscBufferSizes:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscBufferSizes as an ioctl - rejecting");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscBufferSizes as an ioctl - rejecting");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
return DIResult::SecurityError;
|
return DIResult::SecurityError;
|
||||||
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicket:
|
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicket:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicket as an ioctl - rejecting");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicket as an ioctl - rejecting");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
return DIResult::SecurityError;
|
return DIResult::SecurityError;
|
||||||
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicketView:
|
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicketView:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicketView as an ioctl - rejecting");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicketView as an ioctl - rejecting");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
return DIResult::SecurityError;
|
return DIResult::SecurityError;
|
||||||
case DIIoctl::DVDLowGetStatusRegister:
|
case DIIoctl::DVDLowGetStatusRegister:
|
||||||
{
|
{
|
||||||
@ -721,14 +721,14 @@ std::optional<IPCReply> DIDevice::IOCtlV(const IOCtlVRequest& request)
|
|||||||
{
|
{
|
||||||
ERROR_LOG_FMT(IOS_DI,
|
ERROR_LOG_FMT(IOS_DI,
|
||||||
"DVDLowOpenPartition with ticket - not implemented, ignoring ticket parameter");
|
"DVDLowOpenPartition with ticket - not implemented, ignoring ticket parameter");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
}
|
}
|
||||||
if (request.in_vectors[2].address != 0)
|
if (request.in_vectors[2].address != 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG_FMT(
|
ERROR_LOG_FMT(
|
||||||
IOS_DI,
|
IOS_DI,
|
||||||
"DVDLowOpenPartition with cert chain - not implemented, ignoring certs parameter");
|
"DVDLowOpenPartition with cert chain - not implemented, ignoring certs parameter");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 partition_offset =
|
const u64 partition_offset =
|
||||||
@ -752,26 +752,26 @@ std::optional<IPCReply> DIDevice::IOCtlV(const IOCtlVRequest& request)
|
|||||||
}
|
}
|
||||||
case DIIoctl::DVDLowGetNoDiscOpenPartitionParams:
|
case DIIoctl::DVDLowGetNoDiscOpenPartitionParams:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscOpenPartitionParams - dummied out");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscOpenPartitionParams - dummied out");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
request.DumpUnknown(system, GetDeviceName(), Common::Log::LogType::IOS_DI);
|
request.DumpUnknown(system, GetDeviceName(), Common::Log::LogType::IOS_DI);
|
||||||
break;
|
break;
|
||||||
case DIIoctl::DVDLowNoDiscOpenPartition:
|
case DIIoctl::DVDLowNoDiscOpenPartition:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowNoDiscOpenPartition - dummied out");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowNoDiscOpenPartition - dummied out");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
request.DumpUnknown(system, GetDeviceName(), Common::Log::LogType::IOS_DI);
|
request.DumpUnknown(system, GetDeviceName(), Common::Log::LogType::IOS_DI);
|
||||||
break;
|
break;
|
||||||
case DIIoctl::DVDLowGetNoDiscBufferSizes:
|
case DIIoctl::DVDLowGetNoDiscBufferSizes:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscBufferSizes - dummied out");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowGetNoDiscBufferSizes - dummied out");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
request.DumpUnknown(system, GetDeviceName(), Common::Log::LogType::IOS_DI);
|
request.DumpUnknown(system, GetDeviceName(), Common::Log::LogType::IOS_DI);
|
||||||
break;
|
break;
|
||||||
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicket:
|
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicket:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicket - not implemented");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicket - not implemented");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
break;
|
break;
|
||||||
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicketView:
|
case DIIoctl::DVDLowOpenPartitionWithTmdAndTicketView:
|
||||||
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicketView - not implemented");
|
ERROR_LOG_FMT(IOS_DI, "DVDLowOpenPartitionWithTmdAndTicketView - not implemented");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DIFFERENT_PARTITION_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesDifferentPartitionCommand);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(IOS_DI, "Unknown ioctlv {:#04x}", request.request);
|
ERROR_LOG_FMT(IOS_DI, "Unknown ioctlv {:#04x}", request.request);
|
||||||
|
@ -198,7 +198,7 @@ std::optional<IPCReply> NetWDCommandDevice::Open(const OpenRequest& request)
|
|||||||
if (mode != WD::Mode::DSCommunications && mode != WD::Mode::AOSSAccessPointScan)
|
if (mode != WD::Mode::DSCommunications && mode != WD::Mode::AOSSAccessPointScan)
|
||||||
{
|
{
|
||||||
ERROR_LOG_FMT(IOS_NET, "Unsupported WD operating mode: {}", mode);
|
ERROR_LOG_FMT(IOS_NET, "Unsupported WD operating mode: {}", mode);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNCOMMON_WD_MODE);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesUncommonWDMode);
|
||||||
return IPCReply(s32(ResultCode::UnavailableCommand));
|
return IPCReply(s32(ResultCode::UnavailableCommand));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +390,7 @@ std::optional<IPCReply> NetWDCommandDevice::IOCtlV(const IOCtlVRequest& request)
|
|||||||
case IOCTLV_WD_CHANGE_GAMEINFO:
|
case IOCTLV_WD_CHANGE_GAMEINFO:
|
||||||
case IOCTLV_WD_CHANGE_VTSF:
|
case IOCTLV_WD_CHANGE_VTSF:
|
||||||
default:
|
default:
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_WD_UNIMPLEMENTED_IOCTL);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesWDUnimplementedIoctl);
|
||||||
request.Dump(GetSystem(), GetDeviceName(), Common::Log::LogType::IOS_NET,
|
request.Dump(GetSystem(), GetDeviceName(), Common::Log::LogType::IOS_NET,
|
||||||
Common::Log::LogLevel::LWARNING);
|
Common::Log::LogLevel::LWARNING);
|
||||||
}
|
}
|
||||||
|
12
Source/Core/Core/PerformanceSample.h
Normal file
12
Source/Core/Core/PerformanceSample.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2021 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct PerformanceSample
|
||||||
|
{
|
||||||
|
double speed_ratio; // See SystemTimers::GetEstimatedEmulationPerformance().
|
||||||
|
int num_prims;
|
||||||
|
int num_draw_calls;
|
||||||
|
};
|
105
Source/Core/Core/PerformanceSampleAggregator.cpp
Normal file
105
Source/Core/Core/PerformanceSampleAggregator.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2021 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "Core/PerformanceSampleAggregator.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "Common/Random.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr size_t s_samples_per_report = 100;
|
||||||
|
|
||||||
|
std::chrono::microseconds GetCurrentMicroseconds()
|
||||||
|
{
|
||||||
|
const std::chrono::high_resolution_clock::duration time_since_epoch =
|
||||||
|
std::chrono::high_resolution_clock::now().time_since_epoch();
|
||||||
|
return std::chrono::duration_cast<std::chrono::microseconds>(time_since_epoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::microseconds GetSamplingStartTimeJitter()
|
||||||
|
{
|
||||||
|
constexpr long long max_delay = std::chrono::microseconds(std::chrono::minutes(3)).count();
|
||||||
|
return std::chrono::microseconds(Common::Random::GenerateValue<u64>() % max_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::microseconds
|
||||||
|
GetSamplingStartTimestampUsingBaseDelay(const std::chrono::microseconds base_delay)
|
||||||
|
{
|
||||||
|
const std::chrono::microseconds now = GetCurrentMicroseconds();
|
||||||
|
const std::chrono::microseconds jitter = GetSamplingStartTimeJitter();
|
||||||
|
const std::chrono::microseconds sampling_start_timestamp = now + base_delay + jitter;
|
||||||
|
return sampling_start_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::microseconds GetInitialSamplingStartTimestamp()
|
||||||
|
{
|
||||||
|
constexpr std::chrono::microseconds base_initial_delay = std::chrono::minutes(5);
|
||||||
|
return GetSamplingStartTimestampUsingBaseDelay(base_initial_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::microseconds GetRepeatSamplingStartTimestamp()
|
||||||
|
{
|
||||||
|
constexpr std::chrono::microseconds base_repeat_delay = std::chrono::minutes(30);
|
||||||
|
return GetSamplingStartTimestampUsingBaseDelay(base_repeat_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceSampleAggregator::CompletedReport
|
||||||
|
GetCompletedReport(const std::vector<PerformanceSample>& samples)
|
||||||
|
{
|
||||||
|
PerformanceSampleAggregator::CompletedReport report;
|
||||||
|
const size_t num_samples = samples.size();
|
||||||
|
report.speed.resize(num_samples);
|
||||||
|
report.primitives.resize(num_samples);
|
||||||
|
report.draw_calls.resize(num_samples);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_samples; ++i)
|
||||||
|
{
|
||||||
|
const PerformanceSample& sample = samples[i];
|
||||||
|
report.speed[i] = static_cast<u32>(sample.speed_ratio * 1'000);
|
||||||
|
report.primitives[i] = sample.num_prims;
|
||||||
|
report.draw_calls[i] = sample.num_draw_calls;
|
||||||
|
}
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
PerformanceSampleAggregator::PerformanceSampleAggregator()
|
||||||
|
: m_samples(std::vector<PerformanceSample>(s_samples_per_report)),
|
||||||
|
m_next_starting_timestamp(std::numeric_limits<std::chrono::microseconds>::max())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceSampleAggregator::InitializePerformanceSampling()
|
||||||
|
{
|
||||||
|
m_samples.clear();
|
||||||
|
m_next_starting_timestamp = GetInitialSamplingStartTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceSampleAggregator::ResetPerformanceSampling()
|
||||||
|
{
|
||||||
|
m_samples.clear();
|
||||||
|
m_next_starting_timestamp = GetRepeatSamplingStartTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceSampleAggregator::AddSampleIfSamplingInProgress(PerformanceSample&& sample)
|
||||||
|
{
|
||||||
|
if (GetCurrentMicroseconds() >= m_next_starting_timestamp)
|
||||||
|
{
|
||||||
|
m_samples.push_back(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PerformanceSampleAggregator::CompletedReport>
|
||||||
|
PerformanceSampleAggregator::PopReportIfComplete()
|
||||||
|
{
|
||||||
|
if (m_samples.size() < s_samples_per_report)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const CompletedReport report = GetCompletedReport(m_samples);
|
||||||
|
ResetPerformanceSampling();
|
||||||
|
return report;
|
||||||
|
}
|
38
Source/Core/Core/PerformanceSampleAggregator.h
Normal file
38
Source/Core/Core/PerformanceSampleAggregator.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
#include "Core/PerformanceSample.h"
|
||||||
|
|
||||||
|
class PerformanceSampleAggregator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PerformanceSampleAggregator();
|
||||||
|
|
||||||
|
struct CompletedReport
|
||||||
|
{
|
||||||
|
std::vector<u32> speed;
|
||||||
|
std::vector<u32> primitives;
|
||||||
|
std::vector<u32> draw_calls;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Called on game start / title switch.
|
||||||
|
void InitializePerformanceSampling();
|
||||||
|
void AddSampleIfSamplingInProgress(PerformanceSample&& sample);
|
||||||
|
std::optional<CompletedReport> PopReportIfComplete();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Called after sampling report is completed
|
||||||
|
void ResetPerformanceSampling();
|
||||||
|
|
||||||
|
std::vector<PerformanceSample> m_samples;
|
||||||
|
std::chrono::microseconds m_next_starting_timestamp;
|
||||||
|
};
|
@ -437,6 +437,8 @@
|
|||||||
<ClInclude Include="Core\NetPlayServer.h" />
|
<ClInclude Include="Core\NetPlayServer.h" />
|
||||||
<ClInclude Include="Core\NetworkCaptureLogger.h" />
|
<ClInclude Include="Core\NetworkCaptureLogger.h" />
|
||||||
<ClInclude Include="Core\PatchEngine.h" />
|
<ClInclude Include="Core\PatchEngine.h" />
|
||||||
|
<ClInclude Include="Core\PerformanceSample.h" />
|
||||||
|
<ClInclude Include="Core\PerformanceSampleAggregator.h" />
|
||||||
<ClInclude Include="Core\PowerPC\BreakPoints.h" />
|
<ClInclude Include="Core\PowerPC\BreakPoints.h" />
|
||||||
<ClInclude Include="Core\PowerPC\CachedInterpreter\CachedInterpreter.h" />
|
<ClInclude Include="Core\PowerPC\CachedInterpreter\CachedInterpreter.h" />
|
||||||
<ClInclude Include="Core\PowerPC\CachedInterpreter\CachedInterpreterBlockCache.h" />
|
<ClInclude Include="Core\PowerPC\CachedInterpreter\CachedInterpreterBlockCache.h" />
|
||||||
@ -1105,6 +1107,7 @@
|
|||||||
<ClCompile Include="Core\NetPlayServer.cpp" />
|
<ClCompile Include="Core\NetPlayServer.cpp" />
|
||||||
<ClCompile Include="Core\NetworkCaptureLogger.cpp" />
|
<ClCompile Include="Core\NetworkCaptureLogger.cpp" />
|
||||||
<ClCompile Include="Core\PatchEngine.cpp" />
|
<ClCompile Include="Core\PatchEngine.cpp" />
|
||||||
|
<ClCompile Include="Core\PerformanceSampleAggregator.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\BreakPoints.cpp" />
|
<ClCompile Include="Core\PowerPC\BreakPoints.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreter_Disassembler.cpp" />
|
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreter_Disassembler.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreter.cpp" />
|
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreter.cpp" />
|
||||||
|
@ -783,7 +783,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_BP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesUnknownBPCommand);
|
||||||
WARN_LOG_FMT(VIDEO, "Unknown BP opcode: address = {:#010x} value = {:#010x}", bp.address,
|
WARN_LOG_FMT(VIDEO, "Unknown BP opcode: address = {:#010x} value = {:#010x}", bp.address,
|
||||||
bp.newvalue);
|
bp.newvalue);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
if (!(sub_cmd == UNKNOWN_20 && value == 0))
|
if (!(sub_cmd == UNKNOWN_20 && value == 0))
|
||||||
{
|
{
|
||||||
// All titles using libogc or the official SDK issue 0x20 with value=0 on startup
|
// All titles using libogc or the official SDK issue 0x20 with value=0 on startup
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_CP_PERF_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesCPPerfCommand);
|
||||||
DEBUG_LOG_FMT(VIDEO, "Unknown CP command possibly relating to perf queries used: {:02x}",
|
DEBUG_LOG_FMT(VIDEO, "Unknown CP command possibly relating to perf queries used: {:02x}",
|
||||||
sub_cmd);
|
sub_cmd);
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
case MATINDEX_A:
|
case MATINDEX_A:
|
||||||
if (sub_cmd != MATINDEX_A)
|
if (sub_cmd != MATINDEX_A)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesMaybeInvalidCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO,
|
WARN_LOG_FMT(VIDEO,
|
||||||
"CP MATINDEX_A: an exact value of {:02x} was expected "
|
"CP MATINDEX_A: an exact value of {:02x} was expected "
|
||||||
"but instead a value of {:02x} was seen",
|
"but instead a value of {:02x} was seen",
|
||||||
@ -121,7 +121,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
case MATINDEX_B:
|
case MATINDEX_B:
|
||||||
if (sub_cmd != MATINDEX_B)
|
if (sub_cmd != MATINDEX_B)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesMaybeInvalidCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO,
|
WARN_LOG_FMT(VIDEO,
|
||||||
"CP MATINDEX_B: an exact value of {:02x} was expected "
|
"CP MATINDEX_B: an exact value of {:02x} was expected "
|
||||||
"but instead a value of {:02x} was seen",
|
"but instead a value of {:02x} was seen",
|
||||||
@ -134,7 +134,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
case VCD_LO:
|
case VCD_LO:
|
||||||
if (sub_cmd != VCD_LO) // Stricter than YAGCD
|
if (sub_cmd != VCD_LO) // Stricter than YAGCD
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesMaybeInvalidCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO,
|
WARN_LOG_FMT(VIDEO,
|
||||||
"CP VCD_LO: an exact value of {:02x} was expected "
|
"CP VCD_LO: an exact value of {:02x} was expected "
|
||||||
"but instead a value of {:02x} was seen",
|
"but instead a value of {:02x} was seen",
|
||||||
@ -147,7 +147,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
case VCD_HI:
|
case VCD_HI:
|
||||||
if (sub_cmd != VCD_HI) // Stricter than YAGCD
|
if (sub_cmd != VCD_HI) // Stricter than YAGCD
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesMaybeInvalidCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO,
|
WARN_LOG_FMT(VIDEO,
|
||||||
"CP VCD_HI: an exact value of {:02x} was expected "
|
"CP VCD_HI: an exact value of {:02x} was expected "
|
||||||
"but instead a value of {:02x} was seen",
|
"but instead a value of {:02x} was seen",
|
||||||
@ -160,7 +160,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
case CP_VAT_REG_A:
|
case CP_VAT_REG_A:
|
||||||
if ((sub_cmd - CP_VAT_REG_A) >= CP_NUM_VAT_REG)
|
if ((sub_cmd - CP_VAT_REG_A) >= CP_NUM_VAT_REG)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesMaybeInvalidCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_A: Invalid VAT {}", sub_cmd - CP_VAT_REG_A);
|
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_A: Invalid VAT {}", sub_cmd - CP_VAT_REG_A);
|
||||||
}
|
}
|
||||||
vtx_attr[sub_cmd & CP_VAT_MASK].g0.Hex = value;
|
vtx_attr[sub_cmd & CP_VAT_MASK].g0.Hex = value;
|
||||||
@ -169,7 +169,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
case CP_VAT_REG_B:
|
case CP_VAT_REG_B:
|
||||||
if ((sub_cmd - CP_VAT_REG_B) >= CP_NUM_VAT_REG)
|
if ((sub_cmd - CP_VAT_REG_B) >= CP_NUM_VAT_REG)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesMaybeInvalidCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_B: Invalid VAT {}", sub_cmd - CP_VAT_REG_B);
|
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_B: Invalid VAT {}", sub_cmd - CP_VAT_REG_B);
|
||||||
}
|
}
|
||||||
vtx_attr[sub_cmd & CP_VAT_MASK].g1.Hex = value;
|
vtx_attr[sub_cmd & CP_VAT_MASK].g1.Hex = value;
|
||||||
@ -178,7 +178,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
case CP_VAT_REG_C:
|
case CP_VAT_REG_C:
|
||||||
if ((sub_cmd - CP_VAT_REG_C) >= CP_NUM_VAT_REG)
|
if ((sub_cmd - CP_VAT_REG_C) >= CP_NUM_VAT_REG)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_MAYBE_INVALID_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesMaybeInvalidCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_C: Invalid VAT {}", sub_cmd - CP_VAT_REG_C);
|
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_C: Invalid VAT {}", sub_cmd - CP_VAT_REG_C);
|
||||||
}
|
}
|
||||||
vtx_attr[sub_cmd & CP_VAT_MASK].g2.Hex = value;
|
vtx_attr[sub_cmd & CP_VAT_MASK].g2.Hex = value;
|
||||||
@ -195,7 +195,7 @@ void CPState::LoadCPReg(u8 sub_cmd, u32 value)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_CP_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesUnknownCPCommand);
|
||||||
WARN_LOG_FMT(VIDEO, "Unknown CP register {:02x} set to {:08x}", sub_cmd, value);
|
WARN_LOG_FMT(VIDEO, "Unknown CP register {:02x} set to {:08x}", sub_cmd, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,18 +300,15 @@ static void CheckCPConfiguration(int vtx_attr_group)
|
|||||||
// eventually simulate the behavior we have test cases for it.
|
// eventually simulate the behavior we have test cases for it.
|
||||||
if (num_cp_colors != xfmem.invtxspec.numcolors) [[unlikely]]
|
if (num_cp_colors != xfmem.invtxspec.numcolors) [[unlikely]]
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::MismatchedGPUColorsBetweenCPAndXF);
|
||||||
GameQuirk::MISMATCHED_GPU_COLORS_BETWEEN_CP_AND_XF);
|
|
||||||
}
|
}
|
||||||
if (num_cp_normals != num_xf_normals) [[unlikely]]
|
if (num_cp_normals != num_xf_normals) [[unlikely]]
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::MismatchedGPUNormalsBetweenCPAndXF);
|
||||||
GameQuirk::MISMATCHED_GPU_NORMALS_BETWEEN_CP_AND_XF);
|
|
||||||
}
|
}
|
||||||
if (num_cp_tex_coords != xfmem.invtxspec.numtextures) [[unlikely]]
|
if (num_cp_tex_coords != xfmem.invtxspec.numtextures) [[unlikely]]
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::MismatchedGPUTexCoordsBetweenCPAndXF);
|
||||||
GameQuirk::MISMATCHED_GPU_TEX_COORDS_BETWEEN_CP_AND_XF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't bail out, though; we can still render something successfully
|
// Don't bail out, though; we can still render something successfully
|
||||||
@ -327,7 +324,7 @@ static void CheckCPConfiguration(int vtx_attr_group)
|
|||||||
g_main_cp_state.matrix_index_a.Hex, xfmem.MatrixIndexA.Hex,
|
g_main_cp_state.matrix_index_a.Hex, xfmem.MatrixIndexA.Hex,
|
||||||
g_main_cp_state.matrix_index_b.Hex, xfmem.MatrixIndexB.Hex);
|
g_main_cp_state.matrix_index_b.Hex, xfmem.MatrixIndexB.Hex);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(
|
DolphinAnalytics::Instance().ReportGameQuirk(
|
||||||
GameQuirk::MISMATCHED_GPU_MATRIX_INDICES_BETWEEN_CP_AND_XF);
|
GameQuirk::MismatchedGPUMatrixIndicesBetweenCPAndXF);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_main_cp_state.vtx_attr[vtx_attr_group].g0.PosFormat >= ComponentFormat::InvalidFloat5)
|
if (g_main_cp_state.vtx_attr[vtx_attr_group].g0.PosFormat >= ComponentFormat::InvalidFloat5)
|
||||||
@ -337,7 +334,7 @@ static void CheckCPConfiguration(int vtx_attr_group)
|
|||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
|
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
|
||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::INVALID_POSITION_COMPONENT_FORMAT);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::InvalidPositionComponentFormat);
|
||||||
}
|
}
|
||||||
if (g_main_cp_state.vtx_attr[vtx_attr_group].g0.NormalFormat >= ComponentFormat::InvalidFloat5)
|
if (g_main_cp_state.vtx_attr[vtx_attr_group].g0.NormalFormat >= ComponentFormat::InvalidFloat5)
|
||||||
{
|
{
|
||||||
@ -346,7 +343,7 @@ static void CheckCPConfiguration(int vtx_attr_group)
|
|||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
|
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
|
||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::INVALID_NORMAL_COMPONENT_FORMAT);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::InvalidNormalComponentFormat);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < 8; i++)
|
for (size_t i = 0; i < 8; i++)
|
||||||
{
|
{
|
||||||
@ -359,7 +356,7 @@ static void CheckCPConfiguration(int vtx_attr_group)
|
|||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(
|
DolphinAnalytics::Instance().ReportGameQuirk(
|
||||||
GameQuirk::INVALID_TEXTURE_COORDINATE_COMPONENT_FORMAT);
|
GameQuirk::InvalidTextureCoordinateComponentFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < 2; i++)
|
for (size_t i = 0; i < 2; i++)
|
||||||
@ -371,7 +368,7 @@ static void CheckCPConfiguration(int vtx_attr_group)
|
|||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
|
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
|
||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
||||||
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::INVALID_COLOR_COMPONENT_FORMAT);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::InvalidColorComponentFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -459,13 +459,11 @@ void VertexManagerBase::Flush()
|
|||||||
// eventually simulate the behavior we have test cases for it.
|
// eventually simulate the behavior we have test cases for it.
|
||||||
if (xfmem.numTexGen.numTexGens != bpmem.genMode.numtexgens)
|
if (xfmem.numTexGen.numTexGens != bpmem.genMode.numtexgens)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::MismatchedGPUTexgensBetweenXFAndBP);
|
||||||
GameQuirk::MISMATCHED_GPU_TEXGENS_BETWEEN_XF_AND_BP);
|
|
||||||
}
|
}
|
||||||
if (xfmem.numChan.numColorChans != bpmem.genMode.numcolchans)
|
if (xfmem.numChan.numColorChans != bpmem.genMode.numcolchans)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::MismatchedGPUColorsBetweenXFAndBP);
|
||||||
GameQuirk::MISMATCHED_GPU_COLORS_BETWEEN_XF_AND_BP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -124,7 +124,7 @@ u32 VideoBackendBase::Video_GetQueryResult(PerfQueryType type)
|
|||||||
|
|
||||||
u16 VideoBackendBase::Video_GetBoundingBox(int index)
|
u16 VideoBackendBase::Video_GetBoundingBox(int index)
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::READS_BOUNDING_BOX);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::ReadsBoundingBox);
|
||||||
|
|
||||||
if (!g_ActiveConfig.bBBoxEnable)
|
if (!g_ActiveConfig.bBBoxEnable)
|
||||||
{
|
{
|
||||||
|
@ -48,11 +48,11 @@ static void XFRegWritten(Core::System& system, XFStateManager& xf_state_manager,
|
|||||||
{
|
{
|
||||||
ClipDisable setting{.hex = value};
|
ClipDisable setting{.hex = value};
|
||||||
if (setting.disable_clipping_detection)
|
if (setting.disable_clipping_detection)
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SETS_XF_CLIPDISABLE_BIT_0);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SetsXFClipdisableBit0);
|
||||||
if (setting.disable_trivial_rejection)
|
if (setting.disable_trivial_rejection)
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SETS_XF_CLIPDISABLE_BIT_1);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SetsXFClipdisableBit1);
|
||||||
if (setting.disable_cpoly_clipping_acceleration)
|
if (setting.disable_cpoly_clipping_acceleration)
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SETS_XF_CLIPDISABLE_BIT_2);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SetsXFClipdisableBit2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ static void XFRegWritten(Core::System& system, XFStateManager& xf_state_manager,
|
|||||||
case 0x104d:
|
case 0x104d:
|
||||||
case 0x104e:
|
case 0x104e:
|
||||||
case 0x104f:
|
case 0x104f:
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_XF_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesUnknownXFCommand);
|
||||||
DEBUG_LOG_FMT(VIDEO, "Possible Normal Mtx XF reg?: {:x}={:x}", address, value);
|
DEBUG_LOG_FMT(VIDEO, "Possible Normal Mtx XF reg?: {:x}={:x}", address, value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ static void XFRegWritten(Core::System& system, XFStateManager& xf_state_manager,
|
|||||||
case 0x1017:
|
case 0x1017:
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_XF_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::UsesUnknownXFCommand);
|
||||||
WARN_LOG_FMT(VIDEO, "Unknown XF Reg: {:x}={:x}", address, value);
|
WARN_LOG_FMT(VIDEO, "Unknown XF Reg: {:x}={:x}", address, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user