Compare commits

..

1 Commits

Author SHA1 Message Date
Fernando Sahmkow
f58ee3f15f ShaderDecompiler: Add a debug option to dump the game's shaders. 2022-01-04 02:39:00 +01:00
16 changed files with 160 additions and 125 deletions

View File

@@ -597,6 +597,7 @@ struct Values {
BasicSetting<std::string> program_args{std::string(), "program_args"};
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
BasicSetting<bool> dump_nso{false, "dump_nso"};
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
BasicSetting<bool> reporting_services{false, "reporting_services"};
BasicSetting<bool> quest_flag{false, "quest_flag"};

View File

@@ -317,8 +317,6 @@ struct System::Impl {
is_powered_on = false;
exit_lock = false;
gpu_core->NotifyShutdown();
services.reset();
service_manager.reset();
cheat_engine.reset();

View File

@@ -879,36 +879,10 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
if (!is_connected) {
return;
}
if (IsControllerSupported()) {
return;
}
Disconnect();
// Fallback fullkey controllers to Pro controllers
if (IsControllerFullkey() && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
SetNpadStyleIndex(NpadStyleIndex::ProController);
Connect();
return;
}
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
npad_type);
}
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
case NpadStyleIndex::GameCube:
case NpadStyleIndex::NES:
case NpadStyleIndex::SNES:
case NpadStyleIndex::N64:
case NpadStyleIndex::SegaGenesis:
return true;
default:
return false;
if (!IsControllerSupported()) {
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
npad_type);
Disconnect();
}
}

View File

