mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-11 07:03:21 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17c3fb88f4 | |||
| 18de787ba1 | |||
| c746982201 |
-1
@@ -17,7 +17,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
|
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
|
||||||
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
|
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
|
||||||
NCE_INVALIDATION_GPU_READBACK("nce_invalidation_gpu_readback"),
|
NCE_INVALIDATION_GPU_READBACK("nce_invalidation_gpu_readback"),
|
||||||
NCE_RUNTIME_NRO_PATCH("nce_runtime_nro_patch"),
|
|
||||||
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
|
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
|
||||||
EMULATE_BGR565("emulate_bgr565"),
|
EMULATE_BGR565("emulate_bgr565"),
|
||||||
RESCALE_HACK("rescale_hack"),
|
RESCALE_HACK("rescale_hack"),
|
||||||
|
|||||||
-7
@@ -866,13 +866,6 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.nce_invalidation_gpu_readback_description
|
descriptionId = R.string.nce_invalidation_gpu_readback_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.NCE_RUNTIME_NRO_PATCH,
|
|
||||||
titleId = R.string.nce_runtime_nro_patch,
|
|
||||||
descriptionId = R.string.nce_runtime_nro_patch_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.FIX_BLOOM_EFFECTS,
|
BooleanSetting.FIX_BLOOM_EFFECTS,
|
||||||
|
|||||||
-1
@@ -555,7 +555,6 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
||||||
add(BooleanSetting.NCE_INVALIDATION_GPU_READBACK.key)
|
add(BooleanSetting.NCE_INVALIDATION_GPU_READBACK.key)
|
||||||
add(BooleanSetting.NCE_RUNTIME_NRO_PATCH.key)
|
|
||||||
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
||||||
add(BooleanSetting.EMULATE_BGR565.key)
|
add(BooleanSetting.EMULATE_BGR565.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||||
|
|||||||
@@ -595,8 +595,6 @@
|
|||||||
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
||||||
<string name="nce_invalidation_gpu_readback">GPU Readback Before NCE Invalidation</string>
|
<string name="nce_invalidation_gpu_readback">GPU Readback Before NCE Invalidation</string>
|
||||||
<string name="nce_invalidation_gpu_readback_description">Temporary. Fix Ender Magnolia (and other UE titles) crashes. May reduce performance. Improvement in progress.</string>
|
<string name="nce_invalidation_gpu_readback_description">Temporary. Fix Ender Magnolia (and other UE titles) crashes. May reduce performance. Improvement in progress.</string>
|
||||||
<string name="nce_runtime_nro_patch">Patch Runtime NROs for NCE</string>
|
|
||||||
<string name="nce_runtime_nro_patch_description">Temporary. Fix Biomutant boot. May impact performance. Improvement in progress.</string>
|
|
||||||
<string name="fix_bloom_effects">Fix Bloom Effects</string>
|
<string name="fix_bloom_effects">Fix Bloom Effects</string>
|
||||||
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
|
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
|
||||||
<string name="emulate_bgr565">Emulate BGR565</string>
|
<string name="emulate_bgr565">Emulate BGR565</string>
|
||||||
|
|||||||
@@ -641,8 +641,6 @@ struct Values {
|
|||||||
true};
|
true};
|
||||||
SwitchableSetting<bool> nce_invalidation_gpu_readback{
|
SwitchableSetting<bool> nce_invalidation_gpu_readback{
|
||||||
linkage, false, "nce_invalidation_gpu_readback", Category::RendererHacks};
|
linkage, false, "nce_invalidation_gpu_readback", Category::RendererHacks};
|
||||||
SwitchableSetting<bool> nce_runtime_nro_patch{
|
|
||||||
linkage, false, "nce_runtime_nro_patch", Category::RendererHacks};
|
|
||||||
SwitchableSetting<bool> async_presentation{linkage,
|
SwitchableSetting<bool> async_presentation{linkage,
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -45,6 +45,24 @@ Patcher::Patcher() : c(m_patch_instructions), c_pre(m_patch_instructions_pre) {
|
|||||||
WriteLoadContext(c_pre);
|
WriteLoadContext(c_pre);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Patcher::Patcher(std::span<const u8> text) : c(m_patch_instructions), c_pre(m_patch_instructions_pre) {
|
||||||
|
modules.emplace_back();
|
||||||
|
curr_patch = &modules.back();
|
||||||
|
const auto words = std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)};
|
||||||
|
for (u32 i = ModuleCodeIndex; i < static_cast<u32>(words.size()); ++i) {
|
||||||
|
const uintptr_t offset = i * sizeof(u32);
|
||||||
|
if (auto mrs = MRS{words[i]};
|
||||||
|
mrs.Verify() && mrs.GetRt() != 31 && (mrs.GetSystemReg() == TpidrEl0 || mrs.GetSystemReg() == TpidrroEl0)) {
|
||||||
|
BranchToPatch(offset);
|
||||||
|
const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0 : oaknut::SystemReg::TPIDR_EL0;
|
||||||
|
WriteMrsHandler(offset + sizeof(u32), oaknut::XReg{static_cast<int>(mrs.GetRt())}, src_reg);
|
||||||
|
} else if (auto msr = MSR{words[i]}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) {
|
||||||
|
BranchToPatch(offset);
|
||||||
|
WriteMsrHandler(offset + sizeof(u32), oaknut::XReg{static_cast<int>(msr.GetRt())});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Patcher::~Patcher() = default;
|
Patcher::~Patcher() = default;
|
||||||
|
|
||||||
bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code) {
|
bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code) {
|
||||||
@@ -182,6 +200,31 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::span<const u32> Patcher::RelocateRuntimeTls(
|
||||||
|
s64 patch_offset, std::vector<InstructionPatch>& out_patches) {
|
||||||
|
const auto& patch = modules.front();
|
||||||
|
const auto can_branch = [](s64 offset) {
|
||||||
|
return offset % 4 == 0 && offset >= -static_cast<s64>(MaxRelativeBranch) && offset < static_cast<s64>(MaxRelativeBranch);
|
||||||
|
};
|
||||||
|
out_patches.clear();
|
||||||
|
out_patches.reserve(patch.m_branch_to_patch_relocations.size());
|
||||||
|
for (const auto& rel : patch.m_branch_to_patch_relocations) {
|
||||||
|
const s64 offset = patch_offset + rel.patch_offset - static_cast<s64>(rel.module_offset);
|
||||||
|
if (!can_branch(offset)) {return {};}
|
||||||
|
u32 replacement;
|
||||||
|
oaknut::CodeGenerator rc{&replacement};
|
||||||
|
rc.B(offset);
|
||||||
|
out_patches.push_back({rel.module_offset, replacement});
|
||||||
|
}
|
||||||
|
for (const auto& rel : patch.m_branch_to_module_relocations) {
|
||||||
|
const s64 offset = static_cast<s64>(rel.module_offset) - patch_offset - rel.patch_offset;
|
||||||
|
if (!can_branch(offset)) {return {};}
|
||||||
|
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
|
||||||
|
rc.B(offset);
|
||||||
|
}
|
||||||
|
return m_patch_instructions;
|
||||||
|
}
|
||||||
|
|
||||||
bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines) {
|
bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines) {
|
||||||
const size_t patch_size = GetSectionSize();
|
const size_t patch_size = GetSectionSize();
|
||||||
const size_t pre_patch_size = GetPreSectionSize();
|
const size_t pre_patch_size = GetPreSectionSize();
|
||||||
|
|||||||
@@ -48,15 +48,22 @@ using ModuleTextAddress = u64;
|
|||||||
using PatchTextAddress = u64;
|
using PatchTextAddress = u64;
|
||||||
using EntryTrampolines = ::Common::unordered_map<ModuleTextAddress, PatchTextAddress>;
|
using EntryTrampolines = ::Common::unordered_map<ModuleTextAddress, PatchTextAddress>;
|
||||||
|
|
||||||
|
struct InstructionPatch {
|
||||||
|
u64 offset;
|
||||||
|
u32 replacement;
|
||||||
|
};
|
||||||
|
|
||||||
class Patcher {
|
class Patcher {
|
||||||
public:
|
public:
|
||||||
void SetModuleID(const ModuleID& id) {
|
void SetModuleID(const ModuleID& id) {
|
||||||
module_id = id;
|
module_id = id;
|
||||||
}
|
}
|
||||||
explicit Patcher();
|
explicit Patcher();
|
||||||
|
explicit Patcher(std::span<const u8> text);
|
||||||
~Patcher();
|
~Patcher();
|
||||||
bool PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code);
|
bool PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code);
|
||||||
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines);
|
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines);
|
||||||
|
std::span<const u32> RelocateRuntimeTls(s64 patch_offset, std::vector<InstructionPatch>& out_patches);
|
||||||
size_t GetSectionSize() const noexcept;
|
size_t GetSectionSize() const noexcept;
|
||||||
size_t GetPreSectionSize() const noexcept;
|
size_t GetPreSectionSize() const noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs/vfs_types.h"
|
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/bcat/bcat_types.h"
|
#include "core/hle/service/bcat/bcat_types.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "common/scope_exit.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
@@ -425,36 +426,48 @@ public:
|
|||||||
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size),
|
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size),
|
||||||
std::addressof(ro_size), std::addressof(rw_size),
|
std::addressof(ro_size), std::addressof(rw_size),
|
||||||
nro_info->base_address, nro_size, bss_size));
|
nro_info->base_address, nro_size, bss_size));
|
||||||
|
R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size));
|
||||||
#ifdef HAS_NCE
|
#ifdef HAS_NCE
|
||||||
if (Settings::values.nce_runtime_nro_patch.GetValue()) {
|
|
||||||
if (Settings::IsNceEnabled()) {
|
if (Settings::IsNceEnabled()) {
|
||||||
auto* process = context->GetProcess();
|
auto* process = context->GetProcess();
|
||||||
auto& memory = process->GetMemory();
|
auto& memory = process->GetMemory();
|
||||||
|
std::vector<u8> text(rx_size);
|
||||||
std::vector<u8> image(total_size);
|
memory.ReadBlock(nro_info->base_address, text.data(), text.size());
|
||||||
memory.ReadBlock(nro_info->base_address, image.data(), rx_size);
|
Core::NCE::Patcher patch{text};
|
||||||
|
|
||||||
Kernel::CodeSet::Segment code{.size = static_cast<u32>(rx_size)};
|
|
||||||
Core::NCE::Patcher patch;
|
|
||||||
patch.PatchText(image, code);
|
|
||||||
patch.RelocateAndCopy(nro_info->base_address, code, image, nullptr);
|
|
||||||
|
|
||||||
const u64 patch_address = nro_info->base_address + total_size;
|
|
||||||
const size_t patch_size = patch.GetSectionSize();
|
const size_t patch_size = patch.GetSectionSize();
|
||||||
|
if (patch_size != 0) {
|
||||||
constexpr auto permission = Kernel::Svc::MemoryPermission::ReadExecute;
|
constexpr auto permission = Kernel::Svc::MemoryPermission::ReadExecute;
|
||||||
|
|
||||||
auto* patch_memory = Kernel::KSharedMemory::Create(kernel);
|
auto* patch_memory = Kernel::KSharedMemory::Create(kernel);
|
||||||
|
SCOPE_EXIT { patch_memory->Close(kernel); };
|
||||||
R_TRY(patch_memory->Initialize(kernel, kernel.System().DeviceMemory(), process, permission, permission, patch_size));
|
R_TRY(patch_memory->Initialize(kernel, kernel.System().DeviceMemory(), process, permission, permission, patch_size));
|
||||||
std::memcpy(patch_memory->GetPointer(), image.data() + total_size, patch_size);
|
Kernel::KSharedMemory::Register(kernel, patch_memory);
|
||||||
|
const std::array<s64, 2> offsets{static_cast<s64>(total_size + Kernel::PageSize),-static_cast<s64>(patch_size + Kernel::PageSize)};
|
||||||
|
std::vector<Core::NCE::InstructionPatch> tls_patches;
|
||||||
|
std::span<const u32> instructions;
|
||||||
|
u64 patch_address = 0;
|
||||||
|
for (const s64 offset : offsets) {
|
||||||
|
instructions = patch.RelocateRuntimeTls(offset, tls_patches);
|
||||||
|
if (instructions.empty()) {continue;}
|
||||||
|
const u64 address = static_cast<u64>(static_cast<s64>(nro_info->base_address) + offset);
|
||||||
|
if (R_FAILED(patch_memory->Map(*process, address, patch_size, permission))) {continue;}
|
||||||
|
patch_address = address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
R_UNLESS(patch_address != 0, RO::ResultOutOfAddressSpace);
|
||||||
|
ON_RESULT_FAILURE {R_ASSERT(patch_memory->Unmap(*process, patch_address, patch_size));};
|
||||||
R_TRY(process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size));
|
R_TRY(process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size));
|
||||||
R_TRY(patch_memory->Map(*process, patch_address, patch_size, permission));
|
std::memcpy(patch_memory->GetPointer(), instructions.data(), instructions.size_bytes());
|
||||||
memory.WriteBlock(nro_info->base_address, image.data(), rx_size);
|
auto* ptr = reinterpret_cast<char*>(patch_address);
|
||||||
|
__builtin___clear_cache(ptr, ptr + instructions.size_bytes());
|
||||||
|
for (const auto& instruction : tls_patches) {
|
||||||
|
const u64 address = nro_info->base_address + instruction.offset;
|
||||||
|
memory.Write32(address, instruction.replacement);
|
||||||
|
ptr = reinterpret_cast<char*>(address);
|
||||||
|
__builtin___clear_cache(ptr, ptr + sizeof(u32));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Set NRO perms.
|
|
||||||
R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size,
|
|
||||||
rw_size + bss_size));
|
|
||||||
|
|
||||||
context->SetNroInfoInUse(nro_info, true);
|
context->SetNroInfoInUse(nro_info, true);
|
||||||
nro_info->code_size = rx_size + ro_size;
|
nro_info->code_size = rx_size + ro_size;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <SDL3/SDL_hidapi.h>
|
#include <SDL3/SDL_hidapi.h>
|
||||||
|
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include <vector>
|
||||||
#include "input_common/helpers/joycon_protocol/poller.h"
|
#include "input_common/helpers/joycon_protocol/poller.h"
|
||||||
|
|
||||||
namespace InputCommon::Joycon {
|
namespace InputCommon::Joycon {
|
||||||
|
|||||||
@@ -198,10 +198,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
|||||||
tr("GPU Readback Before NCE Invalidation"),
|
tr("GPU Readback Before NCE Invalidation"),
|
||||||
tr("Flushes GPU buffer data back to guest memory before NCE invalidates the whole "
|
tr("Flushes GPU buffer data back to guest memory before NCE invalidates the whole "
|
||||||
"guest page, preserving it while the CPU store proceeds."));
|
"guest page, preserving it while the CPU store proceeds."));
|
||||||
INSERT(Settings, nce_runtime_nro_patch,
|
|
||||||
tr("Patch Runtime NROs for NCE"),
|
|
||||||
tr("Applies NCE code patches to runtime-loaded NRO modules."
|
|
||||||
"This is required for some games to run correctly."));
|
|
||||||
INSERT(Settings, vsync_mode, tr("VSync Mode:"),
|
INSERT(Settings, vsync_mode, tr("VSync Mode:"),
|
||||||
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
|
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
|
||||||
"refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n"
|
"refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user