Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 659b5f8088 | |||
| 45b13c3037 | |||
| ef6cc3aa1d | |||
| 28b822fe38 | |||
| fb0fe3b8c3 | |||
| 09b6f03592 | |||
| 72c1cb85f1 | |||
| 64a5548454 | |||
| 81a037df9d | |||
| 2c57f0fbd5 | |||
| 04e9486651 | |||
| 2a2ee62cfd | |||
| 62766b1326 | |||
| 5dc021d15b | |||
| 34c3ec2f8c | |||
| 45e117b043 | |||
| 9dc4a80b17 | |||
| df0d8c45d2 | |||
| b769b1be26 | |||
| 44c5ea3639 | |||
| 6b00443bc1 | |||
| 8959f3521f | |||
| 6a0143400f | |||
| 748551dafb | |||
| 19c14589d3 | |||
| a8245cf2f1 | |||
| 2afc1060ef | |||
| 5882cc0502 | |||
| 04dcada85f | |||
| f81c783b5b | |||
| cc4335a9c6 | |||
| 1b76e7e890 | |||
| 80a673a27f | |||
| ad48259d7e | |||
| e9bb95ae16 | |||
| 822edff5bd | |||
| 3b0458a7a5 | |||
| df42100320 | |||
| 37ef2ee595 | |||
| 436457b6e7 | |||
| b7febb5625 | |||
| 0e9a6759f9 | |||
| dd790abab0 | |||
| 51512d01d8 | |||
| 5b8bc56e65 | |||
| dc18a1261c | |||
| dca2e2c8f1 | |||
| 83f8c1a25e |
@@ -64,8 +64,10 @@ if (MSVC)
|
||||
else()
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Werror=array-bounds
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
|
||||
@@ -138,6 +138,8 @@ add_library(common STATIC
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
misc.cpp
|
||||
nvidia_flags.cpp
|
||||
nvidia_flags.h
|
||||
page_table.cpp
|
||||
page_table.h
|
||||
param_package.cpp
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "common/nvidia_flags.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void ConfigureNvidiaEnvironmentFlags() {
|
||||
#ifdef _WIN32
|
||||
const std::string shader_path = Common::FS::SanitizePath(
|
||||
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
|
||||
const std::string windows_path =
|
||||
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
|
||||
void(Common::FS::CreateFullPath(shader_path + '/'));
|
||||
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
|
||||
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Configure platform specific flags for Nvidia's driver
|
||||
void ConfigureNvidiaEnvironmentFlags();
|
||||
|
||||
} // namespace Common
|
||||
@@ -400,8 +400,6 @@ add_library(core STATIC
|
||||
hle/service/ldr/ldr.h
|
||||
hle/service/lm/lm.cpp
|
||||
hle/service/lm/lm.h
|
||||
hle/service/lm/manager.cpp
|
||||
hle/service/lm/manager.h
|
||||
hle/service/mig/mig.cpp
|
||||
hle/service/mig/mig.h
|
||||
hle/service/mii/manager.cpp
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include "core/hle/service/apm/controller.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
@@ -293,8 +292,6 @@ struct System::Impl {
|
||||
perf_stats->GetMeanFrametime());
|
||||
}
|
||||
|
||||
lm_manager.Flush();
|
||||
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
@@ -398,7 +395,6 @@ struct System::Impl {
|
||||
|
||||
/// Service State
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
Service::LM::Manager lm_manager{reporter};
|
||||
Service::Time::TimeManager time_manager;
|
||||
|
||||
/// Service manager
|
||||
@@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const {
|
||||
return impl->apm_controller;
|
||||
}
|
||||
|
||||
Service::LM::Manager& System::GetLogManager() {
|
||||
return impl->lm_manager;
|
||||
}
|
||||
|
||||
const Service::LM::Manager& System::GetLogManager() const {
|
||||
return impl->lm_manager;
|
||||
}
|
||||
|
||||
Service::Time::TimeManager& System::GetTimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
@@ -62,10 +62,6 @@ namespace Glue {
|
||||
class ARPManager;
|
||||
}
|
||||
|
||||
namespace LM {
|
||||
class Manager;
|
||||
} // namespace LM
|
||||
|
||||
namespace SM {
|
||||
class ServiceManager;
|
||||
} // namespace SM
|
||||
@@ -351,9 +347,6 @@ public:
|
||||
[[nodiscard]] Service::APM::Controller& GetAPMController();
|
||||
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
|
||||
|
||||
[[nodiscard]] Service::LM::Manager& GetLogManager();
|
||||
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
|
||||
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
|
||||
@@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
}
|
||||
|
||||
cache.erase(old_path);
|
||||
file->Open(new_path, "r+b");
|
||||
cache.insert_or_assign(new_path, std::move(file));
|
||||
if (file->Open(new_path, "r+b")) {
|
||||
cache.insert_or_assign(new_path, std::move(file));
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
|
||||
}
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
@@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
|
||||
}
|
||||
|
||||
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));
|
||||
if (file->Open(file_new_path, "r+b")) {
|
||||
cache.insert_or_assign(std::move(file_new_path), std::move(file));
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
|
||||
}
|
||||
}
|
||||
|
||||
return OpenDirectory(new_path, Mode::ReadWrite);
|
||||
|
||||
@@ -635,7 +635,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
||||
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
|
||||
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
|
||||
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
|
||||
{53, nullptr, "BeginVrModeEx"},
|
||||
{53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
|
||||
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
|
||||
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
|
||||
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
|
||||
@@ -732,6 +732,13 @@ void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::BeginVrModeEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ private:
|
||||
void IsVrModeEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetVrModeEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
|
||||
void BeginVrModeEx(Kernel::HLERequestContext& ctx);
|
||||
void EndVrModeEx(Kernel::HLERequestContext& ctx);
|
||||
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
|
||||
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -946,6 +946,24 @@ void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
|
||||
sixaxis_sensors_enabled = six_axis_status;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetSixAxisFusionParameters(const DeviceHandle& handle, f32 parameter1,
|
||||
f32 parameter2) {
|
||||
sixaxis_fusion_parameter1 = parameter1;
|
||||
sixaxis_fusion_parameter2 = parameter2;
|
||||
}
|
||||
|
||||
std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters(const DeviceHandle& handle) {
|
||||
return {
|
||||
sixaxis_fusion_parameter1,
|
||||
sixaxis_fusion_parameter2,
|
||||
};
|
||||
}
|
||||
|
||||
void Controller_NPad::ResetSixAxisFusionParameters(const DeviceHandle& handle) {
|
||||
sixaxis_fusion_parameter1 = 0.0f;
|
||||
sixaxis_fusion_parameter2 = 0.0f;
|
||||
}
|
||||
|
||||
void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
|
||||
const auto npad_index_1 = NPadIdToIndex(npad_id_1);
|
||||
const auto npad_index_2 = NPadIdToIndex(npad_id_2);
|
||||
|
||||
@@ -202,6 +202,9 @@ public:
|
||||
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
|
||||
bool IsSixAxisSensorAtRest() const;
|
||||
void SetSixAxisEnabled(bool six_axis_status);
|
||||
void SetSixAxisFusionParameters(const DeviceHandle& handle, f32 parameter1, f32 parameter2);
|
||||
std::pair<f32, f32> GetSixAxisFusionParameters(const DeviceHandle& handle);
|
||||
void ResetSixAxisFusionParameters(const DeviceHandle& handle);
|
||||
LedPattern GetLedPattern(u32 npad_id);
|
||||
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
|
||||
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
|
||||
@@ -458,6 +461,8 @@ private:
|
||||
std::array<bool, 10> unintended_home_button_input_protection{};
|
||||
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
|
||||
bool sixaxis_sensors_enabled{true};
|
||||
f32 sixaxis_fusion_parameter1{};
|
||||
f32 sixaxis_fusion_parameter2{};
|
||||
bool sixaxis_at_rest{true};
|
||||
std::array<ControllerPad, 10> npad_pad_states{};
|
||||
bool is_in_lr_assignment_mode{false};
|
||||
|
||||
@@ -209,9 +209,9 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
|
||||
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
|
||||
{68, nullptr, "IsSixAxisSensorFusionEnabled"},
|
||||
{69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
|
||||
{70, nullptr, "SetSixAxisSensorFusionParameters"},
|
||||
{71, nullptr, "GetSixAxisSensorFusionParameters"},
|
||||
{72, nullptr, "ResetSixAxisSensorFusionParameters"},
|
||||
{70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
|
||||
{71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
|
||||
{72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
|
||||
{73, nullptr, "SetAccelerometerParameters"},
|
||||
{74, nullptr, "GetAccelerometerParameters"},
|
||||
{75, nullptr, "ResetAccelerometerParameters"},
|
||||
@@ -534,6 +534,81 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
f32 parameter1;
|
||||
f32 parameter2;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.parameter1,
|
||||
parameters.parameter2);
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, float1={}, float2={}, npad_type={}, npad_id={}, "
|
||||
"device_index={}, applet_resource_user_id={}",
|
||||
parameters.parameter1, parameters.parameter2, parameters.sixaxis_handle.npad_type,
|
||||
parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
f32 parameter1 = 0;
|
||||
f32 parameter2 = 0;
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
std::tie(parameter1, parameter2) =
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.GetSixAxisFusionParameters(parameters.sixaxis_handle);
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, "
|
||||
"device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(parameter1);
|
||||
rb.Push(parameter2);
|
||||
}
|
||||
|
||||
void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.ResetSixAxisFusionParameters(parameters.sixaxis_handle);
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, "
|
||||
"device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
|
||||
|
||||
@@ -97,6 +97,9 @@ private:
|
||||
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
|
||||
void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
|
||||
void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
|
||||
void ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
|
||||
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
+293
-36
@@ -5,22 +5,71 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/lm/lm.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::LM {
|
||||
enum class LogSeverity : u8 {
|
||||
Trace = 0,
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
Error = 3,
|
||||
Fatal = 4,
|
||||
};
|
||||
|
||||
// To keep flags out of hashing as well as the payload size
|
||||
struct LogPacketHeaderEntry {
|
||||
u64_le pid{};
|
||||
u64_le tid{};
|
||||
LogSeverity severity{};
|
||||
u8 verbosity{};
|
||||
|
||||
auto operator<=>(const LogPacketHeaderEntry&) const = default;
|
||||
};
|
||||
} // namespace Service::LM
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Service::LM::LogPacketHeaderEntry> {
|
||||
std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
|
||||
std::size_t seed{};
|
||||
boost::hash_combine(seed, k.pid);
|
||||
boost::hash_combine(seed, k.tid);
|
||||
boost::hash_combine(seed, k.severity);
|
||||
boost::hash_combine(seed, k.verbosity);
|
||||
return seed;
|
||||
};
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace Service::LM {
|
||||
|
||||
enum class LogDestination : u32 {
|
||||
TargetManager = 1 << 0,
|
||||
Uart = 1 << 1,
|
||||
UartSleep = 1 << 2,
|
||||
All = 0xffff,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
|
||||
|
||||
enum class LogPacketFlags : u8 {
|
||||
Head = 1 << 0,
|
||||
Tail = 1 << 1,
|
||||
LittleEndian = 1 << 2,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
|
||||
|
||||
class ILogger final : public ServiceFramework<ILogger> {
|
||||
public:
|
||||
explicit ILogger(Core::System& system_)
|
||||
: ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()},
|
||||
memory{system_.Memory()} {
|
||||
explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILogger::Log, "Log"},
|
||||
{1, &ILogger::SetDestination, "SetDestination"},
|
||||
@@ -30,54 +79,262 @@ public:
|
||||
|
||||
private:
|
||||
void Log(Kernel::HLERequestContext& ctx) {
|
||||
std::size_t offset{};
|
||||
const auto data = ctx.ReadBuffer();
|
||||
|
||||
// This function only succeeds - Get that out of the way
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
// Read MessageHeader, despite not doing anything with it right now
|
||||
MessageHeader header{};
|
||||
VAddr addr{ctx.BufferDescriptorX()[0].Address()};
|
||||
const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
|
||||
memory.ReadBlock(addr, &header, sizeof(MessageHeader));
|
||||
addr += sizeof(MessageHeader);
|
||||
|
||||
FieldMap fields;
|
||||
while (addr < end_addr) {
|
||||
const auto field = static_cast<Field>(memory.Read8(addr++));
|
||||
const auto length = memory.Read8(addr++);
|
||||
|
||||
if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
|
||||
++addr;
|
||||
}
|
||||
|
||||
SCOPE_EXIT({ addr += length; });
|
||||
|
||||
if (field == Field::Skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<u8> data(length);
|
||||
memory.ReadBlock(addr, data.data(), length);
|
||||
fields.emplace(field, std::move(data));
|
||||
if (data.size() < sizeof(LogPacketHeader)) {
|
||||
LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
|
||||
return;
|
||||
}
|
||||
|
||||
manager.Log({header, std::move(fields)});
|
||||
LogPacketHeader header{};
|
||||
std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
|
||||
offset += sizeof(LogPacketHeader);
|
||||
|
||||
LogPacketHeaderEntry entry{
|
||||
.pid = header.pid,
|
||||
.tid = header.tid,
|
||||
.severity = header.severity,
|
||||
.verbosity = header.verbosity,
|
||||
};
|
||||
|
||||
if (True(header.flags & LogPacketFlags::Head)) {
|
||||
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
|
||||
std::memcpy(tmp.data(), data.data() + offset, tmp.size());
|
||||
entries[entry] = std::move(tmp);
|
||||
} else {
|
||||
// Append to existing entry
|
||||
if (!entries.contains(entry)) {
|
||||
LOG_ERROR(Service_LM, "Log entry does not exist!");
|
||||
return;
|
||||
}
|
||||
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
|
||||
|
||||
auto& existing_entry = entries[entry];
|
||||
const auto base = existing_entry.size();
|
||||
existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
|
||||
std::memcpy(existing_entry.data() + base, data.data() + offset,
|
||||
(data.size() - sizeof(LogPacketHeader)));
|
||||
}
|
||||
|
||||
if (True(header.flags & LogPacketFlags::Tail)) {
|
||||
auto it = entries.find(entry);
|
||||
if (it == entries.end()) {
|
||||
LOG_ERROR(Service_LM, "Log entry does not exist!");
|
||||
return;
|
||||
}
|
||||
ParseLog(it->first, it->second);
|
||||
entries.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void SetDestination(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto destination = rp.PopEnum<DestinationFlag>();
|
||||
const auto log_destination = rp.PopEnum<LogDestination>();
|
||||
|
||||
LOG_DEBUG(Service_LM, "called, destination={:08X}", destination);
|
||||
|
||||
manager.SetDestination(destination);
|
||||
LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
|
||||
destination = log_destination;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Manager& manager;
|
||||
Core::Memory::Memory& memory;
|
||||
u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
|
||||
u32 result{};
|
||||
u32 shift{};
|
||||
do {
|
||||
result |= (data[offset] & 0x7f) << shift;
|
||||
shift += 7;
|
||||
offset++;
|
||||
if (offset >= data.size()) {
|
||||
break;
|
||||
}
|
||||
} while ((data[offset] & 0x80) != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
|
||||
std::size_t length) {
|
||||
if (length == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto length_to_read = std::min(length, data.size() - offset);
|
||||
|
||||
std::string output(length_to_read, '\0');
|
||||
std::memcpy(output.data(), data.data() + offset, length_to_read);
|
||||
offset += length_to_read;
|
||||
return output;
|
||||
}
|
||||
|
||||
u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
|
||||
ASSERT(length == sizeof(u32));
|
||||
u32_le output{};
|
||||
std::memcpy(&output, data.data() + offset, sizeof(u32));
|
||||
offset += length;
|
||||
return output;
|
||||
}
|
||||
|
||||
u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
|
||||
ASSERT(length == sizeof(u64));
|
||||
u64_le output{};
|
||||
std::memcpy(&output, data.data() + offset, sizeof(u64));
|
||||
offset += length;
|
||||
return output;
|
||||
}
|
||||
|
||||
void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
|
||||
// Possible entries
|
||||
std::optional<std::string> text_log;
|
||||
std::optional<u32> line_number;
|
||||
std::optional<std::string> file_name;
|
||||
std::optional<std::string> function_name;
|
||||
std::optional<std::string> module_name;
|
||||
std::optional<std::string> thread_name;
|
||||
std::optional<u64> log_pack_drop_count;
|
||||
std::optional<s64> user_system_clock;
|
||||
std::optional<std::string> process_name;
|
||||
|
||||
std::size_t offset{};
|
||||
while (offset < log_data.size()) {
|
||||
const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
|
||||
const auto chunk_size = ReadLeb128(log_data, offset);
|
||||
|
||||
switch (key) {
|
||||
case LogDataChunkKey::LogSessionBegin:
|
||||
case LogDataChunkKey::LogSessionEnd:
|
||||
break;
|
||||
case LogDataChunkKey::TextLog:
|
||||
text_log = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::LineNumber:
|
||||
line_number = ReadAsU32(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::FileName:
|
||||
file_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::FunctionName:
|
||||
function_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::ModuleName:
|
||||
module_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::ThreadName:
|
||||
thread_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::LogPacketDropCount:
|
||||
log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::UserSystemClock:
|
||||
user_system_clock = ReadAsU64(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::ProcessName:
|
||||
process_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string output_log{};
|
||||
if (process_name) {
|
||||
output_log += fmt::format("Process: {}\n", *process_name);
|
||||
}
|
||||
if (module_name) {
|
||||
output_log += fmt::format("Module: {}\n", *module_name);
|
||||
}
|
||||
if (file_name) {
|
||||
output_log += fmt::format("File: {}\n", *file_name);
|
||||
}
|
||||
if (function_name) {
|
||||
output_log += fmt::format("Function: {}\n", *function_name);
|
||||
}
|
||||
if (line_number && *line_number != 0) {
|
||||
output_log += fmt::format("Line: {}\n", *line_number);
|
||||
}
|
||||
output_log += fmt::format("ProcessID: {:X}\n", entry.pid);
|
||||
output_log += fmt::format("ThreadID: {:X}\n", entry.tid);
|
||||
|
||||
if (text_log) {
|
||||
output_log += fmt::format("Log Text: {}\n", *text_log);
|
||||
}
|
||||
|
||||
switch (entry.severity) {
|
||||
case LogSeverity::Trace:
|
||||
LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
case LogSeverity::Info:
|
||||
LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
case LogSeverity::Warning:
|
||||
LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
|
||||
DestinationToString(destination), output_log);
|
||||
break;
|
||||
case LogSeverity::Error:
|
||||
LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
case LogSeverity::Fatal:
|
||||
LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
|
||||
DestinationToString(destination), output_log);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string DestinationToString(LogDestination destination) {
|
||||
if (True(destination & LogDestination::All)) {
|
||||
return "TargetManager | Uart | UartSleep";
|
||||
}
|
||||
std::string output{};
|
||||
if (True(destination & LogDestination::TargetManager)) {
|
||||
output += "| TargetManager";
|
||||
}
|
||||
if (True(destination & LogDestination::Uart)) {
|
||||
output += "| Uart";
|
||||
}
|
||||
if (True(destination & LogDestination::UartSleep)) {
|
||||
output += "| UartSleep";
|
||||
}
|
||||
if (output.length() > 0) {
|
||||
return output.substr(2);
|
||||
}
|
||||
return "No Destination";
|
||||
}
|
||||
|
||||
enum class LogDataChunkKey : u32 {
|
||||
LogSessionBegin = 0,
|
||||
LogSessionEnd = 1,
|
||||
TextLog = 2,
|
||||
LineNumber = 3,
|
||||
FileName = 4,
|
||||
FunctionName = 5,
|
||||
ModuleName = 6,
|
||||
ThreadName = 7,
|
||||
LogPacketDropCount = 8,
|
||||
UserSystemClock = 9,
|
||||
ProcessName = 10,
|
||||
};
|
||||
|
||||
struct LogPacketHeader {
|
||||
u64_le pid{};
|
||||
u64_le tid{};
|
||||
LogPacketFlags flags{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
LogSeverity severity{};
|
||||
u8 verbosity{};
|
||||
u32_le payload_size{};
|
||||
};
|
||||
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
|
||||
|
||||
std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
|
||||
LogDestination destination{LogDestination::All};
|
||||
};
|
||||
|
||||
class LM final : public ServiceFramework<LM> {
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service::LM {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DestinationFlag dest) {
|
||||
std::vector<std::string> array;
|
||||
const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) {
|
||||
if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) {
|
||||
array.emplace_back(std::move(name));
|
||||
}
|
||||
};
|
||||
|
||||
check_single_flag(DestinationFlag::Default, "Default");
|
||||
check_single_flag(DestinationFlag::UART, "UART");
|
||||
check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)");
|
||||
|
||||
os << "[";
|
||||
for (const auto& entry : array) {
|
||||
os << entry << ", ";
|
||||
}
|
||||
return os << "]";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) {
|
||||
switch (severity) {
|
||||
case MessageHeader::Severity::Trace:
|
||||
return os << "Trace";
|
||||
case MessageHeader::Severity::Info:
|
||||
return os << "Info";
|
||||
case MessageHeader::Severity::Warning:
|
||||
return os << "Warning";
|
||||
case MessageHeader::Severity::Error:
|
||||
return os << "Error";
|
||||
case MessageHeader::Severity::Critical:
|
||||
return os << "Critical";
|
||||
default:
|
||||
return os << fmt::format("{:08X}", static_cast<u32>(severity));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Field field) {
|
||||
switch (field) {
|
||||
case Field::Skip:
|
||||
return os << "Skip";
|
||||
case Field::Message:
|
||||
return os << "Message";
|
||||
case Field::Line:
|
||||
return os << "Line";
|
||||
case Field::Filename:
|
||||
return os << "Filename";
|
||||
case Field::Function:
|
||||
return os << "Function";
|
||||
case Field::Module:
|
||||
return os << "Module";
|
||||
case Field::Thread:
|
||||
return os << "Thread";
|
||||
default:
|
||||
return os << fmt::format("{:08X}", static_cast<u32>(field));
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatField(Field type, const std::vector<u8>& data) {
|
||||
switch (type) {
|
||||
case Field::Skip:
|
||||
return "";
|
||||
case Field::Line:
|
||||
if (data.size() >= sizeof(u32)) {
|
||||
u32 line;
|
||||
std::memcpy(&line, data.data(), sizeof(u32));
|
||||
return fmt::format("{}", line);
|
||||
}
|
||||
return "[ERROR DECODING LINE NUMBER]";
|
||||
case Field::Message:
|
||||
case Field::Filename:
|
||||
case Field::Function:
|
||||
case Field::Module:
|
||||
case Field::Thread:
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(data.data()), data.size());
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented field type={}", type);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {}
|
||||
|
||||
Manager::~Manager() = default;
|
||||
|
||||
void Manager::SetEnabled(bool enabled) {
|
||||
this->enabled = enabled;
|
||||
}
|
||||
|
||||
void Manager::SetDestination(DestinationFlag destination) {
|
||||
this->destination = destination;
|
||||
}
|
||||
|
||||
void Manager::Log(LogMessage message) {
|
||||
if (message.header.IsHeadLog()) {
|
||||
InitializeLog();
|
||||
}
|
||||
|
||||
current_log.emplace_back(std::move(message));
|
||||
|
||||
if (current_log.back().header.IsTailLog()) {
|
||||
FinalizeLog();
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::Flush() {
|
||||
FinalizeLog();
|
||||
}
|
||||
|
||||
void Manager::InitializeLog() {
|
||||
current_log.clear();
|
||||
|
||||
LOG_INFO(Service_LM, "Initialized new log session");
|
||||
}
|
||||
|
||||
void Manager::FinalizeLog() {
|
||||
reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log));
|
||||
|
||||
LOG_INFO(Service_LM, "Finalized current log session");
|
||||
}
|
||||
|
||||
} // namespace Service::LM
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace Core {
|
||||
class Reporter;
|
||||
}
|
||||
|
||||
namespace Service::LM {
|
||||
|
||||
enum class DestinationFlag : u32 {
|
||||
Default = 1,
|
||||
UART = 2,
|
||||
UARTSleeping = 4,
|
||||
|
||||
All = 0xFFFF,
|
||||
};
|
||||
|
||||
struct MessageHeader {
|
||||
enum Flags : u32_le {
|
||||
IsHead = 1,
|
||||
IsTail = 2,
|
||||
};
|
||||
enum Severity : u32_le {
|
||||
Trace,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Critical,
|
||||
};
|
||||
|
||||
u64_le pid;
|
||||
u64_le thread_context;
|
||||
union {
|
||||
BitField<0, 16, Flags> flags;
|
||||
BitField<16, 8, Severity> severity;
|
||||
BitField<24, 8, u32> verbosity;
|
||||
};
|
||||
u32_le payload_size;
|
||||
|
||||
bool IsHeadLog() const {
|
||||
return flags & IsHead;
|
||||
}
|
||||
bool IsTailLog() const {
|
||||
return flags & IsTail;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
|
||||
|
||||
enum class Field : u8 {
|
||||
Skip = 1,
|
||||
Message = 2,
|
||||
Line = 3,
|
||||
Filename = 4,
|
||||
Function = 5,
|
||||
Module = 6,
|
||||
Thread = 7,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DestinationFlag dest);
|
||||
std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity);
|
||||
std::ostream& operator<<(std::ostream& os, Field field);
|
||||
|
||||
using FieldMap = std::map<Field, std::vector<u8>>;
|
||||
|
||||
struct LogMessage {
|
||||
MessageHeader header;
|
||||
FieldMap fields;
|
||||
};
|
||||
|
||||
std::string FormatField(Field type, const std::vector<u8>& data);
|
||||
|
||||
class Manager {
|
||||
public:
|
||||
explicit Manager(Core::Reporter& reporter);
|
||||
~Manager();
|
||||
|
||||
void SetEnabled(bool enabled);
|
||||
void SetDestination(DestinationFlag destination);
|
||||
|
||||
void Log(LogMessage message);
|
||||
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
void InitializeLog();
|
||||
void FinalizeLog();
|
||||
|
||||
bool enabled = true;
|
||||
DestinationFlag destination = DestinationFlag::All;
|
||||
|
||||
std::vector<LogMessage> current_log;
|
||||
|
||||
Core::Reporter& reporter;
|
||||
};
|
||||
|
||||
} // namespace Service::LM
|
||||
@@ -21,7 +21,7 @@ namespace {
|
||||
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
|
||||
constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
|
||||
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
|
||||
|
||||
constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
|
||||
constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
|
||||
@@ -141,13 +141,6 @@ T GetRandomValue(T max) {
|
||||
return GetRandomValue<T>({}, max);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetArrayValue(const u8* data, std::size_t index) {
|
||||
T result{};
|
||||
std::memcpy(&result, &data[index * sizeof(T)], sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
@@ -193,32 +186,20 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
|
||||
const std::size_t index{3 * static_cast<std::size_t>(age) +
|
||||
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
|
||||
|
||||
const auto faceline_type_info{
|
||||
GetArrayValue<RandomMiiData4>(&RawData::RandomMiiFaceline[0], index)};
|
||||
const auto faceline_color_info{GetArrayValue<RandomMiiData3>(
|
||||
RawData::RandomMiiFacelineColor.data(),
|
||||
const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)};
|
||||
const auto faceline_color_info{RawData::RandomMiiFacelineColor.at(
|
||||
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
|
||||
const auto faceline_wrinkle_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineWrinkle.data(), index)};
|
||||
const auto faceline_makeup_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineMakeup.data(), index)};
|
||||
const auto hair_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiHairType.data(), index)};
|
||||
const auto hair_color_info{GetArrayValue<RandomMiiData3>(RawData::RandomMiiHairColor.data(),
|
||||
3 * static_cast<std::size_t>(race) +
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto eye_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyeType.data(), index)};
|
||||
const auto eye_color_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiEyeColor.data(),
|
||||
static_cast<std::size_t>(race))};
|
||||
const auto eyebrow_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyebrowType.data(), index)};
|
||||
const auto nose_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiNoseType.data(), index)};
|
||||
const auto mouth_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiMouthType.data(), index)};
|
||||
const auto glasses_type_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiGlassType.data(),
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
|
||||
const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
|
||||
const auto hair_type_info{RawData::RandomMiiHairType.at(index)};
|
||||
const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto eye_type_info{RawData::RandomMiiEyeType.at(index)};
|
||||
const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
|
||||
const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
|
||||
const auto nose_type_info{RawData::RandomMiiNoseType.at(index)};
|
||||
const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)};
|
||||
const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
|
||||
|
||||
bf.faceline_type.Assign(
|
||||
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
|
||||
@@ -455,8 +436,7 @@ MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::BuildDefault(std::size_t index) {
|
||||
return ConvertStoreDataToInfo(BuildDefaultStoreData(
|
||||
GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
|
||||
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
|
||||
}
|
||||
|
||||
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
|
||||
|
||||
@@ -233,7 +233,7 @@ struct RandomMiiData4 {
|
||||
Age age{};
|
||||
Race race{};
|
||||
u32 values_count{};
|
||||
std::array<u8, 0xbc> values{};
|
||||
std::array<u32, 47> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
|
||||
|
||||
@@ -241,14 +241,14 @@ struct RandomMiiData3 {
|
||||
u32 arg_1;
|
||||
u32 arg_2;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
std::array<u32, 47> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
|
||||
|
||||
struct RandomMiiData2 {
|
||||
u32 arg_1;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
std::array<u32, 47> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
|
||||
|
||||
|
||||
+1625
-2223
File diff suppressed because it is too large
Load Diff
@@ -7,21 +7,22 @@
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/mii/manager.h"
|
||||
|
||||
namespace Service::Mii::RawData {
|
||||
|
||||
extern const std::array<u8, 1728> DefaultMii;
|
||||
extern const std::array<u8, 3672> RandomMiiFaceline;
|
||||
extern const std::array<u8, 1200> RandomMiiFacelineColor;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineWrinkle;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineMakeup;
|
||||
extern const std::array<u8, 3672> RandomMiiHairType;
|
||||
extern const std::array<u8, 1800> RandomMiiHairColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyeType;
|
||||
extern const std::array<u8, 588> RandomMiiEyeColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyebrowType;
|
||||
extern const std::array<u8, 3672> RandomMiiNoseType;
|
||||
extern const std::array<u8, 3672> RandomMiiMouthType;
|
||||
extern const std::array<u8, 588> RandomMiiGlassType;
|
||||
extern const std::array<Service::Mii::DefaultMii, 8> DefaultMii;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline;
|
||||
extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType;
|
||||
extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType;
|
||||
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType;
|
||||
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType;
|
||||
|
||||
} // namespace Service::Mii::RawData
|
||||
|
||||
@@ -155,7 +155,13 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (events_interface.registered[event_id]) {
|
||||
return NvResult::BadParameter;
|
||||
const auto event_state = events_interface.status[event_id];
|
||||
if (event_state != EventState::Free) {
|
||||
LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event");
|
||||
events_interface.UnregisterEvent(event_id);
|
||||
} else {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
}
|
||||
events_interface.RegisterEvent(event_id);
|
||||
return NvResult::Success;
|
||||
|
||||
@@ -50,11 +50,11 @@ public:
|
||||
{1046, nullptr, "DisableFeaturesForReset"},
|
||||
{1047, nullptr, "NotifyApplicationDownloadStarted"},
|
||||
{1048, nullptr, "NotifyNetworkProfileCreated"},
|
||||
{1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"},
|
||||
{1062, nullptr, "GetStereoVisionRestriction"},
|
||||
{1063, nullptr, "SetStereoVisionRestriction"},
|
||||
{1064, nullptr, "ResetConfirmedStereoVisionPermission"},
|
||||
{1065, nullptr, "IsStereoVisionPermitted"},
|
||||
{1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"},
|
||||
{1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"},
|
||||
{1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"},
|
||||
{1064, &IParentalControlService::ResetConfirmedStereoVisionPermission, "ResetConfirmedStereoVisionPermission"},
|
||||
{1065, &IParentalControlService::IsStereoVisionPermitted, "IsStereoVisionPermitted"},
|
||||
{1201, nullptr, "UnlockRestrictionTemporarily"},
|
||||
{1202, nullptr, "UnlockSystemSettingsRestriction"},
|
||||
{1203, nullptr, "SetPinCode"},
|
||||
@@ -114,6 +114,7 @@ public:
|
||||
{2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"},
|
||||
{2016, nullptr, "RequestUpdateExemptionListAsync"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -131,6 +132,49 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto can_use = rp.Pop<bool>();
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use);
|
||||
|
||||
can_use_stereo_vision = can_use;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(can_use_stereo_vision);
|
||||
}
|
||||
|
||||
void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
bool can_use_stereo_vision = true;
|
||||
};
|
||||
|
||||
void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -149,7 +193,8 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext
|
||||
rb.PushIpcInterface<IParentalControlService>(system);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, const char* name)
|
||||
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
|
||||
const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)} {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
@@ -5,13 +5,116 @@
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/ptm/psm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::PSM {
|
||||
|
||||
class IPsmSession final : public ServiceFramework<IPsmSession> {
|
||||
public:
|
||||
explicit IPsmSession(Core::System& system_) : ServiceFramework{system_, "IPsmSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IPsmSession::BindStateChangeEvent, "BindStateChangeEvent"},
|
||||
{1, &IPsmSession::UnbindStateChangeEvent, "UnbindStateChangeEvent"},
|
||||
{2, &IPsmSession::SetChargerTypeChangeEventEnabled, "SetChargerTypeChangeEventEnabled"},
|
||||
{3, &IPsmSession::SetPowerSupplyChangeEventEnabled, "SetPowerSupplyChangeEventEnabled"},
|
||||
{4, &IPsmSession::SetBatteryVoltageStateChangeEventEnabled, "SetBatteryVoltageStateChangeEventEnabled"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
state_change_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system_.Kernel(), "IPsmSession::state_change_event");
|
||||
}
|
||||
|
||||
~IPsmSession() override = default;
|
||||
|
||||
void SignalChargerTypeChanged() {
|
||||
if (should_signal && should_signal_charger_type) {
|
||||
state_change_event.writable->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void SignalPowerSupplyChanged() {
|
||||
if (should_signal && should_signal_power_supply) {
|
||||
state_change_event.writable->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void SignalBatteryVoltageStateChanged() {
|
||||
if (should_signal && should_signal_battery_voltage) {
|
||||
state_change_event.writable->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PSM, "called");
|
||||
|
||||
should_signal = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(state_change_event.readable);
|
||||
}
|
||||
|
||||
void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PSM, "called");
|
||||
|
||||
should_signal = false;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto state = rp.Pop<bool>();
|
||||
LOG_DEBUG(Service_PSM, "called, state={}", state);
|
||||
|
||||
should_signal_charger_type = state;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto state = rp.Pop<bool>();
|
||||
LOG_DEBUG(Service_PSM, "called, state={}", state);
|
||||
|
||||
should_signal_power_supply = state;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto state = rp.Pop<bool>();
|
||||
LOG_DEBUG(Service_PSM, "called, state={}", state);
|
||||
|
||||
should_signal_battery_voltage = state;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
bool should_signal_charger_type{};
|
||||
bool should_signal_power_supply{};
|
||||
bool should_signal_battery_voltage{};
|
||||
bool should_signal{};
|
||||
Kernel::EventPair state_change_event;
|
||||
};
|
||||
|
||||
class PSM final : public ServiceFramework<PSM> {
|
||||
public:
|
||||
explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
|
||||
@@ -24,7 +127,7 @@ public:
|
||||
{4, nullptr, "IsBatteryChargingEnabled"},
|
||||
{5, nullptr, "AcquireControllerPowerSupply"},
|
||||
{6, nullptr, "ReleaseControllerPowerSupply"},
|
||||
{7, nullptr, "OpenSession"},
|
||||
{7, &PSM::OpenSession, "OpenSession"},
|
||||
{8, nullptr, "EnableEnoughPowerChargeEmulation"},
|
||||
{9, nullptr, "DisableEnoughPowerChargeEmulation"},
|
||||
{10, nullptr, "EnableFastBatteryCharging"},
|
||||
@@ -61,6 +164,14 @@ private:
|
||||
rb.PushEnum(charger_type);
|
||||
}
|
||||
|
||||
void OpenSession(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PSM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IPsmSession>(system);
|
||||
}
|
||||
|
||||
enum class ChargerType : u32 {
|
||||
Unplugged = 0,
|
||||
RegularCharger = 1,
|
||||
|
||||
@@ -255,6 +255,21 @@ void BSD::GetSockName(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||
}
|
||||
|
||||
void BSD::GetSockOpt(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const u32 level = rp.Pop<u32>();
|
||||
const auto optname = static_cast<OptName>(rp.Pop<u32>());
|
||||
|
||||
LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<s32>(-1);
|
||||
rb.PushEnum(Errno::NOTCONN);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void BSD::Listen(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
@@ -812,7 +827,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
|
||||
{14, &BSD::Connect, "Connect"},
|
||||
{15, &BSD::GetPeerName, "GetPeerName"},
|
||||
{16, &BSD::GetSockName, "GetSockName"},
|
||||
{17, nullptr, "GetSockOpt"},
|
||||
{17, &BSD::GetSockOpt, "GetSockOpt"},
|
||||
{18, &BSD::Listen, "Listen"},
|
||||
{19, nullptr, "Ioctl"},
|
||||
{20, &BSD::Fcntl, "Fcntl"},
|
||||
|
||||
@@ -125,6 +125,7 @@ private:
|
||||
void Connect(Kernel::HLERequestContext& ctx);
|
||||
void GetPeerName(Kernel::HLERequestContext& ctx);
|
||||
void GetSockName(Kernel::HLERequestContext& ctx);
|
||||
void GetSockOpt(Kernel::HLERequestContext& ctx);
|
||||
void Listen(Kernel::HLERequestContext& ctx);
|
||||
void Fcntl(Kernel::HLERequestContext& ctx);
|
||||
void SetSockOpt(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "core/hle/kernel/memory/page_table.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/reporter.h"
|
||||
#include "core/settings.h"
|
||||
@@ -360,55 +359,6 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
|
||||
SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
|
||||
}
|
||||
|
||||
void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const {
|
||||
if (!IsReportingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto timestamp = GetTimestamp();
|
||||
json out;
|
||||
|
||||
out["yuzu_version"] = GetYuzuVersionData();
|
||||
out["report_common"] =
|
||||
GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp);
|
||||
|
||||
out["log_destination"] =
|
||||
fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination));
|
||||
|
||||
auto json_messages = json::array();
|
||||
std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages),
|
||||
[](const Service::LM::LogMessage& message) {
|
||||
json out;
|
||||
out["is_head"] = fmt::format("{}", message.header.IsHeadLog());
|
||||
out["is_tail"] = fmt::format("{}", message.header.IsTailLog());
|
||||
out["pid"] = fmt::format("{:016X}", message.header.pid);
|
||||
out["thread_context"] =
|
||||
fmt::format("{:016X}", message.header.thread_context);
|
||||
out["payload_size"] = fmt::format("{:016X}", message.header.payload_size);
|
||||
out["flags"] = fmt::format("{:04X}", message.header.flags.Value());
|
||||
out["severity"] = fmt::format("{}", message.header.severity.Value());
|
||||
out["verbosity"] = fmt::format("{:02X}", message.header.verbosity);
|
||||
|
||||
auto fields = json::array();
|
||||
std::transform(message.fields.begin(), message.fields.end(),
|
||||
std::back_inserter(fields), [](const auto& kv) {
|
||||
json out;
|
||||
out["type"] = fmt::format("{}", kv.first);
|
||||
out["data"] =
|
||||
Service::LM::FormatField(kv.first, kv.second);
|
||||
return out;
|
||||
});
|
||||
|
||||
out["fields"] = std::move(fields);
|
||||
return out;
|
||||
});
|
||||
|
||||
out["log_messages"] = std::move(json_messages);
|
||||
|
||||
SaveToFile(std::move(out),
|
||||
GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp));
|
||||
}
|
||||
|
||||
void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
|
||||
std::string log_message) const {
|
||||
if (!IsReportingEnabled())
|
||||
|
||||
@@ -72,9 +72,6 @@ public:
|
||||
void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
|
||||
std::string log_message) const;
|
||||
|
||||
// Used by lm services
|
||||
void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const;
|
||||
|
||||
// Can be used anywhere to generate a backtrace and general info report at any point during
|
||||
// execution. Not intended to be used for anything other than debugging or testing.
|
||||
void SaveUserReport() const;
|
||||
|
||||
@@ -179,22 +179,22 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
||||
return ProcessMacroBind(argument);
|
||||
case MAXWELL3D_REG_INDEX(firmware[4]):
|
||||
return ProcessFirmwareCall4();
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
|
||||
return StartCBData(method);
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[0]):
|
||||
return ProcessCBBind(0);
|
||||
@@ -287,22 +287,22 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
|
||||
ProcessCBMultiData(method, base_start, amount);
|
||||
break;
|
||||
default:
|
||||
@@ -592,7 +592,7 @@ void Maxwell3D::ProcessCBData(u32 value) {
|
||||
}
|
||||
|
||||
void Maxwell3D::StartCBData(u32 method) {
|
||||
constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]);
|
||||
constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
|
||||
cb_data_state.start_pos = regs.const_buffer.cb_pos;
|
||||
cb_data_state.id = method - first_cb_data;
|
||||
cb_data_state.current = method;
|
||||
@@ -605,7 +605,7 @@ void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount
|
||||
if (cb_data_state.current != null_cb_data) {
|
||||
FinishCBData();
|
||||
}
|
||||
constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]);
|
||||
constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
|
||||
cb_data_state.start_pos = regs.const_buffer.cb_pos;
|
||||
cb_data_state.id = method - first_cb_data;
|
||||
cb_data_state.current = method;
|
||||
|
||||
@@ -1337,7 +1337,7 @@ public:
|
||||
u32 cb_address_high;
|
||||
u32 cb_address_low;
|
||||
u32 cb_pos;
|
||||
u32 cb_data[NumCBData];
|
||||
std::array<u32, NumCBData> cb_data;
|
||||
|
||||
GPUVAddr BufferAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
|
||||
@@ -55,7 +55,7 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
|
||||
OUTPUT
|
||||
${SPIRV_HEADER_FILE}
|
||||
COMMAND
|
||||
${GLSLANGVALIDATOR} -V ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE}
|
||||
${GLSLANGVALIDATOR} -V --quiet ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE}
|
||||
MAIN_DEPENDENCY
|
||||
${SOURCE_FILE}
|
||||
)
|
||||
|
||||
@@ -314,17 +314,29 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buf
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
|
||||
size_t remaining_size{size};
|
||||
size_t page_index{gpu_addr >> page_bits};
|
||||
size_t page_offset{gpu_addr & page_mask};
|
||||
while (remaining_size > 0) {
|
||||
const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
|
||||
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
|
||||
rasterizer->FlushRegion(*page_addr + page_offset, num_bytes);
|
||||
}
|
||||
++page_index;
|
||||
page_offset = 0;
|
||||
remaining_size -= num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
|
||||
std::vector<u8> tmp_buffer(size);
|
||||
ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
|
||||
WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
|
||||
}
|
||||
|
||||
void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
|
||||
std::size_t size) {
|
||||
std::vector<u8> tmp_buffer(size);
|
||||
ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size);
|
||||
WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
|
||||
// The output block must be flushed in case it has data modified from the GPU.
|
||||
// Fixes NPC geometry in Zombie Panic in Wonderland DX
|
||||
FlushRegion(gpu_dest_addr, size);
|
||||
WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
|
||||
}
|
||||
|
||||
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
|
||||
@@ -107,7 +107,6 @@ public:
|
||||
*/
|
||||
void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const;
|
||||
void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
|
||||
void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* IsGranularRange checks if a gpu region can be simply read with a pointer.
|
||||
@@ -131,6 +130,8 @@ private:
|
||||
void TryLockPage(PageEntry page_entry, std::size_t size);
|
||||
void TryUnlockPage(PageEntry page_entry, std::size_t size);
|
||||
|
||||
void FlushRegion(GPUVAddr gpu_addr, size_t size) const;
|
||||
|
||||
[[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
|
||||
return (gpu_addr >> page_bits) & page_table_mask;
|
||||
}
|
||||
|
||||
@@ -246,6 +246,7 @@ Device::Device()
|
||||
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
|
||||
|
||||
use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
|
||||
use_driver_cache = is_nvidia;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
|
||||
|
||||
@@ -120,6 +120,10 @@ public:
|
||||
return use_asynchronous_shaders;
|
||||
}
|
||||
|
||||
bool UseDriverCache() const {
|
||||
return use_driver_cache;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestPreciseBug();
|
||||
@@ -147,6 +151,7 @@ private:
|
||||
bool has_debugging_tool_attached{};
|
||||
bool use_assembly_shaders{};
|
||||
bool use_asynchronous_shaders{};
|
||||
bool use_driver_cache{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -159,6 +159,10 @@ std::unordered_set<GLenum> GetSupportedFormats() {
|
||||
|
||||
ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 unique_identifier,
|
||||
const ShaderIR& ir, const Registry& registry, bool hint_retrievable) {
|
||||
if (device.UseDriverCache()) {
|
||||
// Ignore hint retrievable if we are using the driver cache
|
||||
hint_retrievable = false;
|
||||
}
|
||||
const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
|
||||
LOG_INFO(Render_OpenGL, "{}", shader_id);
|
||||
|
||||
@@ -336,7 +340,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
|
||||
}
|
||||
|
||||
std::vector<ShaderDiskCachePrecompiled> gl_cache;
|
||||
if (!device.UseAssemblyShaders()) {
|
||||
if (!device.UseAssemblyShaders() && !device.UseDriverCache()) {
|
||||
// Only load precompiled cache when we are not using assembly shaders
|
||||
gl_cache = disk_cache.LoadPrecompiled();
|
||||
}
|
||||
@@ -356,8 +360,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
|
||||
std::atomic_bool gl_cache_failed = false;
|
||||
|
||||
const auto find_precompiled = [&gl_cache](u64 id) {
|
||||
return std::find_if(gl_cache.begin(), gl_cache.end(),
|
||||
[id](const auto& entry) { return entry.unique_identifier == id; });
|
||||
return std::ranges::find(gl_cache, id, &ShaderDiskCachePrecompiled::unique_identifier);
|
||||
};
|
||||
|
||||
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
|
||||
@@ -432,8 +435,8 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
|
||||
return;
|
||||
}
|
||||
|
||||
if (device.UseAssemblyShaders()) {
|
||||
// Don't store precompiled binaries for assembly shaders.
|
||||
if (device.UseAssemblyShaders() || device.UseDriverCache()) {
|
||||
// Don't store precompiled binaries for assembly shaders or when using the driver cache
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_type.h"
|
||||
@@ -877,7 +878,7 @@ private:
|
||||
|
||||
u32 binding = device.GetBaseBindings(stage).uniform_buffer;
|
||||
for (const auto& [index, info] : ir.GetConstantBuffers()) {
|
||||
const u32 num_elements = Common::AlignUp(info.GetSize(), 4) / 4;
|
||||
const u32 num_elements = Common::DivCeil(info.GetSize(), 4 * sizeof(u32));
|
||||
const u32 size = info.IsIndirect() ? MAX_CONSTBUFFER_ELEMENTS : num_elements;
|
||||
code.AddLine("layout (std140, binding = {}) uniform {} {{", binding++,
|
||||
GetConstBufferBlock(index));
|
||||
|
||||
@@ -110,8 +110,8 @@ VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_
|
||||
} // namespace Sampler
|
||||
|
||||
namespace {
|
||||
|
||||
enum : u32 { Attachable = 1, Storage = 2 };
|
||||
constexpr u32 Attachable = 1 << 0;
|
||||
constexpr u32 Storage = 1 << 1;
|
||||
|
||||
struct FormatTuple {
|
||||
VkFormat format; ///< Vulkan format
|
||||
@@ -222,22 +222,27 @@ constexpr bool IsZetaFormat(PixelFormat pixel_format) {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format) {
|
||||
ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples));
|
||||
|
||||
auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)];
|
||||
FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with_srgb,
|
||||
PixelFormat pixel_format) {
|
||||
ASSERT(static_cast<size_t>(pixel_format) < std::size(tex_format_tuples));
|
||||
FormatTuple tuple = tex_format_tuples[static_cast<size_t>(pixel_format)];
|
||||
if (tuple.format == VK_FORMAT_UNDEFINED) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented texture format with pixel format={}", pixel_format);
|
||||
return {VK_FORMAT_A8B8G8R8_UNORM_PACK32, true, true};
|
||||
return FormatInfo{VK_FORMAT_A8B8G8R8_UNORM_PACK32, true, true};
|
||||
}
|
||||
|
||||
// Use A8B8G8R8_UNORM on hardware that doesn't support ASTC natively
|
||||
if (!device.IsOptimalAstcSupported() && VideoCore::Surface::IsPixelFormatASTC(pixel_format)) {
|
||||
const bool is_srgb = VideoCore::Surface::IsPixelFormatSRGB(pixel_format);
|
||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
const bool is_srgb = with_srgb && VideoCore::Surface::IsPixelFormatSRGB(pixel_format);
|
||||
if (is_srgb) {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32;
|
||||
} else {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
tuple.usage |= Storage;
|
||||
}
|
||||
}
|
||||
const bool attachable = tuple.usage & Attachable;
|
||||
const bool storage = tuple.usage & Storage;
|
||||
const bool attachable = (tuple.usage & Attachable) != 0;
|
||||
const bool storage = (tuple.usage & Storage) != 0;
|
||||
|
||||
VkFormatFeatureFlags usage{};
|
||||
switch (format_type) {
|
||||
@@ -671,7 +676,7 @@ VkFrontFace FrontFace(Maxwell::FrontFace front_face) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkCullModeFlags CullFace(Maxwell::CullFace cull_face) {
|
||||
VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face) {
|
||||
switch (cull_face) {
|
||||
case Maxwell::CullFace::Front:
|
||||
return VK_CULL_MODE_FRONT_BIT;
|
||||
|
||||
@@ -35,7 +35,15 @@ struct FormatInfo {
|
||||
bool storage;
|
||||
};
|
||||
|
||||
FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format);
|
||||
/**
|
||||
* Returns format properties supported in the host
|
||||
* @param device Host device
|
||||
* @param format_type Type of image the buffer will use
|
||||
* @param with_srgb True when the format can be sRGB when converted to another format (ASTC)
|
||||
* @param pixel_format Guest pixel format to describe
|
||||
*/
|
||||
[[nodiscard]] FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with_srgb,
|
||||
PixelFormat pixel_format);
|
||||
|
||||
VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage);
|
||||
|
||||
@@ -55,7 +63,7 @@ VkBlendFactor BlendFactor(Maxwell::Blend::Factor factor);
|
||||
|
||||
VkFrontFace FrontFace(Maxwell::FrontFace front_face);
|
||||
|
||||
VkCullModeFlags CullFace(Maxwell::CullFace cull_face);
|
||||
VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face);
|
||||
|
||||
VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle);
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.codeSize = 0,
|
||||
.pCode = nullptr,
|
||||
};
|
||||
|
||||
std::vector<vk::ShaderModule> shader_modules;
|
||||
@@ -326,8 +327,8 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program,
|
||||
.rasterizerDiscardEnable =
|
||||
static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode =
|
||||
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE,
|
||||
.cullMode = static_cast<VkCullModeFlags>(
|
||||
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
|
||||
.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
|
||||
.depthBiasEnable = state.depth_bias_enable,
|
||||
.depthBiasConstantFactor = 0.0f,
|
||||
|
||||
@@ -355,14 +355,12 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
|
||||
SPIRVProgram program;
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
for (std::size_t index = 1; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
const auto program_enum = static_cast<Maxwell::ShaderProgram>(index);
|
||||
|
||||
// Skip stages that are not enabled
|
||||
if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const GPUVAddr gpu_addr = GetShaderAddress(maxwell3d, program_enum);
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
|
||||
Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get();
|
||||
@@ -372,12 +370,8 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
|
||||
const auto& entries = shader->GetEntries();
|
||||
program[stage] = {
|
||||
Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization),
|
||||
entries};
|
||||
|
||||
if (program_enum == Maxwell::ShaderProgram::VertexA) {
|
||||
// VertexB was combined with VertexA, so we skip the VertexB iteration
|
||||
++index;
|
||||
}
|
||||
entries,
|
||||
};
|
||||
|
||||
const u32 old_binding = specialization.base_binding;
|
||||
specialization.base_binding =
|
||||
|
||||
@@ -95,20 +95,12 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, info.format);
|
||||
VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
|
||||
info.size.width == info.size.height) {
|
||||
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
}
|
||||
if (info.type == ImageType::e3D) {
|
||||
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||
}
|
||||
[[nodiscard]] VkImageUsageFlags ImageUsageFlags(const MaxwellToVK::FormatInfo& info,
|
||||
PixelFormat format) {
|
||||
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
if (format_info.attachable) {
|
||||
switch (VideoCore::Surface::GetFormatType(info.format)) {
|
||||
if (info.attachable) {
|
||||
switch (VideoCore::Surface::GetFormatType(format)) {
|
||||
case VideoCore::Surface::SurfaceType::ColorTexture:
|
||||
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
break;
|
||||
@@ -120,9 +112,33 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
UNREACHABLE_MSG("Invalid surface type");
|
||||
}
|
||||
}
|
||||
if (format_info.storage) {
|
||||
if (info.storage) {
|
||||
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
/// Returns the preferred format for a VkImage
|
||||
[[nodiscard]] PixelFormat StorageFormat(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
return PixelFormat::A8B8G8R8_UNORM;
|
||||
default:
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
|
||||
const PixelFormat format = StorageFormat(info.format);
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format);
|
||||
VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
|
||||
info.size.width == info.size.height) {
|
||||
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
}
|
||||
if (info.type == ImageType::e3D) {
|
||||
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||
}
|
||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
|
||||
return VkImageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
@@ -130,17 +146,16 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
.flags = flags,
|
||||
.imageType = ConvertImageType(info.type),
|
||||
.format = format_info.format,
|
||||
.extent =
|
||||
{
|
||||
.width = info.size.width >> samples_x,
|
||||
.height = info.size.height >> samples_y,
|
||||
.depth = info.size.depth,
|
||||
},
|
||||
.extent{
|
||||
.width = info.size.width >> samples_x,
|
||||
.height = info.size.height >> samples_y,
|
||||
.depth = info.size.depth,
|
||||
},
|
||||
.mipLevels = static_cast<u32>(info.resources.levels),
|
||||
.arrayLayers = static_cast<u32>(info.resources.layers),
|
||||
.samples = ConvertSampleCount(info.num_samples),
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = usage,
|
||||
.usage = ImageUsageFlags(format_info, format),
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
@@ -209,10 +224,11 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
|
||||
[[nodiscard]] VkAttachmentDescription AttachmentDescription(const Device& device,
|
||||
const ImageView* image_view) {
|
||||
const auto pixel_format = image_view->format;
|
||||
using MaxwellToVK::SurfaceFormat;
|
||||
const PixelFormat pixel_format = image_view->format;
|
||||
return VkAttachmentDescription{
|
||||
.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
|
||||
.format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, pixel_format).format,
|
||||
.format = SurfaceFormat(device, FormatType::Optimal, true, pixel_format).format,
|
||||
.samples = image_view->Samples(),
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
@@ -868,11 +884,16 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
|
||||
}
|
||||
}
|
||||
const VkFormat vk_format =
|
||||
MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, format).format;
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||
const VkFormat vk_format = format_info.format;
|
||||
const VkImageViewUsageCreateInfo image_view_usage{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.usage = ImageUsageFlags(format_info, format),
|
||||
};
|
||||
const VkImageViewCreateInfo create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.pNext = &image_view_usage,
|
||||
.flags = 0,
|
||||
.image = image.Handle(),
|
||||
.viewType = VkImageViewType{},
|
||||
@@ -962,7 +983,7 @@ vk::ImageView ImageView::MakeDepthStencilView(VkImageAspectFlags aspect_mask) {
|
||||
.flags = 0,
|
||||
.image = image_handle,
|
||||
.viewType = ImageViewType(type),
|
||||
.format = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, format).format,
|
||||
.format = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format).format,
|
||||
.components{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
|
||||
@@ -72,7 +72,7 @@ struct TextureCacheRuntime {
|
||||
MemoryAllocator& memory_allocator;
|
||||
StagingBufferPool& staging_buffer_pool;
|
||||
BlitImageHelper& blit_image_helper;
|
||||
std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache;
|
||||
std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache{};
|
||||
|
||||
void Finish();
|
||||
|
||||
|
||||
@@ -129,6 +129,15 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
|
||||
.compiler_settings = compiler_settings,
|
||||
.registry = registry,
|
||||
.cpu_address = cpu_addr,
|
||||
.pp_cache = nullptr,
|
||||
.vk_device = nullptr,
|
||||
.scheduler = nullptr,
|
||||
.descriptor_pool = nullptr,
|
||||
.update_descriptor_queue = nullptr,
|
||||
.bindings{},
|
||||
.program{},
|
||||
.key{},
|
||||
.num_color_buffers = 0,
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
@@ -143,6 +152,15 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
|
||||
std::unique_lock lock(queue_mutex);
|
||||
pending_queue.push({
|
||||
.backend = Backend::Vulkan,
|
||||
.device = nullptr,
|
||||
.shader_type{},
|
||||
.uid = 0,
|
||||
.code{},
|
||||
.code_b{},
|
||||
.main_offset = 0,
|
||||
.compiler_settings{},
|
||||
.registry{},
|
||||
.cpu_address = 0,
|
||||
.pp_cache = pp_cache,
|
||||
.vk_device = &device,
|
||||
.scheduler = &scheduler,
|
||||
|
||||
@@ -465,14 +465,6 @@ public:
|
||||
return operands.size();
|
||||
}
|
||||
|
||||
NodeBlock& GetOperands() {
|
||||
return operands;
|
||||
}
|
||||
|
||||
const NodeBlock& GetOperands() const {
|
||||
return operands;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Node& operator[](std::size_t operand_index) const {
|
||||
return operands.at(operand_index);
|
||||
}
|
||||
|
||||
@@ -388,54 +388,9 @@ void ShaderIR::SetInternalFlagsFromInteger(NodeBlock& bb, Node value, bool sets_
|
||||
if (!sets_cc) {
|
||||
return;
|
||||
}
|
||||
switch (value->index()) {
|
||||
case 0: // Operation Node
|
||||
SearchOperands(bb, value);
|
||||
break;
|
||||
case 2: // General Purpose Node
|
||||
if (const auto* gpr = std::get_if<GprNode>(value.get())) {
|
||||
LOG_DEBUG(HW_GPU, "GprNode: index={}", gpr->GetIndex());
|
||||
Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value),
|
||||
Immediate(gpr->GetIndex()));
|
||||
SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), Immediate(0));
|
||||
SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop));
|
||||
LOG_WARNING(HW_GPU, "Node Type: {}", value->index());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderIR::SearchOperands(NodeBlock& nb, Node var) {
|
||||
const auto* op = std::get_if<OperationNode>(var.get());
|
||||
if (op == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (op->GetOperandsCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& operand : op->GetOperands()) {
|
||||
switch (operand->index()) {
|
||||
case 0: // Operation Node
|
||||
return SearchOperands(nb, operand);
|
||||
case 2: // General Purpose Node
|
||||
if (const auto* gpr = std::get_if<GprNode>(operand.get())) {
|
||||
LOG_DEBUG(HW_GPU, "Child GprNode: index={}", gpr->GetIndex());
|
||||
Node zerop = Operation(OperationCode::LogicalIEqual, std::move(operand),
|
||||
Immediate(gpr->GetIndex()));
|
||||
SetInternalFlag(nb, InternalFlag::Zero, std::move(zerop));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(HW_GPU, "Child Node Type: {}", operand->index());
|
||||
break;
|
||||
}
|
||||
}
|
||||
Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), Immediate(0));
|
||||
SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop));
|
||||
LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete");
|
||||
}
|
||||
|
||||
Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) {
|
||||
|
||||
@@ -346,9 +346,6 @@ private:
|
||||
/// Access a bindless image sampler.
|
||||
ImageEntry& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
|
||||
|
||||
/// Recursive Iteration over the OperationNode operands, searching for GprNodes.
|
||||
void SearchOperands(NodeBlock& nb, Node var);
|
||||
|
||||
/// Extracts a sequence of bits from a node
|
||||
Node BitfieldExtract(Node value, u32 offset, u32 bits);
|
||||
|
||||
|
||||
@@ -1193,25 +1193,35 @@ u32 MapSizeBytes(const ImageBase& image) {
|
||||
}
|
||||
}
|
||||
|
||||
using P = PixelFormat;
|
||||
static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0}, 0) ==
|
||||
0x7f8000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000);
|
||||
|
||||
static_assert(CalculateLevelSize(LevelInfo{{1920, 1080}, {0, 2, 0}, {1, 1}, 2, 0}, 0) == 0x7f8000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{32, 32}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 1, 0, 7) ==
|
||||
0x2afc00);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::ASTC_2D_12X12_UNORM, {8192, 4096, 1}, {0, 2, 0}, 1,
|
||||
0, 12) == 0x50d200);
|
||||
|
||||
static_assert(CalculateLevelOffset(P::R8_SINT, {1920, 1080}, {0, 2}, 1, 0, 7) == 0x2afc00);
|
||||
static_assert(CalculateLevelOffset(P::ASTC_2D_12X12_UNORM, {8192, 4096}, {0, 2}, 1, 0, 12) ==
|
||||
0x50d200);
|
||||
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 0) == 0);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 1) == 0x400000);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 2) == 0x500000);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 3) == 0x540000);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 4) == 0x550000);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 5) == 0x554000);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 6) == 0x555000);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 7) == 0x555400);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 8) == 0x555600);
|
||||
static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 9) == 0x555800);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
0) == 0);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
1) == 0x400000);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
2) == 0x500000);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
3) == 0x540000);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
4) == 0x550000);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
5) == 0x554000);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
6) == 0x555000);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
7) == 0x555400);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
8) == 0x555600);
|
||||
static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0,
|
||||
9) == 0x555800);
|
||||
|
||||
constexpr u32 ValidateLayerSize(PixelFormat format, u32 width, u32 height, u32 block_height,
|
||||
u32 tile_width_spacing, u32 level) {
|
||||
@@ -1221,13 +1231,14 @@ constexpr u32 ValidateLayerSize(PixelFormat format, u32 width, u32 height, u32 b
|
||||
return AlignLayerSize(offset, size, block, DefaultBlockHeight(format), tile_width_spacing);
|
||||
}
|
||||
|
||||
static_assert(ValidateLayerSize(P::ASTC_2D_12X12_UNORM, 8192, 4096, 2, 0, 12) == 0x50d800);
|
||||
static_assert(ValidateLayerSize(P::A8B8G8R8_UNORM, 1024, 1024, 2, 0, 10) == 0x556000);
|
||||
static_assert(ValidateLayerSize(P::BC3_UNORM, 128, 128, 2, 0, 8) == 0x6000);
|
||||
static_assert(ValidateLayerSize(PixelFormat::ASTC_2D_12X12_UNORM, 8192, 4096, 2, 0, 12) ==
|
||||
0x50d800);
|
||||
static_assert(ValidateLayerSize(PixelFormat::A8B8G8R8_UNORM, 1024, 1024, 2, 0, 10) == 0x556000);
|
||||
static_assert(ValidateLayerSize(PixelFormat::BC3_UNORM, 128, 128, 2, 0, 8) == 0x6000);
|
||||
|
||||
static_assert(ValidateLayerSize(P::A8B8G8R8_UNORM, 518, 572, 4, 3, 1) == 0x190000,
|
||||
static_assert(ValidateLayerSize(PixelFormat::A8B8G8R8_UNORM, 518, 572, 4, 3, 1) == 0x190000,
|
||||
"Tile width spacing is not working");
|
||||
static_assert(ValidateLayerSize(P::BC5_UNORM, 1024, 1024, 3, 4, 11) == 0x160000,
|
||||
static_assert(ValidateLayerSize(PixelFormat::BC5_UNORM, 1024, 1024, 3, 4, 11) == 0x160000,
|
||||
"Compressed tile width spacing is not working");
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -39,6 +39,7 @@ vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) {
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
||||
.pfnUserCallback = Callback,
|
||||
.pUserData = nullptr,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -84,21 +84,6 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsRDNA(std::string_view device_name, VkDriverIdKHR driver_id) {
|
||||
static constexpr std::array RDNA_DEVICES{
|
||||
"5700",
|
||||
"5600",
|
||||
"5500",
|
||||
"5300",
|
||||
};
|
||||
if (driver_id != VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
|
||||
return false;
|
||||
}
|
||||
return std::any_of(RDNA_DEVICES.begin(), RDNA_DEVICES.end(), [device_name](const char* name) {
|
||||
return device_name.find(name) != std::string_view::npos;
|
||||
});
|
||||
}
|
||||
|
||||
std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) {
|
||||
static constexpr std::array formats{
|
||||
VK_FORMAT_A8B8G8R8_UNORM_PACK32,
|
||||
@@ -436,13 +421,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
"Blacklisting RADV for VK_EXT_extended_dynamic state, likely due to a bug in yuzu");
|
||||
ext_extended_dynamic_state = false;
|
||||
}
|
||||
if (ext_extended_dynamic_state && IsRDNA(properties.deviceName, driver_id)) {
|
||||
// AMD's proprietary driver supports VK_EXT_extended_dynamic_state but on RDNA devices it
|
||||
// seems to cause stability issues
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"Blacklisting AMD proprietary on RDNA devices from VK_EXT_extended_dynamic_state");
|
||||
ext_extended_dynamic_state = false;
|
||||
if (is_float16_supported && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
|
||||
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
|
||||
LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math");
|
||||
is_float16_supported = false;
|
||||
}
|
||||
|
||||
graphics_queue = logical.GetQueue(graphics_family);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "applets/profile_select.h"
|
||||
#include "applets/software_keyboard.h"
|
||||
#include "applets/web_browser.h"
|
||||
#include "common/nvidia_flags.h"
|
||||
#include "configuration/configure_input.h"
|
||||
#include "configuration/configure_per_game.h"
|
||||
#include "configuration/configure_vibration.h"
|
||||
@@ -3023,6 +3024,8 @@ int main(int argc, char* argv[]) {
|
||||
MicroProfileOnThreadCreate("Frontend");
|
||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
||||
|
||||
Common::ConfigureNvidiaEnvironmentFlags();
|
||||
|
||||
// Init settings params
|
||||
QCoreApplication::setOrganizationName(QStringLiteral("yuzu team"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("yuzu"));
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/nvidia_flags.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -152,6 +153,8 @@ int main(int argc, char** argv) {
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
||||
|
||||
Common::ConfigureNvidiaEnvironmentFlags();
|
||||
|
||||
if (filepath.empty()) {
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user