Compare commits

..

4 Commits

Author SHA1 Message Date
lizzie cbb74b3e70 fix
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-19 04:28:22 +00:00
lizzie 7da774f766 [hle] make HBLoader work
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-19 04:16:52 +00:00
lizzie e875a3196b [core/hle/services/sockets] allow 'valid' range from [16,255] for IPv4 (#3491)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3491
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-18 23:54:47 +02:00
lizzie 4eb082485d [video_core] fix odr violation in formatter for pixelFormat (#3504)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3504
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-18 23:54:07 +02:00
13 changed files with 50 additions and 426 deletions
+8
View File
@@ -9,3 +9,11 @@ At the moment of writing, we do not support CFW such as Atmosphere, due to:
We do however, maintain HLE compatibility with the former mentioned CFW, applications that require Atmosphere to run will run fine in the emulator without any adjustments. We do however, maintain HLE compatibility with the former mentioned CFW, applications that require Atmosphere to run will run fine in the emulator without any adjustments.
If they don't run - then that's a bug! If they don't run - then that's a bug!
## Atmosphere
Fusee Galee, the bootloader and other low-level mechanisms are not emulated at the moment.
Having OFW is recommended, but may not be required (untested).
Extract the contents of Atmosphere into `sdmc`. Then to launch simply use `-hlaunch` instead (orthogonal to `-qlaunch`).
+3
View File
@@ -10,6 +10,9 @@ There are two main applications, an SDL-based app (`eden-cli`) and a Qt based ap
- `-u <number>`: Select the index of the user to load as. - `-u <number>`: Select the index of the user to load as.
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only). - `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
- `-qlaunch`: Launch QLaunch. - `-qlaunch`: Launch QLaunch.
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
- `-setup`: Launch setup applet. - `-setup`: Launch setup applet.
## eden-cli ## eden-cli
-79
View File
@@ -14,17 +14,12 @@
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include <span> #include <span>
#include <utility>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/range_mutex.h" #include "common/range_mutex.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
#if defined(__linux__)
#include <sys/mman.h>
#endif
namespace Core { namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL; constexpr size_t DEVICE_PAGEBITS = 12ULL;
@@ -50,74 +45,6 @@ class DeviceMemoryManager {
using DeviceMethods = typename Traits::DeviceMethods; using DeviceMethods = typename Traits::DeviceMethods;
public: public:
class MirrorMapping {
public:
MirrorMapping() = default;
MirrorMapping(u8* mapped_base_, size_t mapped_size_, size_t data_offset_)
: mapped_base{mapped_base_}, mapped_size{mapped_size_}, data_offset{data_offset_} {}
MirrorMapping(const MirrorMapping&) = delete;
MirrorMapping& operator=(const MirrorMapping&) = delete;
MirrorMapping(MirrorMapping&& other) noexcept {
MoveFrom(other);
}
MirrorMapping& operator=(MirrorMapping&& other) noexcept {
if (this != &other) {
Release();
MoveFrom(other);
}
return *this;
}
~MirrorMapping() {
Release();
}
[[nodiscard]] bool IsValid() const noexcept {
return mapped_base != nullptr;
}
[[nodiscard]] explicit operator bool() const noexcept {
return IsValid();
}
[[nodiscard]] u8* Data() noexcept {
return mapped_base ? mapped_base + data_offset : nullptr;
}
[[nodiscard]] const u8* Data() const noexcept {
return mapped_base ? mapped_base + data_offset : nullptr;
}
[[nodiscard]] size_t Size() const noexcept {
return mapped_size >= data_offset ? mapped_size - data_offset : 0;
}
private:
void MoveFrom(MirrorMapping& other) noexcept {
mapped_base = std::exchange(other.mapped_base, nullptr);
mapped_size = std::exchange(other.mapped_size, 0);
data_offset = std::exchange(other.data_offset, 0);
}
void Release() noexcept {
#if defined(__linux__)
if (mapped_base) {
munmap(mapped_base, mapped_size);
}
#endif
mapped_base = nullptr;
mapped_size = 0;
data_offset = 0;
}
u8* mapped_base{};
size_t mapped_size{};
size_t data_offset{};
};
DeviceMemoryManager(const DeviceMemory& device_memory); DeviceMemoryManager(const DeviceMemory& device_memory);
~DeviceMemoryManager(); ~DeviceMemoryManager();
@@ -191,11 +118,6 @@ public:
void WriteBlock(DAddr address, const void* src_pointer, size_t size); void WriteBlock(DAddr address, const void* src_pointer, size_t size);
void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size); void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size);
[[nodiscard]] MirrorMapping CreateMirrorMapping(DAddr address, size_t size) const;
[[nodiscard]] u64 GetMappingVersion() const noexcept {
return mapping_version.load(std::memory_order_acquire);
}
Asid RegisterProcess(Memory::Memory* memory); Asid RegisterProcess(Memory::Memory* memory);
void UnregisterProcess(Asid id); void UnregisterProcess(Asid id);
@@ -314,7 +236,6 @@ private:
std::unique_ptr<CachedPages> cached_pages; std::unique_ptr<CachedPages> cached_pages;
Common::RangeMutex counter_guard; Common::RangeMutex counter_guard;
std::mutex mapping_guard; std::mutex mapping_guard;
std::atomic<u64> mapping_version{1};
}; };
-89
View File
@@ -4,10 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include <atomic> #include <atomic>
#include <limits> #include <limits>
#include <memory> #include <memory>
@@ -15,17 +11,6 @@
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#if defined(__linux__)
#include <sys/mman.h>
#ifndef MREMAP_MAYMOVE
#define MREMAP_MAYMOVE 1
#endif
#ifndef MREMAP_FIXED
#define MREMAP_FIXED 2
#endif
extern "C" void* mremap(void* old_address, size_t old_size, size_t new_size, int flags, ...);
#endif
#include "common/address_space.h" #include "common/address_space.h"
#include "common/address_space.inc" #include "common/address_space.inc"
#include "common/alignment.h" #include "common/alignment.h"
@@ -255,7 +240,6 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
impl->multi_dev_address.Register(new_dev, start_id); impl->multi_dev_address.Register(new_dev, start_id);
} }
t_slot = {}; t_slot = {};
mapping_version.fetch_add(1, std::memory_order_release);
if (track) { if (track) {
TrackContinuityImpl(address, virtual_address, size, asid); TrackContinuityImpl(address, virtual_address, size, asid);
} }
@@ -288,7 +272,6 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
} }
} }
t_slot = {}; t_slot = {};
mapping_version.fetch_add(1, std::memory_order_release);
} }
template <typename Traits> template <typename Traits>
void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address, void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address,
@@ -332,78 +315,6 @@ const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::
return nullptr; return nullptr;
} }
template <typename Traits>
typename DeviceMemoryManager<Traits>::MirrorMapping DeviceMemoryManager<Traits>::CreateMirrorMapping(
DAddr address, size_t size) const {
#if !defined(__linux__)
return {};
#else
if (size == 0) {
return {};
}
const DAddr aligned_address = Common::AlignDown(address, DAddr{page_size});
const size_t data_offset = static_cast<size_t>(address - aligned_address);
const size_t mapped_size = Common::AlignUp(size + data_offset, page_size);
struct Segment {
const u8* source;
size_t size;
};
std::vector<Segment> segments;
segments.reserve(Common::DivCeil(mapped_size, page_size));
size_t remaining_size = mapped_size;
size_t page_index = aligned_address >> page_bits;
while (remaining_size > 0) {
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
const size_t copy_amount = (std::min)(next_pages << page_bits, remaining_size);
const auto phys_addr = tracked_entries[page_index].compressed_physical_ptr;
if (phys_addr == 0) {
return {};
}
const auto* source =
GetPointerFromRaw<u8>(PAddr(phys_addr - 1U) << Memory::YUZU_PAGEBITS);
if (!segments.empty() && segments.back().source + segments.back().size == source) {
segments.back().size += copy_amount;
} else {
segments.push_back({source, copy_amount});
}
page_index += next_pages;
remaining_size -= copy_amount;
}
void* const mirror_base =
mmap(nullptr, mapped_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mirror_base == MAP_FAILED) {
return {};
}
size_t mirror_offset = 0;
for (const auto& segment : segments) {
void* const target = static_cast<u8*>(mirror_base) + mirror_offset;
void* const result = mremap(const_cast<u8*>(segment.source), 0, segment.size,
MREMAP_MAYMOVE | MREMAP_FIXED, target);
if (result == MAP_FAILED) {
munmap(mirror_base, mapped_size);
return {};
}
if (mprotect(result, segment.size, PROT_READ | PROT_WRITE) != 0) {
munmap(mirror_base, mapped_size);
return {};
}
mirror_offset += segment.size;
}
return MirrorMapping{static_cast<u8*>(mirror_base), mapped_size, data_offset};
#endif
}
template <typename Traits> template <typename Traits>
void DeviceMemoryManager<Traits>::InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, void DeviceMemoryManager<Traits>::InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer,
PAddr address) { PAddr address) {
+5 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -268,7 +271,8 @@ Result KCapabilities::SetHandleTableCapability(const u32 cap) {
Result KCapabilities::SetDebugFlagsCapability(const u32 cap) { Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
// Validate. // Validate.
const DebugFlags pack{cap}; const DebugFlags pack{cap};
R_UNLESS(pack.reserved == 0, ResultReservedUsed); // TODO: Enabling this breaks compatibility with HBloader and such
//R_UNLESS(pack.reserved == 0, ResultReservedUsed);
DebugFlags debug_capabilities{m_debug_capabilities}; DebugFlags debug_capabilities{m_debug_capabilities};
debug_capabilities.allow_debug.Assign(pack.allow_debug); debug_capabilities.allow_debug.Assign(pack.allow_debug);
+4 -4
View File
@@ -629,7 +629,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
ASSERT(addr.size() == sizeof(SockAddrIn)); ASSERT(addr.size() >= 16);
auto addr_in = GetValue<SockAddrIn>(addr); auto addr_in = GetValue<SockAddrIn>(addr);
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
@@ -640,7 +640,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
return Errno::BADF; return Errno::BADF;
} }
UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); ASSERT(addr.size() >= 16);
auto addr_in = GetValue<SockAddrIn>(addr); auto addr_in = GetValue<SockAddrIn>(addr);
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
@@ -874,7 +874,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
if (ret < 0) { if (ret < 0) {
addr.clear(); addr.clear();
} else { } else {
ASSERT(addr.size() == sizeof(SockAddrIn)); ASSERT(addr.size() >= 16);
const SockAddrIn result = Translate(addr_in); const SockAddrIn result = Translate(addr_in);
PutValue(addr, result); PutValue(addr, result);
} }
@@ -899,7 +899,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes
Network::SockAddrIn addr_in; Network::SockAddrIn addr_in;
Network::SockAddrIn* p_addr_in = nullptr; Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) { if (!addr.empty()) {
ASSERT(addr.size() == sizeof(SockAddrIn)); ASSERT(addr.size() >= 16);
auto guest_addr_in = GetValue<SockAddrIn>(addr); auto guest_addr_in = GetValue<SockAddrIn>(addr);
addr_in = Translate(guest_addr_in); addr_in = Translate(guest_addr_in);
p_addr_in = &addr_in; p_addr_in = &addr_in;
+1 -1
View File
@@ -238,7 +238,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
Append<u32_be>(data, sizeof(SockAddrIn)); // ai_addrlen Append<u32_be>(data, 16); // ai_addrlen
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size // ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
// ai_addr: // ai_addr:
+2 -1
View File
@@ -110,8 +110,9 @@ struct SockAddrIn {
u8 family; u8 family;
u16 portno; u16 portno;
std::array<u8, 4> ip; std::array<u8, 4> ip;
std::array<u8, 8> zeroes; std::array<u8, 248> zeroes;
}; };
static_assert(sizeof(SockAddrIn) == 0x100);
enum class PollEvents : u16 { enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows. // Using Pascal case because IN is a macro on Windows.
@@ -265,13 +265,9 @@ PollEvents Translate(Network::PollEvents flags) {
} }
Network::SockAddrIn Translate(SockAddrIn value) { Network::SockAddrIn Translate(SockAddrIn value) {
if (value.len != 0 && value.len != sizeof(value) && value.len != 6) { // All lengths are valid, from [0 upto 256]
LOG_WARNING(Service, "Unexpected SockAddrIn len={}, expected 0, {}, or 6",
value.len, sizeof(value));
}
return { return {
.family = Translate(static_cast<Domain>(value.family)), .family = Translate(Domain(value.family)),
.ip = value.ip, .ip = value.ip,
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8), .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
}; };
@@ -279,7 +275,7 @@ Network::SockAddrIn Translate(SockAddrIn value) {
SockAddrIn Translate(Network::SockAddrIn value) { SockAddrIn Translate(Network::SockAddrIn value) {
return { return {
.len = sizeof(SockAddrIn), .len = 16,
.family = static_cast<u8>(Translate(value.family)), .family = static_cast<u8>(Translate(value.family)),
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8), .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
.ip = value.ip, .ip = value.ip,
+4 -217
View File
@@ -104,51 +104,6 @@ void BufferCache<P>::TickFrame() {
RunGarbageCollector(); RunGarbageCollector();
} }
++frame_tick; ++frame_tick;
static constexpr u64 mirror_stats_log_interval = 300;
if ((frame_tick % mirror_stats_log_interval) == 0) {
const u64 upload_hit_copies = mirror_upload_hit_copies - mirror_upload_hit_copies_last;
const u64 upload_miss_copies = mirror_upload_miss_copies - mirror_upload_miss_copies_last;
const u64 upload_hit_bytes = mirror_upload_hit_bytes - mirror_upload_hit_bytes_last;
const u64 upload_miss_bytes = mirror_upload_miss_bytes - mirror_upload_miss_bytes_last;
const u64 download_hit_copies =
mirror_download_hit_copies - mirror_download_hit_copies_last;
const u64 download_miss_copies =
mirror_download_miss_copies - mirror_download_miss_copies_last;
const u64 download_hit_bytes = mirror_download_hit_bytes - mirror_download_hit_bytes_last;
const u64 download_miss_bytes =
mirror_download_miss_bytes - mirror_download_miss_bytes_last;
const u64 upload_total_copies = upload_hit_copies + upload_miss_copies;
const u64 download_total_copies = download_hit_copies + download_miss_copies;
if (upload_total_copies > 0 || download_total_copies > 0) {
const double upload_hit_ratio = upload_total_copies > 0
? (100.0 * static_cast<double>(upload_hit_copies) /
static_cast<double>(upload_total_copies))
: 0.0;
const double download_hit_ratio =
download_total_copies > 0
? (100.0 * static_cast<double>(download_hit_copies) /
static_cast<double>(download_total_copies))
: 0.0;
LOG_INFO(HW_GPU,
"Buffer mirror counters (last {} frames): upload hit/miss copies = {}/{}, "
"hit ratio = {:.2f}%, bytes hit/miss = {}/{}, download hit/miss copies = "
"{}/{}, hit ratio = {:.2f}%, bytes hit/miss = {}/{}",
mirror_stats_log_interval, upload_hit_copies, upload_miss_copies,
upload_hit_ratio, upload_hit_bytes, upload_miss_bytes, download_hit_copies,
download_miss_copies, download_hit_ratio, download_hit_bytes,
download_miss_bytes);
}
mirror_upload_hit_copies_last = mirror_upload_hit_copies;
mirror_upload_miss_copies_last = mirror_upload_miss_copies;
mirror_upload_hit_bytes_last = mirror_upload_hit_bytes;
mirror_upload_miss_bytes_last = mirror_upload_miss_bytes;
mirror_download_hit_copies_last = mirror_download_hit_copies;
mirror_download_miss_copies_last = mirror_download_miss_copies;
mirror_download_hit_bytes_last = mirror_download_hit_bytes;
mirror_download_miss_bytes_last = mirror_download_miss_bytes;
}
delayed_destruction_ring.Tick(); delayed_destruction_ring.Tick();
for (auto& buffer : async_buffers_death_ring) { for (auto& buffer : async_buffers_death_ring) {
@@ -1609,21 +1564,6 @@ BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
const u32 size = static_cast<u32>(overlap.end - overlap.begin); const u32 size = static_cast<u32>(overlap.end - overlap.begin);
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size); const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
auto& new_buffer = slot_buffers[new_buffer_id]; auto& new_buffer = slot_buffers[new_buffer_id];
const u64 current_mapping_version = device_memory.GetMappingVersion();
if (mirror_mapping_version != current_mapping_version) {
buffer_mirrors.clear();
mirror_mapping_version = current_mapping_version;
}
buffer_mirrors.erase(new_buffer.CpuAddr());
if (auto mirror =
device_memory.CreateMirrorMapping(new_buffer.CpuAddr(), new_buffer.SizeBytes());
mirror) {
buffer_mirrors.emplace(new_buffer.CpuAddr(), std::move(mirror));
if (!mirror_creation_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
mirror_creation_logged = true;
}
}
const size_t size_bytes = new_buffer.SizeBytes(); const size_t size_bytes = new_buffer.SizeBytes();
runtime.ClearBuffer(new_buffer, 0, size_bytes, 0); runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
new_buffer.MarkUsage(0, size_bytes); new_buffer.MarkUsage(0, size_bytes);
@@ -1717,52 +1657,15 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
[[maybe_unused]] std::span<const BufferCopy> copies) { [[maybe_unused]] std::span<const BufferCopy> copies) {
if constexpr (!USE_MEMORY_MAPS_FOR_UPLOADS) { if constexpr (!USE_MEMORY_MAPS_FOR_UPLOADS) {
std::span<u8> immediate_buffer; std::span<u8> immediate_buffer;
const auto resolve_mirror_pointer = [&]() -> const u8* {
const u64 current_mapping_version = device_memory.GetMappingVersion();
if (mirror_mapping_version != current_mapping_version) {
buffer_mirrors.clear();
mirror_mapping_version = current_mapping_version;
}
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
if (mirror_it == buffer_mirrors.end()) {
if (auto mirror =
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
mirror) {
auto [it, inserted] =
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
mirror_it = it;
if (inserted && !mirror_creation_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
mirror_creation_logged = true;
}
}
}
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
};
const u8* const mirror_pointer = resolve_mirror_pointer();
for (const BufferCopy& copy : copies) { for (const BufferCopy& copy : copies) {
std::span<const u8> upload_span; std::span<const u8> upload_span;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
if (mirror_pointer != nullptr) { if (IsRangeGranular(device_addr, copy.size)) {
mirror_upload_hit_copies++;
mirror_upload_hit_bytes += copy.size;
if (!mirror_upload_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror fast path active for upload sync");
mirror_upload_logged = true;
}
upload_span =
std::span(mirror_pointer + static_cast<size_t>(copy.dst_offset), copy.size);
} else if (IsRangeGranular(device_addr, copy.size)) {
mirror_upload_miss_copies++;
mirror_upload_miss_bytes += copy.size;
auto* const ptr = device_memory.GetPointer<u8>(device_addr); auto* const ptr = device_memory.GetPointer<u8>(device_addr);
if (ptr != nullptr) { if (ptr != nullptr) {
upload_span = std::span(ptr, copy.size); upload_span = std::span(ptr, copy.size);
} }
} else { } else {
mirror_upload_miss_copies++;
mirror_upload_miss_bytes += copy.size;
if (immediate_buffer.empty()) { if (immediate_buffer.empty()) {
immediate_buffer = ImmediateBuffer(largest_copy); immediate_buffer = ImmediateBuffer(largest_copy);
} }
@@ -1781,47 +1684,10 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
if constexpr (USE_MEMORY_MAPS) { if constexpr (USE_MEMORY_MAPS) {
auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes); auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
const std::span<u8> staging_pointer = upload_staging.mapped_span; const std::span<u8> staging_pointer = upload_staging.mapped_span;
const auto resolve_mirror_pointer = [&]() -> const u8* {
const u64 current_mapping_version = device_memory.GetMappingVersion();
if (mirror_mapping_version != current_mapping_version) {
buffer_mirrors.clear();
mirror_mapping_version = current_mapping_version;
}
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
if (mirror_it == buffer_mirrors.end()) {
if (auto mirror =
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
mirror) {
auto [it, inserted] =
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
mirror_it = it;
if (inserted && !mirror_creation_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
mirror_creation_logged = true;
}
}
}
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
};
const u8* const mirror_pointer = resolve_mirror_pointer();
for (BufferCopy& copy : copies) { for (BufferCopy& copy : copies) {
u8* const src_pointer = staging_pointer.data() + copy.src_offset; u8* const src_pointer = staging_pointer.data() + copy.src_offset;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
if (mirror_pointer != nullptr) { device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
mirror_upload_hit_copies++;
mirror_upload_hit_bytes += copy.size;
if (!mirror_upload_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror fast path active for upload sync");
mirror_upload_logged = true;
}
std::memcpy(src_pointer, mirror_pointer + static_cast<size_t>(copy.dst_offset),
copy.size);
} else {
mirror_upload_miss_copies++;
mirror_upload_miss_bytes += copy.size;
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
}
// Apply the staging offset // Apply the staging offset
copy.src_offset += upload_staging.offset; copy.src_offset += upload_staging.offset;
@@ -1914,30 +1780,6 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
if constexpr (USE_MEMORY_MAPS) { if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes); auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
const u8* const mapped_memory = download_staging.mapped_span.data(); const u8* const mapped_memory = download_staging.mapped_span.data();
const auto resolve_mirror_pointer = [&]() -> u8* {
const u64 current_mapping_version = device_memory.GetMappingVersion();
if (mirror_mapping_version != current_mapping_version) {
buffer_mirrors.clear();
mirror_mapping_version = current_mapping_version;
}
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
if (mirror_it == buffer_mirrors.end()) {
if (auto mirror =
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
mirror) {
auto [it, inserted] =
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
mirror_it = it;
if (inserted && !mirror_creation_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
mirror_creation_logged = true;
}
}
}
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
};
u8* const mirror_pointer = resolve_mirror_pointer();
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size()); const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
for (BufferCopy& copy : copies) { for (BufferCopy& copy : copies) {
// Modify copies to have the staging offset in mind // Modify copies to have the staging offset in mind
@@ -1951,65 +1793,14 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
// Undo the modified offset // Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset; const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* copy_mapped_memory = mapped_memory + dst_offset; const u8* copy_mapped_memory = mapped_memory + dst_offset;
if (mirror_pointer != nullptr) { device_memory.WriteBlockUnsafe(copy_device_addr, copy_mapped_memory, copy.size);
mirror_download_hit_copies++;
mirror_download_hit_bytes += copy.size;
if (!mirror_download_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror fast path active for download sync");
mirror_download_logged = true;
}
std::memcpy(mirror_pointer + static_cast<size_t>(copy.src_offset),
copy_mapped_memory, copy.size);
} else {
mirror_download_miss_copies++;
mirror_download_miss_bytes += copy.size;
device_memory.WriteBlockUnsafe(copy_device_addr, copy_mapped_memory, copy.size);
}
} }
} else { } else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy); const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
const auto resolve_mirror_pointer = [&]() -> u8* {
const u64 current_mapping_version = device_memory.GetMappingVersion();
if (mirror_mapping_version != current_mapping_version) {
buffer_mirrors.clear();
mirror_mapping_version = current_mapping_version;
}
auto mirror_it = buffer_mirrors.find(buffer.CpuAddr());
if (mirror_it == buffer_mirrors.end()) {
if (auto mirror =
device_memory.CreateMirrorMapping(buffer.CpuAddr(), buffer.SizeBytes());
mirror) {
auto [it, inserted] =
buffer_mirrors.emplace(buffer.CpuAddr(), std::move(mirror));
mirror_it = it;
if (inserted && !mirror_creation_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror mapping enabled (first successful mapping)");
mirror_creation_logged = true;
}
}
}
return mirror_it != buffer_mirrors.end() ? mirror_it->second.Data() : nullptr;
};
u8* const mirror_pointer = resolve_mirror_pointer();
for (const BufferCopy& copy : copies) { for (const BufferCopy& copy : copies) {
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size)); buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
const DAddr copy_device_addr = buffer.CpuAddr() + copy.src_offset; const DAddr copy_device_addr = buffer.CpuAddr() + copy.src_offset;
if (mirror_pointer != nullptr) { device_memory.WriteBlockUnsafe(copy_device_addr, immediate_buffer.data(), copy.size);
mirror_download_hit_copies++;
mirror_download_hit_bytes += copy.size;
if (!mirror_download_logged) [[unlikely]] {
LOG_INFO(HW_GPU, "Buffer mirror fast path active for download sync");
mirror_download_logged = true;
}
std::memcpy(mirror_pointer + static_cast<size_t>(copy.src_offset),
immediate_buffer.data(), copy.size);
} else {
mirror_download_miss_copies++;
mirror_download_miss_bytes += copy.size;
device_memory.WriteBlockUnsafe(copy_device_addr, immediate_buffer.data(),
copy.size);
}
} }
} }
} }
@@ -2050,10 +1841,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
if (!do_not_mark) { if (!do_not_mark) {
Buffer& buffer = slot_buffers[buffer_id]; Buffer& buffer = slot_buffers[buffer_id];
memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes()); memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes());
buffer_mirrors.erase(buffer.CpuAddr());
} else {
const Buffer& buffer = slot_buffers[buffer_id];
buffer_mirrors.erase(buffer.CpuAddr());
} }
Unregister(buffer_id); Unregister(buffer_id);
@@ -473,8 +473,6 @@ private:
Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::MaxwellDeviceMemoryManager& device_memory;
Common::SlotVector<Buffer> slot_buffers; Common::SlotVector<Buffer> slot_buffers;
ankerl::unordered_dense::map<DAddr, Tegra::MaxwellDeviceMemoryManager::MirrorMapping>
buffer_mirrors;
#ifdef YUZU_LEGACY #ifdef YUZU_LEGACY
static constexpr size_t TICKS_TO_DESTROY = 6; static constexpr size_t TICKS_TO_DESTROY = 6;
#else #else
@@ -524,26 +522,6 @@ private:
std::array<BufferId, ((1ULL << 34) >> CACHING_PAGEBITS)> page_table; std::array<BufferId, ((1ULL << 34) >> CACHING_PAGEBITS)> page_table;
Common::ScratchBuffer<u8> tmp_buffer; Common::ScratchBuffer<u8> tmp_buffer;
bool mirror_creation_logged = false;
bool mirror_upload_logged = false;
bool mirror_download_logged = false;
u64 mirror_mapping_version = 0;
u64 mirror_upload_hit_copies = 0;
u64 mirror_upload_miss_copies = 0;
u64 mirror_upload_hit_bytes = 0;
u64 mirror_upload_miss_bytes = 0;
u64 mirror_download_hit_copies = 0;
u64 mirror_download_miss_copies = 0;
u64 mirror_download_hit_bytes = 0;
u64 mirror_download_miss_bytes = 0;
u64 mirror_upload_hit_copies_last = 0;
u64 mirror_upload_miss_copies_last = 0;
u64 mirror_upload_hit_bytes_last = 0;
u64 mirror_upload_miss_bytes_last = 0;
u64 mirror_download_hit_copies_last = 0;
u64 mirror_download_miss_copies_last = 0;
u64 mirror_download_hit_bytes_last = 0;
u64 mirror_download_miss_bytes_last = 0;
}; };
} // namespace VideoCommon } // namespace VideoCommon
+10 -1
View File
@@ -5,6 +5,8 @@
#pragma once #pragma once
#undef PIXEL_FORMAT_LIST
#include <climits> #include <climits>
#include <utility> #include <utility>
#include "common/assert.h" #include "common/assert.h"
@@ -128,7 +130,7 @@ namespace VideoCore::Surface {
PIXEL_FORMAT_ELEM(S8_UINT_D24_UNORM, 1, 1, 32) \ PIXEL_FORMAT_ELEM(S8_UINT_D24_UNORM, 1, 1, 32) \
PIXEL_FORMAT_ELEM(D32_FLOAT_S8_UINT, 1, 1, 64) PIXEL_FORMAT_ELEM(D32_FLOAT_S8_UINT, 1, 1, 64)
enum class PixelFormat { enum class PixelFormat : u32 {
#define PIXEL_FORMAT_ELEM(name, ...) name, #define PIXEL_FORMAT_ELEM(name, ...) name,
PIXEL_FORMAT_LIST PIXEL_FORMAT_LIST
#undef PIXEL_FORMAT_ELEM #undef PIXEL_FORMAT_ELEM
@@ -192,6 +194,13 @@ constexpr u32 BytesPerBlock(PixelFormat pixel_format) {
return BitsPerBlock(pixel_format) / CHAR_BIT; return BitsPerBlock(pixel_format) / CHAR_BIT;
} }
}
#include "video_core/gpu.h"
#include "video_core/textures/texture.h"
namespace VideoCore::Surface {
SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type); SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);
bool SurfaceTargetIsLayered(SurfaceTarget target); bool SurfaceTargetIsLayered(SurfaceTarget target);
bool SurfaceTargetIsArray(SurfaceTarget target); bool SurfaceTargetIsArray(SurfaceTarget target);
+10 -4
View File
@@ -3,6 +3,7 @@
// Qt on macOS doesn't define VMA shit // Qt on macOS doesn't define VMA shit
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include "common/fs/path_util.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_enums.h" #include "common/settings_enums.h"
#include "frontend_common/settings_generator.h" #include "frontend_common/settings_generator.h"
@@ -635,6 +636,7 @@ MainWindow::MainWindow(bool has_broken_vulkan)
QString game_path; QString game_path;
bool should_launch_qlaunch = false; bool should_launch_qlaunch = false;
bool should_launch_hlaunch = false;
bool should_launch_setup = false; bool should_launch_setup = false;
bool has_gamepath = false; bool has_gamepath = false;
bool is_fullscreen = false; bool is_fullscreen = false;
@@ -676,6 +678,8 @@ MainWindow::MainWindow(bool has_broken_vulkan)
players[0].profile_name = args[++i].toStdString(); players[0].profile_name = args[++i].toStdString();
} else if (args[i] == QStringLiteral("-qlaunch")) { } else if (args[i] == QStringLiteral("-qlaunch")) {
should_launch_qlaunch = true; should_launch_qlaunch = true;
} else if (args[i] == QStringLiteral("-hlaunch")) {
should_launch_hlaunch = true;
} else if (args[i] == QStringLiteral("-setup")) { } else if (args[i] == QStringLiteral("-setup")) {
should_launch_setup = true; should_launch_setup = true;
} else { } else {
@@ -694,10 +698,12 @@ MainWindow::MainWindow(bool has_broken_vulkan)
} else { } else {
if (!game_path.isEmpty()) { if (!game_path.isEmpty()) {
BootGame(game_path, ApplicationAppletParameters()); BootGame(game_path, ApplicationAppletParameters());
} else { } else if (should_launch_qlaunch) {
if (should_launch_qlaunch) { LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt); } else if (should_launch_hlaunch) {
} std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
BootGame(QString::fromStdString(hbl_path), ApplicationAppletParameters());
} }
} }
} }