@@ -320,12 +320,6 @@ private:
/// Set the params for TAS devices
void LoadTASParams();
/**
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller style is fullkey
*/
bool IsControllerFullkey(bool use_temporary_value = false) const;
/**
* Checks the current controller type against the supported_style_tag
* @param use_temporary_value If true tmp_npad_type will be used

View File

@@ -31,6 +31,8 @@ public:
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
virtual void Dump(u64 hash) = 0;
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
return sph;
}

View File

@@ -312,12 +312,6 @@ struct GPU::Impl {
cpu_context->MakeCurrent();
}
void NotifyShutdown() {
std::unique_lock lk{sync_mutex};
shutting_down.store(true, std::memory_order::relaxed);
sync_cv.notify_all();
}
/// Obtain the CPU Context
void ObtainContext() {
cpu_context->MakeCurrent();
@@ -865,10 +859,6 @@ void GPU::Start() {
impl->Start();
}
void GPU::NotifyShutdown() {
impl->NotifyShutdown();
}
void GPU::ObtainContext() {
impl->ObtainContext();
}

View File

@@ -232,9 +232,6 @@ public:
/// core timing events.
void Start();
/// Performs any additional necessary steps to shutdown GPU emulation.
void NotifyShutdown();
/// Obtain the CPU Context
void ObtainContext();

View File

@@ -425,6 +425,11 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (Settings::values.dump_shaders) {
env.Dump(key.unique_hashes[index]);
}
if (!uses_vertex_a || index != 1) {
// Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -511,8 +516,12 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
if (Settings::values.dump_shaders) {
env.Dump(key.Hash());
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
Shader::RuntimeInfo info;
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();

View File

@@ -517,6 +517,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (Settings::values.dump_shaders) {
env.Dump(key.unique_hashes[index]);
}
if (!uses_vertex_a || index != 1) {
// Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -613,6 +616,12 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
// Dump it before error.
if (Settings::values.dump_shaders) {
env.Dump(key.Hash());
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const std::vector<u32> code{EmitSPIRV(profile, program)};
device.SaveShader(code);

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <bit>
#include <filesystem>
#include <fstream>
#include <memory>
@@ -14,6 +15,7 @@
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "shader_recompiler/environment.h"
#include "video_core/engines/kepler_compute.h"
@@ -57,6 +59,47 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
}
}
static std::string_view StageToPrefix(Shader::Stage stage) {
switch (stage) {
case Shader::Stage::VertexB:
return "VB";
case Shader::Stage::TessellationControl:
return "TC";
case Shader::Stage::TessellationEval:
return "TE";
case Shader::Stage::Geometry:
return "GS";
case Shader::Stage::Fragment:
return "FS";
case Shader::Stage::Compute:
return "CS";
case Shader::Stage::VertexA:
return "VA";
default:
return "UK";
}
}
static void DumpImpl(u64 hash, const u64* code, u32 read_highest, u32 read_lowest,
u32 initial_offset, Shader::Stage stage) {
const auto shader_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
const auto base_dir{shader_dir / "shaders"};
if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories");
return;
}
const auto prefix = StageToPrefix(stage);
const auto name{base_dir / fmt::format("{}{:016x}.ash", prefix, hash)};
const size_t real_size = read_highest - read_lowest + initial_offset;
const size_t padding_needed = ((32 - (real_size % 32)) % 32);
std::fstream shader_file(name, std::ios::out | std::ios::binary);
const size_t jump_index = initial_offset / sizeof(u64);
shader_file.write(reinterpret_cast<const char*>(code + jump_index), real_size);
for (size_t i = 0; i < padding_needed; i++) {
shader_file.put(0);
}
}
GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
u32 start_address_)
: gpu_memory{&gpu_memory_}, program_base{program_base_} {
@@ -128,6 +171,10 @@ u64 GenericEnvironment::CalculateHash() const {
return Common::CityHash64(data.get(), size);
}
void GenericEnvironment::Dump(u64 hash) {
DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage);
}
void GenericEnvironment::Serialize(std::ofstream& file) const {
const u64 code_size{static_cast<u64>(CachedSize())};
const u64 num_texture_types{static_cast<u64>(texture_types.size())};
@@ -207,6 +254,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
u32 start_address_)
: GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
initial_offset = sizeof(sph);
gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask;
switch (program) {
case Maxwell::ShaderProgram::VertexA:
@@ -323,14 +371,20 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
if (stage == Shader::Stage::Compute) {
file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
.read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
initial_offset = 0;
} else {
file.read(reinterpret_cast<char*>(&sph), sizeof(sph));
initial_offset = sizeof(sph);
if (stage == Shader::Stage::Geometry) {
file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
}
}
}
void FileEnvironment::Dump(u64 [[maybe_unused]] hash) {
DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage);
}
u64 FileEnvironment::ReadInstruction(u32 address) {
if (address < read_lowest || address > read_highest) {
throw Shader::LogicError("Out of bounds address {}", address);

View File

@@ -57,6 +57,8 @@ public:
[[nodiscard]] u64 CalculateHash() const;
void Dump(u64 hash) override;
void Serialize(std::ofstream& file) const;
protected:
@@ -82,6 +84,7 @@ protected:
u32 cached_lowest = std::numeric_limits<u32>::max();
u32 cached_highest = 0;
u32 initial_offset = 0;
bool has_unbound_instructions = false;
};
@@ -149,6 +152,8 @@ public:
[[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
void Dump(u64 hash) override;
private:
std::unique_ptr<u64[]> code;
std::unordered_map<u32, Shader::TextureType> texture_types;
@@ -159,6 +164,7 @@ private:
u32 texture_bound{};
u32 read_lowest{};
u32 read_highest{};
u32 initial_offset{};
};
void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,

View File

@@ -400,66 +400,36 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
}
void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
const auto npad_style_set = system.HIDCore().GetSupportedStyleTag();
auto& pairs = index_controller_type_pairs[player_index];
pairs.clear();
emulated_controllers[player_index]->clear();
const auto add_item = [&](Core::HID::NpadStyleIndex controller_type,
const QString& controller_name) {
pairs.emplace_back(emulated_controllers[player_index]->count(), controller_type);
emulated_controllers[player_index]->addItem(controller_name);
};
pairs.emplace_back(emulated_controllers[player_index]->count(),
Core::HID::NpadStyleIndex::ProController);
emulated_controllers[player_index]->addItem(tr("Pro Controller"));
if (npad_style_set.fullkey == 1) {
add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller"));
pairs.emplace_back(emulated_controllers[player_index]->count(),
Core::HID::NpadStyleIndex::JoyconDual);
emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
pairs.emplace_back(emulated_controllers[player_index]->count(),
Core::HID::NpadStyleIndex::JoyconLeft);
emulated_controllers[player_index]->addItem(tr("Left Joycon"));
pairs.emplace_back(emulated_controllers[player_index]->count(),
Core::HID::NpadStyleIndex::JoyconRight);
emulated_controllers[player_index]->addItem(tr("Right Joycon"));
if (player_index == 0) {
pairs.emplace_back(emulated_controllers[player_index]->count(),
Core::HID::NpadStyleIndex::Handheld);
emulated_controllers[player_index]->addItem(tr("Handheld"));
}
if (npad_style_set.joycon_dual == 1) {
add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons"));
}
if (npad_style_set.joycon_left == 1) {
add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon"));
}
if (npad_style_set.joycon_right == 1) {
add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon"));
}
if (player_index == 0 && npad_style_set.handheld == 1) {
add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld"));
}
if (npad_style_set.gamecube == 1) {
add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller"));
}
// Disable all unsupported controllers
if (!Settings::values.enable_all_controllers) {
return;
}
if (npad_style_set.palma == 1) {
add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus"));
}
if (npad_style_set.lark == 1) {
add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller"));
}
if (npad_style_set.lucia == 1) {
add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller"));
}
if (npad_style_set.lagoon == 1) {
add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller"));
}
if (npad_style_set.lager == 1) {
add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis"));
}
pairs.emplace_back(emulated_controllers[player_index]->count(),
Core::HID::NpadStyleIndex::GameCube);
emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
}
Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex(

View File

@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
ui->dump_shaders->setEnabled(runtime_lock);
ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
ui->disable_macro_jit->setEnabled(runtime_lock);
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
ui->disable_loop_safety_checks->setEnabled(runtime_lock);
@@ -73,6 +75,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
Settings::values.dump_shaders = ui->dump_shaders->isChecked();
Settings::values.disable_shader_loop_safety_checks =
ui->disable_loop_safety_checks->isChecked();
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();

View File

@@ -105,6 +105,19 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="dump_shaders">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
</property>
<property name="text">
<string>Dump Game Shaders</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">

View File

@@ -907,63 +907,78 @@ void ConfigureInputPlayer::UpdateUI() {
}
void ConfigureInputPlayer::SetConnectableControllers() {
const auto npad_style_set = hid_core.GetSupportedStyleTag();
Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag();
index_controller_type_pairs.clear();
ui->comboControllerType->clear();
const auto add_item = [&](Core::HID::NpadStyleIndex controller_type,
const QString& controller_name) {
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), controller_type);
ui->comboControllerType->addItem(controller_name);
};
if (npad_style_set.fullkey == 1) {
add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::ProController);
ui->comboControllerType->addItem(tr("Pro Controller"));
}
if (npad_style_set.joycon_dual == 1) {
add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::JoyconDual);
ui->comboControllerType->addItem(tr("Dual Joycons"));
}
if (npad_style_set.joycon_left == 1) {
add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::JoyconLeft);
ui->comboControllerType->addItem(tr("Left Joycon"));
}
if (npad_style_set.joycon_right == 1) {
add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::JoyconRight);
ui->comboControllerType->addItem(tr("Right Joycon"));
}
if (player_index == 0 && npad_style_set.handheld == 1) {
add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::Handheld);
ui->comboControllerType->addItem(tr("Handheld"));
}
if (npad_style_set.gamecube == 1) {
add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::GameCube);
ui->comboControllerType->addItem(tr("GameCube Controller"));
}
// Disable all unsupported controllers
if (!Settings::values.enable_all_controllers) {
return;
}
if (npad_style_set.palma == 1) {
add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::Pokeball);
ui->comboControllerType->addItem(tr("Poke Ball Plus"));
}
if (npad_style_set.lark == 1) {
add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::NES);
ui->comboControllerType->addItem(tr("NES Controller"));
}
if (npad_style_set.lucia == 1) {
add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::SNES);
ui->comboControllerType->addItem(tr("SNES Controller"));
}
if (npad_style_set.lagoon == 1) {
add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::N64);
ui->comboControllerType->addItem(tr("N64 Controller"));
}
if (npad_style_set.lager == 1) {
add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis"));
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::SegaGenesis);
ui->comboControllerType->addItem(tr("Sega Genesis"));
}
}

View File

@@ -1547,8 +1547,6 @@ void GMainWindow::ShutdownGame() {
emu_thread->wait();
emu_thread = nullptr;
emulation_running = false;
discord_rpc->Update();
// The emulation is stopped, so closing the window or not does not matter anymore
@@ -1587,6 +1585,8 @@ void GMainWindow::ShutdownGame() {
emu_frametime_label->setVisible(false);
renderer_status_button->setEnabled(true);
emulation_running = false;
game_path.clear();
// When closing the game, destroy the GLWindow to clear the context after the game is closed