mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 05:28:56 +02:00
Compare commits
9 commits
66a161dbea
...
8308d1a466
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8308d1a466 | ||
|
|
18daea9126 | ||
|
|
bf70b7433e | ||
|
|
47ed86d3e2 | ||
|
|
2aea7f9584 | ||
|
|
59b0e66722 | ||
|
|
8de1dd151f | ||
|
|
98604d369a | ||
|
|
4337135910 |
18 changed files with 284 additions and 216 deletions
|
|
@ -38,13 +38,13 @@ set(GIT_DESC ${BUILD_VERSION})
|
|||
# Auto-updater metadata! Must somewhat mirror GitHub API endpoint
|
||||
if (NIGHTLY_BUILD)
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
|
||||
set(BUILD_AUTO_UPDATE_API "api.github.com")
|
||||
set(BUILD_AUTO_UPDATE_API "https://api.github.com")
|
||||
set(BUILD_AUTO_UPDATE_API_PATH "/repos/")
|
||||
set(BUILD_AUTO_UPDATE_REPO "Eden-CI/Nightly")
|
||||
set(REPO_NAME "Eden Nightly")
|
||||
else()
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
|
||||
set(BUILD_AUTO_UPDATE_API "git.eden-emu.dev")
|
||||
set(BUILD_AUTO_UPDATE_API "https://git.eden-emu.dev")
|
||||
set(BUILD_AUTO_UPDATE_API_PATH "/api/v1/repos/")
|
||||
set(BUILD_AUTO_UPDATE_REPO "eden-emu/eden")
|
||||
set(REPO_NAME "Eden")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -5,15 +8,18 @@ package org.yuzu.yuzu_emu.fragments
|
|||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.InstallableActions
|
||||
|
||||
class ContentTypeSelectionDialogFragment : DialogFragment() {
|
||||
private val addonViewModel: AddonViewModel by activityViewModels()
|
||||
|
|
@ -23,6 +29,52 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
|
|||
|
||||
private var selectedItem = 0
|
||||
|
||||
private val installGameUpdateLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { documents ->
|
||||
if (documents.isEmpty()) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val game = addonViewModel.game
|
||||
if (game == null) {
|
||||
installContent(documents)
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.verifying_content,
|
||||
false
|
||||
) { _, _ ->
|
||||
var updatesMatchProgram = true
|
||||
for (document in documents) {
|
||||
val valid = NativeLibrary.doesUpdateMatchProgram(
|
||||
game.programId,
|
||||
document.toString()
|
||||
)
|
||||
if (!valid) {
|
||||
updatesMatchProgram = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
requireActivity().runOnUiThread {
|
||||
if (updatesMatchProgram) {
|
||||
installContent(documents)
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
titleId = R.string.content_install_notice,
|
||||
descriptionId = R.string.content_install_notice_description,
|
||||
positiveAction = { installContent(documents) },
|
||||
negativeAction = {}
|
||||
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
return@newInstance Any()
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val launchOptions =
|
||||
arrayOf(getString(R.string.updates_and_dlc), getString(R.string.mods_and_cheats))
|
||||
|
|
@ -31,12 +83,11 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
|
|||
selectedItem = savedInstanceState.getInt(SELECTED_ITEM)
|
||||
}
|
||||
|
||||
val mainActivity = requireActivity() as MainActivity
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.select_content_type)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||
when (selectedItem) {
|
||||
0 -> mainActivity.installGameUpdate.launch(arrayOf("*/*"))
|
||||
0 -> installGameUpdateLauncher.launch(arrayOf("*/*"))
|
||||
else -> {
|
||||
if (!preferences.getBoolean(MOD_NOTICE_SEEN, false)) {
|
||||
preferences.edit().putBoolean(MOD_NOTICE_SEEN, true).apply()
|
||||
|
|
@ -47,7 +98,7 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
.setSingleChoiceItems(launchOptions, 0) { _: DialogInterface, i: Int ->
|
||||
.setSingleChoiceItems(launchOptions, selectedItem) { _: DialogInterface, i: Int ->
|
||||
selectedItem = i
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
|
|
@ -65,4 +116,13 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
|
|||
private const val SELECTED_ITEM = "SelectedItem"
|
||||
private const val MOD_NOTICE_SEEN = "ModNoticeSeen"
|
||||
}
|
||||
|
||||
private fun installContent(documents: List<Uri>) {
|
||||
InstallableActions.installContent(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
addonViewModel = addonViewModel,
|
||||
documents = documents
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,8 +184,7 @@ if(ARCHITECTURE_x86_64)
|
|||
x64/native_clock.h
|
||||
x64/rdtsc.cpp
|
||||
x64/rdtsc.h
|
||||
x64/xbyak_abi.h
|
||||
x64/xbyak_util.h)
|
||||
x64/xbyak.h)
|
||||
target_link_libraries(common PRIVATE xbyak::xbyak)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -410,39 +410,48 @@ struct Impl {
|
|||
|
||||
// Constructor shall NOT depend upon Settings() or whatever
|
||||
// it's ran at global static ctor() time... so BE CAREFUL MFER!
|
||||
static Common::Log::Impl logging_instance{};
|
||||
static std::optional<Common::Log::Impl> logging_instance{};
|
||||
|
||||
void Initialize() {
|
||||
logging_instance.filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
if (logging_instance) {
|
||||
LOG_WARNING(Log, "Reinitializing logging backend");
|
||||
} else {
|
||||
logging_instance.emplace();
|
||||
logging_instance->filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
#ifndef __OPENORBIS__
|
||||
using namespace Common::FS;
|
||||
const auto& log_dir = GetEdenPath(EdenPath::LogDir);
|
||||
void(CreateDir(log_dir));
|
||||
logging_instance.file_backend.emplace(log_dir / LOG_FILE);
|
||||
using namespace Common::FS;
|
||||
const auto& log_dir = GetEdenPath(EdenPath::LogDir);
|
||||
void(CreateDir(log_dir));
|
||||
logging_instance->file_backend.emplace(log_dir / LOG_FILE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Start() {
|
||||
logging_instance.StartBackendThread();
|
||||
if (logging_instance)
|
||||
logging_instance->StartBackendThread();
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
logging_instance.StopBackendThread();
|
||||
if (logging_instance)
|
||||
logging_instance->StopBackendThread();
|
||||
}
|
||||
|
||||
void SetGlobalFilter(const Filter& filter) {
|
||||
logging_instance.filter = filter;
|
||||
if (logging_instance)
|
||||
logging_instance->filter = filter;
|
||||
}
|
||||
|
||||
void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
logging_instance.color_console_backend.enabled = enabled;
|
||||
if (logging_instance)
|
||||
logging_instance->color_console_backend.enabled = enabled;
|
||||
}
|
||||
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
|
||||
if (logging_instance.filter.CheckMessage(log_class, log_level)) {
|
||||
logging_instance.message_queue.EmplaceWait(Entry{
|
||||
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
|
||||
logging_instance->message_queue.EmplaceWait(Entry{
|
||||
.message = fmt::vformat(format, args),
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance.time_origin),
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = TrimSourcePath(filename),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,37 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/assert.h"
|
||||
|
||||
// xbyak hates human beings
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
constexpr size_t RegToIndex(const Xbyak::Reg& reg) {
|
||||
|
|
@ -174,12 +198,13 @@ inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alig
|
|||
rsp_alignment -= subtraction;
|
||||
subtraction += rsp_alignment & 0xF;
|
||||
|
||||
return ABIFrameInfo{static_cast<s32>(subtraction),
|
||||
static_cast<s32>(subtraction - xmm_base_subtraction)};
|
||||
return ABIFrameInfo{
|
||||
s32(subtraction),
|
||||
s32(subtraction - xmm_base_subtraction)
|
||||
};
|
||||
}
|
||||
|
||||
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
|
||||
|
||||
for (size_t i = 0; i < regs.size(); ++i) {
|
||||
|
|
@ -202,8 +227,7 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::b
|
|||
return ABI_SHADOW_SPACE;
|
||||
}
|
||||
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
|
||||
|
||||
for (size_t i = 0; i < regs.size(); ++i) {
|
||||
|
|
@ -226,4 +250,38 @@ inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bits
|
|||
}
|
||||
}
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
const u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
const u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
|
|
@ -128,14 +127,83 @@ struct Memory::Impl {
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
|
||||
auto const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
return paddr ? system.DeviceMemory().GetPointer<u8>(paddr + vaddr) : nullptr;
|
||||
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
|
||||
Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u8* GetPointerFromDebugMemory(u64 vaddr) const {
|
||||
auto const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
return paddr ? system.DeviceMemory().GetPointer<u8>(paddr + vaddr) : nullptr;
|
||||
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
|
||||
const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr != 0)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
return {};
|
||||
}
|
||||
|
||||
u8 Read8(const Common::ProcessAddress addr) {
|
||||
return Read<u8>(addr);
|
||||
}
|
||||
|
||||
u16 Read16(const Common::ProcessAddress addr) {
|
||||
if ((addr & 1) == 0) {
|
||||
return Read<u16_le>(addr);
|
||||
} else {
|
||||
const u32 a{Read<u8>(addr)};
|
||||
const u32 b{Read<u8>(addr + sizeof(u8))};
|
||||
return static_cast<u16>((b << 8) | a);
|
||||
}
|
||||
}
|
||||
|
||||
u32 Read32(const Common::ProcessAddress addr) {
|
||||
if ((addr & 3) == 0) {
|
||||
return Read<u32_le>(addr);
|
||||
} else {
|
||||
const u32 a{Read16(addr)};
|
||||
const u32 b{Read16(addr + sizeof(u16))};
|
||||
return (b << 16) | a;
|
||||
}
|
||||
}
|
||||
|
||||
u64 Read64(const Common::ProcessAddress addr) {
|
||||
if ((addr & 7) == 0) {
|
||||
return Read<u64_le>(addr);
|
||||
} else {
|
||||
const u32 a{Read32(addr)};
|
||||
const u32 b{Read32(addr + sizeof(u32))};
|
||||
return (static_cast<u64>(b) << 32) | a;
|
||||
}
|
||||
}
|
||||
|
||||
void Write8(const Common::ProcessAddress addr, const u8 data) {
|
||||
Write<u8>(addr, data);
|
||||
}
|
||||
|
||||
void Write16(const Common::ProcessAddress addr, const u16 data) {
|
||||
if ((addr & 1) == 0) {
|
||||
Write<u16_le>(addr, data);
|
||||
} else {
|
||||
Write<u8>(addr, static_cast<u8>(data));
|
||||
Write<u8>(addr + sizeof(u8), static_cast<u8>(data >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
void Write32(const Common::ProcessAddress addr, const u32 data) {
|
||||
if ((addr & 3) == 0) {
|
||||
Write<u32_le>(addr, data);
|
||||
} else {
|
||||
Write16(addr, static_cast<u16>(data));
|
||||
Write16(addr + sizeof(u16), static_cast<u16>(data >> 16));
|
||||
}
|
||||
}
|
||||
|
||||
void Write64(const Common::ProcessAddress addr, const u64 data) {
|
||||
if ((addr & 7) == 0) {
|
||||
Write<u64_le>(addr, data);
|
||||
} else {
|
||||
Write32(addr, static_cast<u32>(data));
|
||||
Write32(addr + sizeof(u32), static_cast<u32>(data >> 32));
|
||||
}
|
||||
}
|
||||
|
||||
bool WriteExclusive8(const Common::ProcessAddress addr, const u8 data, const u8 expected) {
|
||||
|
|
@ -590,7 +658,7 @@ struct Memory::Impl {
|
|||
}
|
||||
|
||||
template<typename F, typename G>
|
||||
[[nodiscard]] inline u8* GetPointerImpl(u64 vaddr, F&& on_unmapped, G&& on_rasterizer) const {
|
||||
[[nodiscard]] u8* GetPointerImpl(u64 vaddr, F&& on_unmapped, G&& on_rasterizer) const {
|
||||
// AARCH64 masks the upper 16 bit of all memory accesses
|
||||
vaddr &= 0xffffffffffffULL;
|
||||
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
|
||||
|
|
@ -645,42 +713,18 @@ struct Memory::Impl {
|
|||
/// @returns The instance of T read from the specified virtual address.
|
||||
template <typename T>
|
||||
inline T Read(Common::ProcessAddress vaddr) noexcept requires(std::is_trivially_copyable_v<T>) {
|
||||
auto const addr_c1 = GetInteger(vaddr);
|
||||
if (!(sizeof(T) > 1 && (addr_c1 & 4095) + sizeof(T) > 4096)) {
|
||||
if (auto const ptr_c1 = GetPointerImpl(addr_c1, [addr_c1] {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr_c1);
|
||||
}, [&] {
|
||||
HandleRasterizerDownload(addr_c1, sizeof(T));
|
||||
}); ptr_c1) {
|
||||
// It may be tempting to rewrite this particular section to use "reinterpret_cast";
|
||||
// afterall, it's trivially copyable so surely it can be copied ov- Alignment.
|
||||
// Remember, alignment. memcpy() will deal with all the alignment extremely fast.
|
||||
T result{};
|
||||
std::memcpy(&result, ptr_c1, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
auto const addr_c2 = (addr_c1 & (~0xfff)) + 0x1000;
|
||||
// page crossing: say if sizeof(T) = 2, vaddr = 4095
|
||||
// 4095 + 2 mod 4096 = 1 => 2 - 1 = 1, thus c1=1, c2=1
|
||||
auto const count_c2 = (addr_c1 + sizeof(T)) & 4095;
|
||||
auto const count_c1 = sizeof(T) - count_c2;
|
||||
if (auto const ptr_c1 = GetPointerImpl(addr_c1, [addr_c1] {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr_c1);
|
||||
}, [&] {
|
||||
HandleRasterizerDownload(addr_c1, count_c1);
|
||||
}); ptr_c1) {
|
||||
if (auto const ptr_c2 = GetPointerImpl(addr_c2, [addr_c2] {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr_c2);
|
||||
}, [&] {
|
||||
HandleRasterizerDownload(addr_c2, count_c2);
|
||||
}); ptr_c2) {
|
||||
std::array<char, sizeof(T)> result{};
|
||||
std::memcpy(result.data() + 0, ptr_c1, count_c1);
|
||||
std::memcpy(result.data() + count_c1, ptr_c2, count_c2);
|
||||
return std::bit_cast<T>(result);
|
||||
}
|
||||
}
|
||||
const u64 addr = GetInteger(vaddr);
|
||||
if (auto const ptr = GetPointerImpl(addr, [addr]() {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr);
|
||||
}, [&]() {
|
||||
HandleRasterizerDownload(addr, sizeof(T));
|
||||
}); ptr) [[likely]] {
|
||||
// It may be tempting to rewrite this particular section to use "reinterpret_cast";
|
||||
// afterall, it's trivially copyable so surely it can be copied ov- Alignment.
|
||||
// Remember, alignment. memcpy() will deal with all the alignment extremely fast.
|
||||
T result{};
|
||||
std::memcpy(&result, ptr, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
return T{};
|
||||
}
|
||||
|
|
@ -690,37 +734,11 @@ struct Memory::Impl {
|
|||
/// @tparam T The data type to write to memory.
|
||||
template <typename T>
|
||||
inline void Write(Common::ProcessAddress vaddr, const T data) noexcept requires(std::is_trivially_copyable_v<T>) {
|
||||
auto const addr_c1 = GetInteger(vaddr);
|
||||
if (!(sizeof(T) > 1 && (addr_c1 & 4095) + sizeof(T) > 4096)) {
|
||||
if (auto const ptr_c1 = GetPointerImpl(addr_c1, [addr_c1] {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr_c1);
|
||||
}, [&] {
|
||||
HandleRasterizerWrite(addr_c1, sizeof(T));
|
||||
}); ptr_c1) {
|
||||
std::memcpy(ptr_c1, &data, sizeof(T));
|
||||
}
|
||||
} else {
|
||||
auto const addr_c2 = (addr_c1 & (~0xfff)) + 0x1000;
|
||||
// page crossing: say if sizeof(T) = 2, vaddr = 4095
|
||||
// 4095 + 2 mod 4096 = 1 => 2 - 1 = 1, thus c1=1, c2=1
|
||||
auto const count_c2 = (addr_c1 + sizeof(T)) & 4095;
|
||||
auto const count_c1 = sizeof(T) - count_c2;
|
||||
if (auto const ptr_c1 = GetPointerImpl(addr_c1, [addr_c1] {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X}", sizeof(T) * 8, addr_c1);
|
||||
}, [&] {
|
||||
HandleRasterizerWrite(addr_c1, count_c1);
|
||||
}); ptr_c1) {
|
||||
if (auto const ptr_c2 = GetPointerImpl(addr_c2, [addr_c2] {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X}", sizeof(T) * 8, addr_c2);
|
||||
}, [&] {
|
||||
HandleRasterizerWrite(addr_c2, count_c2);
|
||||
}); ptr_c2) {
|
||||
std::array<char, sizeof(T)> tmp = std::bit_cast<std::array<char, sizeof(T)>>(data);
|
||||
std::memcpy(ptr_c1, tmp.data() + 0, count_c1);
|
||||
std::memcpy(ptr_c2, tmp.data() + count_c1, count_c2);
|
||||
}
|
||||
}
|
||||
}
|
||||
const u64 addr = GetInteger(vaddr);
|
||||
if (auto const ptr = GetPointerImpl(addr, [addr, data]() {
|
||||
LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, addr, u64(data));
|
||||
}, [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); ptr) [[likely]]
|
||||
std::memcpy(ptr, &data, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -924,35 +942,35 @@ const u8* Memory::GetPointer(Common::ProcessAddress vaddr) const {
|
|||
}
|
||||
|
||||
u8 Memory::Read8(const Common::ProcessAddress addr) {
|
||||
return impl->Read<u8>(addr);
|
||||
return impl->Read8(addr);
|
||||
}
|
||||
|
||||
u16 Memory::Read16(const Common::ProcessAddress addr) {
|
||||
return impl->Read<u16_le>(addr);
|
||||
return impl->Read16(addr);
|
||||
}
|
||||
|
||||
u32 Memory::Read32(const Common::ProcessAddress addr) {
|
||||
return impl->Read<u32_le>(addr);
|
||||
return impl->Read32(addr);
|
||||
}
|
||||
|
||||
u64 Memory::Read64(const Common::ProcessAddress addr) {
|
||||
return impl->Read<u64_le>(addr);
|
||||
return impl->Read64(addr);
|
||||
}
|
||||
|
||||
void Memory::Write8(Common::ProcessAddress addr, u8 data) {
|
||||
impl->Write<u8>(addr, data);
|
||||
impl->Write8(addr, data);
|
||||
}
|
||||
|
||||
void Memory::Write16(Common::ProcessAddress addr, u16 data) {
|
||||
impl->Write<u16_le>(addr, data);
|
||||
impl->Write16(addr, data);
|
||||
}
|
||||
|
||||
void Memory::Write32(Common::ProcessAddress addr, u32 data) {
|
||||
impl->Write<u32_le>(addr, data);
|
||||
impl->Write32(addr, data);
|
||||
}
|
||||
|
||||
void Memory::Write64(Common::ProcessAddress addr, u64 data) {
|
||||
impl->Write<u64_le>(addr, data);
|
||||
impl->Write64(addr, data);
|
||||
}
|
||||
|
||||
bool Memory::WriteExclusive8(Common::ProcessAddress addr, u8 data, u8 expected) {
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
|||
terminal_handler_fast_dispatch_hint = code.getCurr<const void*>();
|
||||
calculate_location_descriptor();
|
||||
code.L(rsb_cache_miss);
|
||||
code.mov(r8, reinterpret_cast<u64>(fast_dispatch_table.data()));
|
||||
code.mov(r8, u64(fast_dispatch_table.data()));
|
||||
//code.mov(r12, qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)]);
|
||||
code.mov(r12, rbx);
|
||||
if (code.HasHostFeature(HostFeature::SSE42)) {
|
||||
|
|
@ -242,7 +242,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
|||
|
||||
code.align();
|
||||
fast_dispatch_table_lookup = code.getCurr<FastDispatchEntry& (*)(u64)>();
|
||||
code.mov(code.ABI_PARAM2, reinterpret_cast<u64>(fast_dispatch_table.data()));
|
||||
code.mov(code.ABI_PARAM2, u64(fast_dispatch_table.data()));
|
||||
if (code.HasHostFeature(HostFeature::SSE42)) {
|
||||
code.crc32(code.ABI_PARAM1, code.ABI_PARAM2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ struct FrameInfo {
|
|||
};
|
||||
static_assert(ABI_SHADOW_SPACE <= 32);
|
||||
|
||||
static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) {
|
||||
static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) noexcept {
|
||||
// We are initially 8 byte aligned because the return value is pushed onto an aligned stack after a call.
|
||||
const size_t rsp_alignment = (num_gprs % 2 == 0) ? 8 : 0;
|
||||
const size_t total_xmm_size = num_xmms * XMM_SIZE;
|
||||
|
|
@ -40,7 +40,7 @@ static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms
|
|||
};
|
||||
}
|
||||
|
||||
void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) {
|
||||
static void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = (ABI_ALL_GPRS & regs).count();
|
||||
|
|
@ -65,7 +65,7 @@ void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size,
|
|||
}
|
||||
}
|
||||
|
||||
void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) {
|
||||
static void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = (ABI_ALL_GPRS & regs).count();
|
||||
|
|
@ -107,13 +107,13 @@ void ABI_PopCallerSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size
|
|||
|
||||
// Windows ABI registers are not in the same allocation algorithm as unix's
|
||||
void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||
std::bitset<32> regs = ABI_ALL_CALLER_SAVE;
|
||||
auto regs = ABI_ALL_CALLER_SAVE;
|
||||
regs.reset(size_t(exception));
|
||||
ABI_PushRegistersAndAdjustStack(code, 0, regs);
|
||||
}
|
||||
|
||||
void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||
std::bitset<32> regs = ABI_ALL_CALLER_SAVE;
|
||||
auto regs = ABI_ALL_CALLER_SAVE;
|
||||
regs.reset(size_t(exception));
|
||||
ABI_PopRegistersAndAdjustStack(code, 0, regs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
|
|
@ -6,23 +6,21 @@
|
|||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "dynarmic/backend/x64/constant_pool.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
#include "dynarmic/backend/x64/constant_pool.h"
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
||||
: code(code), insertion_point(0) {
|
||||
: code(code)
|
||||
, insertion_point(0)
|
||||
{
|
||||
code.EnsureMemoryCommitted(align_size + size);
|
||||
code.int3();
|
||||
code.align(align_size);
|
||||
pool = std::span<ConstantT>(
|
||||
reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||
}
|
||||
|
||||
Xbyak::Address ConstantPool::GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
// TODO: Defining this crashes e v e r y t h i n g
|
||||
// #define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
// #define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
// #define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
|
||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
|
|
@ -79,7 +79,7 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou
|
|||
}
|
||||
|
||||
// Detect Overflow
|
||||
const int min_exponent_for_overflow = static_cast<int>(ibits) - static_cast<int>(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight<u64>(1, exponent) : 0))) - (unsigned_ ? 0 : 1);
|
||||
const int min_exponent_for_overflow = int(ibits) - int(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight<u64>(1, exponent) : 0))) - (unsigned_ ? 0 : 1);
|
||||
if (exponent >= min_exponent_for_overflow) {
|
||||
// Positive overflow
|
||||
if (unsigned_ || !sign) {
|
||||
|
|
@ -88,10 +88,10 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou
|
|||
}
|
||||
|
||||
// Negative overflow
|
||||
const u64 min_value = Safe::Negate<u64>(static_cast<u64>(1) << (ibits - 1));
|
||||
const u64 min_value = Safe::Negate<u64>(u64(1) << (ibits - 1));
|
||||
if (!(exponent == min_exponent_for_overflow && int_result == min_value)) {
|
||||
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
||||
return static_cast<u64>(1) << (ibits - 1);
|
||||
return u64(1) << (ibits - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
|
|
@ -11,7 +14,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
TEST_CASE("Host CPU supports", "[a64]") {
|
||||
using Cpu = Xbyak::util::Cpu;
|
||||
|
|
|
|||
|
|
@ -11,17 +11,9 @@
|
|||
|
||||
#include <fstream>
|
||||
#include <variant>
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
// xbyak hates human beings
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/x64/xbyak.h"
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
|
|
@ -39,10 +31,6 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/logging.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
#include "common/x64/xbyak_util.h"
|
||||
#endif
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
|
|
|||
|
|
@ -109,14 +109,6 @@ VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat
|
|||
// Null buffer not supported, adjust offset and size
|
||||
offset = 0;
|
||||
size = 0;
|
||||
} else {
|
||||
// Align offset down to minTexelBufferOffsetAlignment
|
||||
const u32 alignment = static_cast<u32>(device->GetMinTexelBufferOffsetAlignment());
|
||||
if (alignment > 1) {
|
||||
const u32 aligned_offset = offset & ~(alignment - 1);
|
||||
size += offset - aligned_offset;
|
||||
offset = aligned_offset;
|
||||
}
|
||||
}
|
||||
const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) {
|
||||
return offset == view.offset && size == view.size && format == view.format;
|
||||
|
|
|
|||
|
|
@ -880,8 +880,6 @@ private:
|
|||
}
|
||||
has_flushed_end_pending = false;
|
||||
|
||||
// Refresh buffer state before ending transform feedback to ensure counters_count is up-to-date.
|
||||
UpdateBuffers();
|
||||
if (buffers_count == 0) {
|
||||
LOG_DEBUG(Render_Vulkan, "EndTransformFeedbackEXT called with no counters (buffers_count=0)");
|
||||
scheduler.Record([](vk::CommandBuffer cmdbuf) {
|
||||
|
|
|
|||
|
|
@ -318,11 +318,6 @@ public:
|
|||
return properties.properties.limits.minStorageBufferOffsetAlignment;
|
||||
}
|
||||
|
||||
/// Returns texel buffer offset alignment requirement.
|
||||
VkDeviceSize GetMinTexelBufferOffsetAlignment() const {
|
||||
return properties.properties.limits.minTexelBufferOffsetAlignment;
|
||||
}
|
||||
|
||||
/// Returns the maximum range for storage buffers.
|
||||
VkDeviceSize GetMaxStorageBufferRange() const {
|
||||
return properties.properties.limits.maxStorageBufferRange;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue