Compare commits

..

1 Commits

Author SHA1 Message Date
lizzie 382157249d [dynarmic] Add A64:SM4/SM3 tests
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-10-08 16:15:58 +00:00
28 changed files with 172 additions and 209 deletions
@@ -17,6 +17,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
NCE_INVALIDATION_GPU_READBACK("nce_invalidation_gpu_readback"),
NCE_RUNTIME_NRO_PATCH("nce_runtime_nro_patch"),
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
EMULATE_BGR565("emulate_bgr565"),
RESCALE_HACK("rescale_hack"),
@@ -26,6 +27,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
RENDERER_ASYNC_PRESENTATION("async_presentation"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
@@ -753,6 +753,13 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_asynchronous_gpu_emulation_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNC_PRESENTATION,
titleId = R.string.renderer_async_presentation,
descriptionId = R.string.renderer_async_presentation_description
)
)
put(
SingleChoiceSetting(
IntSetting.DMA_ACCURACY,
@@ -866,6 +873,13 @@ abstract class SettingsItem(
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(
SwitchSetting(
BooleanSetting.FIX_BLOOM_EFFECTS,
@@ -555,11 +555,13 @@ class SettingsFragmentPresenter(
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
add(BooleanSetting.NCE_INVALIDATION_GPU_READBACK.key)
add(BooleanSetting.NCE_RUNTIME_NRO_PATCH.key)
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
add(HeaderSetting(R.string.extensions))
@@ -574,6 +574,8 @@
<string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string>
<string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string>
<string name="renderer_asynchronous_gpu_emulation_description">يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت.</string>
<string name="renderer_async_presentation">عرض غير متزامن</string>
<string name="renderer_async_presentation_description">يمكن لهذه الحيلة أن تزيد من الأداء عن طريق نقل عملية العرض إلى خيط معالجة منفصل على حساب مشاكل الرسوميات.</string>
<string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string>
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
<string name="enable_buffer_history">تفعيل سجل التخزين المؤقت</string>
@@ -518,6 +518,8 @@
<string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
<string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string>
<string name="renderer_asynchronous_gpu_emulation_description">Este hack puede aumentar el rendimiento ejecutando la emulación de la GPU de forma asíncrona, a costa de problemas gráficos y un aumento en la tasa de fallos debido a operaciones relacionadas con la sincronización.</string>
<string name="renderer_async_presentation">Presentación asíncrona</string>
<string name="renderer_async_presentation_description">Este hack puede aumentar el rendimiento al mover la presentación a un hilo independiente de la CPU a costa de problemas gráficos.</string>
<string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
<string name="enable_buffer_history">Activar el historial del búfer</string>
@@ -563,6 +563,8 @@
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
<string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string>
<string name="renderer_asynchronous_gpu_emulation_description">Может повысить производительность за счёт асинхронного запуска эмуляции ГПУ, но ценой появления графических ошибок и увеличения частоты вылетов из-за операций, зависящих от синхронизации.</string>
<string name="renderer_async_presentation">Асинхронная презентация</string>
<string name="renderer_async_presentation_description">Может повысить производительность за счёт перемещения вывода кадров в отдельный поток ЦП, но ценой возникновения графических проблем.</string>
<string name="renderer_reactive_flushing">Реактивная очистка</string>
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
<string name="enable_buffer_history">Включить историю буфера</string>
@@ -481,6 +481,8 @@
<string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string>
<string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string>
<string name="renderer_asynchronous_gpu_emulation_description">Це обхідне рішення може покращити продуктивність завдяки асинхронному виконанню емуляції ГП, але спричинить проблеми з графікою та збільшить частоту збоїв чутливих до таймінгів операцій.</string>
<string name="renderer_async_presentation">Асинхронне подання</string>
<string name="renderer_async_presentation_description">Це обхідне рішення може покращити продуктивність завдяки переміщенню подання на окремий потік ЦП, але спричинить проблеми з графікою.</string>
<string name="renderer_reactive_flushing">Реактивне очищення</string>
<string name="renderer_reactive_flushing_description">Покращує точність рендерингу в деяких іграх.</string>
<string name="enable_buffer_history">Увімкнути історію буфера</string>
@@ -564,6 +564,8 @@
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (温控依然生效)。</string>
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
<string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。</string>
<string name="renderer_async_presentation">异步呈现</string>
<string name="renderer_async_presentation_description">此技巧通过将图形呈现移至独立的 CPU 线程来提升性能,但可能会带来图形显示问题。</string>
<string name="renderer_reactive_flushing">启用反应性刷新</string>
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
<string name="enable_buffer_history">启用缓冲区历史</string>
@@ -560,10 +560,12 @@
<string name="sync_memory_operations_description">確保計算和記憶體操作之間的資料一致性,此選項應能修復某些遊戲中的問題,但在某些情況下可能會降低效能。使用Unreal Engine 4的遊戲似乎受影響最大</string>
<string name="use_disk_shader_cache">磁碟著色器快取</string>
<string name="use_disk_shader_cache_description">將產生的著色器快取儲存至硬碟以減少中斷</string>
<string name="renderer_force_max_clock">強制使用最大時脈 (僅限Adreno)</string>
<string name="renderer_force_max_clock_description">強制 GPU 以可能的最大時脈執行 (熱溫限制仍會被套用)</string>
<string name="renderer_asynchronous_gpu_emulation">GPU 非同步模擬</string>
<string name="renderer_asynchronous_gpu_emulation_description">此修改可透過使用 GPU 非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率</string>
<string name="renderer_force_max_clock">強制使用最大時脈(僅限Adreno)</string>
<string name="renderer_force_max_clock_description">強制GPU以可能的最大時脈運作(熱溫限制仍會被套用)</string>
<string name="renderer_asynchronous_gpu_emulation">GPU非同步模擬</string>
<string name="renderer_asynchronous_gpu_emulation_description">此修改可透過使用GPU非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率</string>
<string name="renderer_async_presentation">非同步呈現</string>
<string name="renderer_async_presentation_description">此修改可以透過將渲染移至單獨的CPU執行緒來提升性能,但可能會導致圖形問題</string>
<string name="renderer_reactive_flushing">使用重新啟用排清</string>
<string name="renderer_reactive_flushing_description">犧牲效能以改善部分遊戲的轉譯準確度</string>
<string name="enable_buffer_history">啟用緩衝區歷史</string>
@@ -580,6 +580,8 @@
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
<string name="renderer_asynchronous_gpu_emulation">GPU async emulation</string>
<string name="renderer_asynchronous_gpu_emulation_description">This hack can increase performance by running GPU emulation asynchronously at the cost of graphical issues and increased crash rates by timing-related operations.</string>
<string name="renderer_async_presentation">Asynchronous presentation</string>
<string name="renderer_async_presentation_description">This hack can increase performance by moving presentation to a separate CPU thread at the cost of graphical issues.</string>
<string name="renderer_reactive_flushing">Use reactive flushing</string>
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
<string name="enable_buffer_history">Enable buffer history</string>
@@ -595,6 +597,8 @@
<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_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_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>
+17 -12
View File
@@ -171,13 +171,8 @@ struct Values {
SwitchableSetting<bool> enable_overlay{linkage, false, "enable_overlay", Category::LibraryApplet};
// Audio
SwitchableSetting<AudioEngine> sink_id{linkage,
#ifdef __ANDROID__
AudioEngine::Sdl3,
#else
AudioEngine::Auto,
#endif
"output_engine", Category::Audio, Specialization::RuntimeList};
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
Category::Audio, Specialization::RuntimeList};
SwitchableSetting<std::string> audio_output_device_id{
linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList};
SwitchableSetting<std::string> audio_input_device_id{
@@ -477,8 +472,13 @@ struct Values {
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
Category::Renderer};
SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage, true, "use_asynchronous_gpu_emulation",
Category::Renderer};
SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage,
#ifdef __ANDROID__
false,
#else
true,
#endif
"use_asynchronous_gpu_emulation", Category::Renderer};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
@@ -537,7 +537,7 @@ struct Values {
"vram_usage_mode",
Category::RendererAdvanced};
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Cpu,
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu,
"nvdec_emulation", Category::RendererAdvanced};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
@@ -641,9 +641,11 @@ struct Values {
true};
SwitchableSetting<bool> nce_invalidation_gpu_readback{
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,
#ifdef __ANDROID__
true,
false,
#else
false,
#endif
@@ -655,6 +657,9 @@ struct Values {
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
Category::RendererHacks};
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
Category::RendererHacks};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks};
@@ -931,7 +936,7 @@ struct Values {
// Network
Setting<std::string> network_interface{linkage, std::string(), "network_interface",
Category::Network};
SwitchableSetting<bool> airplane_mode{linkage, true, "airplane_mode", Category::Network};
SwitchableSetting<bool> airplane_mode{linkage, false, "airplane_mode", Category::Network};
// WebService
Setting<std::string> web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url",
-43
View File
@@ -45,24 +45,6 @@ Patcher::Patcher() : c(m_patch_instructions), c_pre(m_patch_instructions_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;
bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code) {
@@ -200,31 +182,6 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet
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) {
const size_t patch_size = GetSectionSize();
const size_t pre_patch_size = GetPreSectionSize();
-7
View File
@@ -48,22 +48,15 @@ using ModuleTextAddress = u64;
using PatchTextAddress = u64;
using EntryTrampolines = ::Common::unordered_map<ModuleTextAddress, PatchTextAddress>;
struct InstructionPatch {
u64 offset;
u32 replacement;
};
class Patcher {
public:
void SetModuleID(const ModuleID& id) {
module_id = id;
}
explicit Patcher();
explicit Patcher(std::span<const u8> text);
~Patcher();
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);
std::span<const u32> RelocateRuntimeTls(s64 patch_offset, std::vector<InstructionPatch>& out_patches);
size_t GetSectionSize() const noexcept;
size_t GetPreSectionSize() const noexcept;
+2 -1
View File
@@ -6,11 +6,12 @@
#pragma once
#include <functional>
#include <optional>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/kernel_helpers.h"
+23 -39
View File
@@ -7,7 +7,6 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include "common/scope_exit.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/cmif_serialization.h"
@@ -426,51 +425,36 @@ public:
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size),
std::addressof(ro_size), std::addressof(rw_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
if (Settings::IsNceEnabled()) {
auto* process = context->GetProcess();
auto& memory = process->GetMemory();
std::vector<u8> text(rx_size);
memory.ReadBlock(nro_info->base_address, text.data(), text.size());
Core::NCE::Patcher patch{text};
const size_t patch_size = patch.GetSectionSize();
if (patch_size != 0) {
if (Settings::values.nce_runtime_nro_patch.GetValue()) {
if (Settings::IsNceEnabled()) {
auto* process = context->GetProcess();
auto& memory = process->GetMemory();
std::vector<u8> image(total_size);
memory.ReadBlock(nro_info->base_address, image.data(), rx_size);
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();
constexpr auto permission = Kernel::Svc::MemoryPermission::ReadExecute;
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));
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);
const Result add_shared_memory_result = process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size);
if (R_FAILED(add_shared_memory_result)) {
R_ASSERT(patch_memory->Unmap(*process, patch_address, patch_size));
}
R_TRY(add_shared_memory_result);
std::memcpy(patch_memory->GetPointer(), instructions.data(), instructions.size_bytes());
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));
}
std::memcpy(patch_memory->GetPointer(), image.data() + total_size, patch_size);
R_TRY(process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size));
R_TRY(patch_memory->Map(*process, patch_address, patch_size, permission));
memory.WriteBlock(nro_info->base_address, image.data(), rx_size);
}
}
#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);
nro_info->code_size = rx_size + ro_size;
+63
View File
@@ -2632,3 +2632,66 @@ TEST_CASE("A64: Rounding", "[a64]") {
CHECK(jit.GetVector(5) == Vector{0x4000000040000000, 0x4000000040000000});
CHECK(jit.GetVector(6) == Vector{0x4000000040000000, 0x4000000040000000});
}
TEST_CASE("A64: SM4", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM4EKEY(V4.S4(), V0.S4(), V1.S4());
code.SM4E(V5.S4(), V0.S4(), V1.S4());
jit.SetPC(0);
jit.SetVector(0, {0xefcdab8967452301, 0x1032547698badcfe});
jit.SetVector(1, {0xefcdab8967452301, 0x1032547698badcfe});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(4) == Vector{0x486a0c2e00224466, 0x5c7e183a62402604});
CHECK(jit.GetVector(5) == Vector{0xc2e086a43c1e785a, 0xd7f593b104264062});
}
TEST_CASE("A64: SM3", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
SECTION("SM3SS1") {
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM3SS1(V0.S4(), V0.S4(), V1.S4(), V2.S4());
jit.SetPC(0);
jit.SetVector(0, {0xec4aff517369c667, 0x46e3fbf2abbacd29});
jit.SetVector(1, {0x8de7e81bf854c27c, 0x9ac99f33632e5a76});
jit.SetVector(2, {0x5aa35831b70d3266, 0xd45ee95817055d25});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(0) == Vector{0x0000000000000000, 0x73d67cd700000000});
}
SECTION("SM3PARTW1") {
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM3PARTW1(V0.S4(), V0.S4(), V1.S4(), V2.S4());
jit.SetPC(0);
jit.SetVector(0, {0xec4aff517369c667, 0x46e3fbf2abbacd29});
jit.SetVector(1, {0x8de7e81bf854c27c, 0x9ac99f33632e5a76});
jit.SetVector(2, {0x5aa35831b70d3266, 0xd45ee95817055d25});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(0) == Vector{0x653c64995693280c, 0xabcc7db97adebf12});
}
SECTION("SM3PARTW2") {
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM3PARTW2(V0.S4(), V0.S4(), V1.S4(), V2.S4());
jit.SetPC(0);
jit.SetVector(0, {0xec4aff517369c667, 0x46e3fbf2abbacd29});
jit.SetVector(1, {0x8de7e81bf854c27c, 0x9ac99f33632e5a76});
jit.SetVector(2, {0x5aa35831b70d3266, 0xd45ee95817055d25});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(0) == Vector{0x30010fe70da43740, 0x6702425b4a3a05d4});
}
}
+1 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -6,8 +6,6 @@
#pragma once
#include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -13,8 +13,6 @@
#include <array>
#include <functional>
#include <vector>
#include <SDL3/SDL_hidapi.h>
#include "common/bit_field.h"
@@ -4,7 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <vector>
#include "common/logging.h"
#include "input_common/helpers/joycon_protocol/poller.h"
namespace InputCommon::Joycon {
@@ -198,6 +198,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("GPU Readback Before NCE Invalidation"),
tr("Flushes GPU buffer data back to guest memory before NCE invalidates the whole "
"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:"),
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"
@@ -275,6 +279,12 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
"quality and performance consistency in some games."));
INSERT(Settings, fix_bloom_effects, tr("Fix bloom effects"), tr("Removes bloom in Burnout."));
INSERT(Settings, rescale_hack, tr("Enable Legacy Rescale Pass"),
tr("May fix rescale issues in some games by relying on behavior from the previous "
"implementation.\n"
"Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and "
"grey texture flicker on Nvidia GPUs in Luigis Mansion 3."));
// Renderer (Extensions)
INSERT(Settings, dyna_state, tr("Extended Dynamic State"),
tr("Controls the number of features that can be used in Extended Dynamic State.\n"
@@ -301,7 +301,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
Optimization::TexturePass(env, program, normalized_host_info);
if (Settings::values.resolution_info.active) {
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
Optimization::RescalingPass(program);
}
Optimization::DeadCodeEliminationPass(program);
-4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -31,7 +28,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
void VendorWorkaroundPass(IR::Program& program);
void VerificationPass(const IR::Program& program);
bool HasBrokenPattern(const IR::Program& program);
// Dual Vertex
void VertexATransformPass(IR::Program& program);
@@ -67,11 +67,18 @@ void VisitMark(IR::Block& block, IR::Inst& inst) {
if (must_patch_outside) {
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
IR::IREmitter ir{block, it};
IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_bitcast, up_factor)};
inst.ReplaceUsesWith(converted);
if (Settings::values.rescale_hack.GetValue()) {
const IR::F32 new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_inst, up_factor)};
inst.ReplaceUsesWith(converted);
} else {
IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_bitcast, up_factor)};
inst.ReplaceUsesWith(converted);
}
}
break;
}
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -79,44 +76,4 @@ void VendorWorkaroundPass(IR::Program& program) {
}
}
bool HasBrokenPattern(const IR::Program& program) {
if (program.stage != Stage::Fragment) {
return false;
}
for (const IR::Block* block : program.post_order_blocks) {
for (const IR::Inst& inst : block->Instructions()) {
switch (inst.GetOpcode()) {
case IR::Opcode::ShuffleIndex:
case IR::Opcode::ShuffleUp:
case IR::Opcode::ShuffleDown:
case IR::Opcode::ShuffleButterfly:
break;
default:
continue;
}
const IR::Value shuffle_arg{inst.Arg(0)};
if (shuffle_arg.IsImmediate()) {
continue;
}
const IR::Inst* bitcast{shuffle_arg.InstRecursive()};
if (!bitcast || bitcast->GetOpcode() != IR::Opcode::BitCastU32F32) {
continue;
}
const IR::Value bitcast_arg{bitcast->Arg(0)};
if (bitcast_arg.IsImmediate()) {
continue;
}
const IR::Inst* attribute{bitcast_arg.InstRecursive()};
if (!attribute || attribute->GetOpcode() != IR::Opcode::GetAttribute) {
continue;
}
const IR::Attribute attr{attribute->Arg(0).Attribute()};
if (attr == IR::Attribute::PositionX || attr == IR::Attribute::PositionY) {
return true;
}
}
}
return false;
}
} // namespace Shader::Optimization
@@ -217,15 +217,13 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
scheduler.RequestOutsideRenderPassOperationContext();
blit_swapchain.DrawToFrame(device, rasterizer, frame, framebuffers,
render_window.GetFramebufferLayout(),
present_manager.SwapchainImageCount(),
render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
swapchain.GetImageViewFormat());
#ifdef HAS_LSFG
void(frame_gen.WantedGenerations(present_manager.MaxExtraFrames()));
frame_gen.Process(device, frame, present_manager.SwapchainImageFormat(),
GuestExtent(framebuffers));
frame_gen.Process(device, frame, swapchain.GetImageFormat(), GuestExtent(framebuffers));
const size_t generated_frames = frame_gen.GeneratedFrameCount();
for (size_t generation = 0; generation < generated_frames; ++generation) {
@@ -23,7 +23,6 @@
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate_program.h"
#include "shader_recompiler/ir_opt/passes.h"
#include "shader_recompiler/program_header.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
@@ -827,11 +826,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
}
if (device.GetDriverID() != VK_DRIVER_ID_NVIDIA_PROPRIETARY && Shader::Optimization::HasBrokenPattern(programs[index])) {
LOG_WARNING(Render_Vulkan, "0x{:016x}: Skipping shader with known issues", key.unique_hashes[index]);
return nullptr;
}
if (Settings::values.dump_guest_shaders) {
env.Dump(hash, key.unique_hashes[index]);
}
@@ -23,7 +23,6 @@ namespace Vulkan {
namespace {
constexpr size_t MAX_FRAMES_IN_FLIGHT = 7;
constexpr u32 MAX_PRESENT_ATTEMPTS = 3;
#ifdef HAS_LSFG
static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS);
#endif
@@ -373,33 +372,12 @@ void PresentManager::SetImageCount() {
#else
image_count = std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT);
#endif
swapchain_image_count = swapchain.GetImageCount();
swapchain_image_format = swapchain.GetImageFormat();
}
void PresentManager::DiscardFrame(Frame* frame) {
static constexpr VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
const VkSemaphore render_ready = *frame->render_ready;
const VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 1U,
.pWaitSemaphores = &render_ready,
.pWaitDstStageMask = &wait_stage,
.commandBufferCount = 0U,
.pCommandBuffers = nullptr,
.signalSemaphoreCount = 0U,
.pSignalSemaphores = nullptr,
};
std::scoped_lock submit_lock{scheduler.submit_mutex};
void(device.GetGraphicsQueue().Submit(submit_info, *frame->present_done));
}
void PresentManager::CopyToSwapchain(Frame* frame) {
bool requires_recreation = false;
for (u32 attempt = 0; attempt < MAX_PRESENT_ATTEMPTS; ++attempt) {
while (true) {
try {
// Recreate surface and swapchain if needed.
if (requires_recreation) {
@@ -419,8 +397,6 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
requires_recreation = true;
}
}
DiscardFrame(frame);
}
void PresentManager::CopyToSwapchainImpl(Frame* frame) {
@@ -6,7 +6,6 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <boost/container/deque.hpp>
@@ -69,9 +68,6 @@ public:
/// How many additional frames can be queued without stalling the render thread
[[nodiscard]] size_t MaxExtraFrames() const;
[[nodiscard]] std::size_t SwapchainImageCount() const { return swapchain_image_count; }
[[nodiscard]] VkFormat SwapchainImageFormat() const { return swapchain_image_format; }
private:
void PresentThread(std::stop_token token);
@@ -81,8 +77,6 @@ private:
void RecreateSwapchain(Frame* frame);
void DiscardFrame(Frame* frame);
void SetImageCount();
private:
@@ -106,9 +100,7 @@ private:
bool blit_supported;
bool storage_supported;
bool use_present_thread;
std::atomic<std::size_t> image_count{};
std::atomic<std::size_t> swapchain_image_count{};
std::atomic<VkFormat> swapchain_image_format{};
std::size_t image_count{};
};
} // namespace Vulkan