Compare commits

...

4 Commits

Author SHA1 Message Date
CamilleLaVey 4e20cd98a7 [TEST] Another increased on windows size 2026-08-07 20:58:31 -04:00
CamilleLaVey 6a3adefd46 Increased windows size 2026-08-07 20:50:03 -04:00
CamilleLaVey 0fc813332f [TEST] Increased window limiter on AHB 2026-08-07 20:09:33 -04:00
CamilleLaVey 95c78a206c [TEST] Adjustment AHB to tiled-gpu-v2 state 2026-08-07 15:54:08 -04:00
6 changed files with 41 additions and 161 deletions
+14 -34
View File
@@ -4,43 +4,34 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <fstream>
#include "common/heap_tracker.h"
#include "common/logging.h"
#include "common/assert.h"
#include "common/memory_detect.h"
namespace Common {
namespace {
s64 GetMaxPermissibleResidentMapCount(const Common::HostMemory& buffer) {
s64 GetMaxPermissibleResidentMapCount() {
// Default value.
constexpr s64 DefaultMaxMapCount = 65530;
s64 value = 65530;
// Try to read how many mappings we can make.
const u64 reported = Common::GetMaxMapCount();
const s64 value = reported != 0 ? static_cast<s64>(reported) : DefaultMaxMapCount;
std::ifstream s("/proc/sys/vm/max_map_count");
s >> value;
// Print, for debug.
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
// Allow 20000 maps for other code and to account for split inaccuracy.
constexpr s64 ForeignMapReservation = 20000;
const size_t hardware_buffer_windows = buffer.BackingHardwareBuffers().size();
const s64 unmergeable_window_boundaries =
hardware_buffer_windows != 0 ? static_cast<s64>(hardware_buffer_windows) + 1 : 0;
return std::max<s64>(value - ForeignMapReservation - unmergeable_window_boundaries, 0);
return std::max<s64>(value - 20000, 0);
}
} // namespace
HeapTracker::HeapTracker(Common::HostMemory& buffer)
: m_buffer(buffer),
m_has_hardware_buffer_backing(!buffer.BackingHardwareBuffers().empty()),
m_max_resident_map_count(GetMaxPermissibleResidentMapCount(buffer)) {}
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
HeapTracker::~HeapTracker() = default;
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
@@ -94,8 +85,7 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
// If resident, erase from resident map.
if (item->is_resident) {
m_resident_map_count -= this->HostMapCount(item->paddr, item->size);
ASSERT(m_resident_map_count >= 0);
ASSERT(--m_resident_map_count >= 0);
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
}
@@ -201,7 +191,7 @@ bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
// This map is now resident.
it->is_resident = true;
m_resident_map_count += this->HostMapCount(it->paddr, it->size);
m_resident_map_count++;
m_resident_mappings.insert(*it);
}
@@ -223,17 +213,17 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
// Despite being worse in theory, this has proven to be better in practice than more
// regularly dumping a smaller amount, because it significantly reduces average case
// lock contention.
s64 const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
std::size_t const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
std::size_t const evict_count = m_resident_map_count - desired_count;
auto it = m_resident_mappings.begin();
while (m_resident_map_count > desired_count && it != m_resident_mappings.end()) {
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
// Unmark and unmap.
it->is_resident = false;
m_buffer.Unmap(it->vaddr, it->size, false);
// Advance.
m_resident_map_count -= this->HostMapCount(it->paddr, it->size);
ASSERT(m_resident_map_count >= 0);
ASSERT(--m_resident_map_count >= 0);
it = m_resident_mappings.erase(it);
}
}
@@ -255,7 +245,6 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
// Cache the original values.
auto* const left = std::addressof(*it);
const size_t orig_size = left->size;
const s64 orig_host_map_count = this->HostMapCount(left->paddr, orig_size);
// Adjust the left map.
const size_t left_size = offset - left->vaddr;
@@ -277,20 +266,11 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
// If resident, also insert into resident map.
if (right->is_resident) {
m_resident_map_count += this->HostMapCount(left->paddr, left->size) +
this->HostMapCount(right->paddr, right->size) -
orig_host_map_count;
m_resident_map_count++;
m_resident_mappings.insert(*right);
}
}
s64 HeapTracker::HostMapCount(PAddr paddr, size_t size) const {
if (!m_has_hardware_buffer_backing) {
return size != 0 ? 1 : 0;
}
return static_cast<s64>(m_buffer.BackingMapCount(paddr, size));
}
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
const SeparateHeapMap key{
.vaddr = offset,
-3
View File
@@ -85,13 +85,10 @@ private:
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
s64 HostMapCount(PAddr paddr, size_t size) const;
void RebuildSeparateHeapAddressSpace();
private:
Common::HostMemory& m_buffer;
const bool m_has_hardware_buffer_backing;
const s64 m_max_resident_map_count;
std::shared_mutex m_rebuild_lock{};
+22 -85
View File
@@ -637,12 +637,10 @@ public:
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
LOG_WARNING(HW_Memory, "Hardware buffer probe allocation failed");
return false;
}
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
LOG_WARNING(HW_Memory, "Hardware buffer has no mappable file descriptor");
AHardwareBuffer_release(buffer);
return false;
}
@@ -654,8 +652,6 @@ public:
}
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
if (ptr == MAP_FAILED) {
LOG_WARNING(HW_Memory, "Hardware buffer backing rejects {}: {}", what,
strerror(errno));
ok = false;
return;
}
@@ -670,37 +666,34 @@ public:
return ok;
}
size_t ComputeAhbWindowCount(size_t window_size) const {
size_t ComputeAhbBudget(size_t window_size) const {
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
if (total_physical == 0) {
LOG_WARNING(HW_Memory, "Host memory size is unknown, not committing hardware buffers");
return 0;
}
constexpr u64 MinimumTotalPhysical = 5632ULL << 20;
constexpr u64 MinimumTotalPhysical = 7ULL << 30;
if (total_physical < MinimumTotalPhysical) {
LOG_INFO(HW_Memory,
"Skipping hardware buffer backing, {} MiB of RAM is below the {} MiB minimum",
total_physical >> 20, MinimumTotalPhysical >> 20);
return 0;
}
constexpr u64 MaxWindows = 2;
u64 windows = MaxWindows;
windows = (std::min)(windows, (total_physical / 6) / window_size);
const u64 max_map_count = Common::GetMaxMapCount();
constexpr u64 ReservedMaps = 24576;
if (max_map_count == 0 || max_map_count <= ReservedMaps) {
return 0;
}
u64 budget = total_physical / 6;
budget = (std::min)(budget, (max_map_count - ReservedMaps) * PageAlignment);
const u64 available = Common::GetAvailablePhysicalMemory();
if (available != 0) {
constexpr u64 Headroom = 2ULL << 30;
const u64 spare = available > Headroom ? available - Headroom : 0;
windows = (std::min)(windows, spare / window_size);
budget = (std::min)(budget, available > Headroom ? available - Headroom : 0);
}
windows = (std::min)(windows, static_cast<u64>(backing_size) / window_size);
if (windows == 0) {
LOG_INFO(HW_Memory,
"Skipping hardware buffer backing, no {} MiB window fits on a {} MiB system "
"with {} MiB available",
window_size >> 20, total_physical >> 20, available >> 20);
budget = (std::min)(budget, static_cast<u64>(backing_size));
budget = Common::AlignDown(budget, window_size);
constexpr u64 MinimumBudget = 256ULL << 20;
if (budget < MinimumBudget) {
return 0;
}
return static_cast<size_t>(windows);
return static_cast<size_t>(budget);
}
bool InitAhbBacking() {
@@ -710,41 +703,25 @@ public:
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
ResolveGetNativeHandle();
if (get_native_handle == nullptr) {
LOG_WARNING(HW_Memory, "AHardwareBuffer_getNativeHandle is not available");
return false;
}
constexpr size_t WindowCandidates[] = {
512ULL << 20,
256ULL << 20,
128ULL << 20,
64ULL << 20,
};
static_assert(WindowCandidates[0] <= 0xFFFFFFFFull,
"AHARDWAREBUFFER_FORMAT_BLOB encodes its size in a u32 width");
size_t window_size = 0;
for (const size_t candidate : WindowCandidates) {
const AHardwareBuffer_Desc candidate_desc = MakeBlobDesc(candidate);
if (AHardwareBuffer_isSupported(&candidate_desc) != 0) {
window_size = candidate;
break;
}
LOG_INFO(HW_Memory, "Allocator rejects {} MiB hardware buffer windows", candidate >> 20);
}
if (window_size == 0) {
LOG_WARNING(HW_Memory, "No hardware buffer window size is supported");
constexpr size_t window_size = 512ULL << 20;
const AHardwareBuffer_Desc window_desc = MakeBlobDesc(window_size);
if (AHardwareBuffer_isSupported(&window_desc) == 0) {
return false;
}
const size_t num_windows = ComputeAhbWindowCount(window_size);
if (num_windows == 0) {
const size_t budget = ComputeAhbBudget(window_size);
if (budget == 0) {
return false;
}
if (!ProbeAhbBacking(get_native_handle)) {
return false;
}
const size_t aligned_backing = Common::AlignDown(backing_size, window_size);
const size_t region_size = num_windows * window_size;
const size_t region_size = (std::min)(budget, aligned_backing);
const size_t region_base = Common::AlignDown(
(std::min)(preferred_offset, aligned_backing - region_size), window_size);
const size_t num_windows = region_size / window_size;
std::vector<AHardwareBuffer*> buffers;
std::vector<int> buffer_fds;
@@ -759,22 +736,18 @@ public:
const AHardwareBuffer_Desc desc = MakeBlobDesc(window_size);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
LOG_WARNING(HW_Memory, "Hardware buffer allocation failed for window {} of {}", i,
num_windows);
cleanup();
return false;
}
buffers.push_back(buffer);
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
LOG_WARNING(HW_Memory, "Hardware buffer has no mappable file descriptor");
cleanup();
return false;
}
const int buffer_fd = handle->data[0];
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
if (buffer_len < static_cast<off_t>(window_size)) {
LOG_WARNING(HW_Memory, "Hardware buffer descriptor smaller than requested");
cleanup();
return false;
}
@@ -783,7 +756,6 @@ public:
u8* const base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (base == MAP_FAILED) {
LOG_WARNING(HW_Memory, "Failed to reserve backing address space: {}", strerror(errno));
cleanup();
return false;
}
@@ -794,7 +766,6 @@ public:
}
if (mmap(base + offset, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, map_fd,
map_offset) == MAP_FAILED) {
LOG_WARNING(HW_Memory, "Backing mmap failed: {}", strerror(errno));
munmap(base, backing_size);
cleanup();
return false;
@@ -822,9 +793,6 @@ public:
ahb_base = region_base;
ahb_bytes = region_size;
committed_backing_size.store(region_size, std::memory_order_relaxed);
LOG_INFO(HW_Memory,
"Guest memory {:#x}-{:#x} backed by {} hardware buffer windows, {} MiB committed",
region_base, region_base + region_size, ahb_windows.size(), region_size >> 20);
return true;
}
@@ -852,29 +820,6 @@ public:
}
}
size_t BackingMapCount(size_t host_offset, size_t length) const noexcept {
if (length == 0) {
return 0;
}
if (ahb_bytes == 0) {
return 1;
}
size_t count = 0;
while (length > 0) {
size_t chunk = length;
if (host_offset < ahb_base) {
chunk = (std::min)(chunk, ahb_base - host_offset);
} else if (host_offset < ahb_base + ahb_bytes) {
const size_t local = (host_offset - ahb_base) % ahb_window_size;
chunk = (std::min)(chunk, ahb_window_size - local);
}
host_offset += chunk;
length -= chunk;
++count;
}
return count;
}
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
return ahb_windows;
}
@@ -1132,14 +1077,6 @@ std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noe
#endif
}
size_t HostMemory::BackingMapCount(size_t host_offset, size_t length) const noexcept {
#ifdef __ANDROID__
return impl ? impl->BackingMapCount(host_offset, length) : (length != 0 ? 1 : 0);
#else
return length != 0 ? 1 : 0;
#endif
}
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindowSize() : 0;
-2
View File
@@ -71,8 +71,6 @@ public:
return backing_size;
}
[[nodiscard]] size_t BackingMapCount(size_t host_offset, size_t length) const noexcept;
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
@@ -990,10 +990,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
#ifdef __ANDROID__
if (extensions.external_memory_ahb && !extensions.queue_family_foreign) {
LOG_INFO(Render_Vulkan,
"Not loading {} because its dependency {} is unavailable",
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME);
loaded_extensions.erase(VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME);
extensions.external_memory_ahb = false;
}
@@ -234,9 +234,9 @@ namespace Vulkan {
return;
}
if (device.IsTiler()) {
LOG_INFO(Render_Vulkan,
"Unified memory disabled, hardware buffer import is the only path supported "
"by tiler drivers");
return;
}
if (!hardware_buffers.empty()) {
return;
}
if (ImportHostPointer(base, size)) {
@@ -247,16 +247,11 @@ namespace Vulkan {
bool HostMemoryImport::ImportHostPointer(void *base, size_t size) {
if (!device.IsExtExternalMemoryHostSupported()) {
LOG_INFO(Render_Vulkan,
"Unified memory disabled, VK_EXT_external_memory_host is not supported");
return false;
}
const u64 alignment = device.GetMinImportedHostPointerAlignment();
if (alignment == 0 || !Common::IsAligned(reinterpret_cast<uintptr_t>(base), alignment) ||
!Common::IsAligned(size, alignment)) {
LOG_INFO(Render_Vulkan,
"Unified memory disabled, host allocation does not satisfy alignment {}",
alignment);
return false;
}
using namespace Common::Literals;
@@ -325,14 +320,9 @@ namespace Vulkan {
logical.DestroyBufferRaw(new_buffer);
break;
}
constexpr VkDeviceSize MaxHeapFractionDenominator = 2;
const u32 heap_index = memory_props.memoryTypes[*type_index].heapIndex;
const VkDeviceSize heap_size = memory_props.memoryHeaps[heap_index].size;
const VkDeviceSize heap_import_limit = heap_size / MaxHeapFractionDenominator;
if (imported_size + window_len > heap_import_limit) {
LOG_INFO(Render_Vulkan,
"Stopping guest memory import at {} MiB to leave room on heap {} of {} MiB",
imported_size >> 20, heap_index, heap_size >> 20);
if (imported_size + window_len > heap_size / 2) {
logical.DestroyBufferRaw(new_buffer);
break;
}
@@ -364,12 +354,8 @@ namespace Vulkan {
imported_size += static_cast<size_t>(window_len);
}
if (windows.empty()) {
LOG_INFO(Render_Vulkan, "Host pointer import failed");
return false;
}
LOG_INFO(Render_Vulkan,
"Imported {} MiB of guest memory for unified memory access in {} windows",
imported_size >> 20, windows.size());
return true;
}
@@ -382,18 +368,8 @@ namespace Vulkan {
!device.IsExtExternalMemoryAhbSupported()) {
return false;
}
using namespace Common::Literals;
u64 max_allocation_size = device.GetMaxMemoryAllocationSize();
if (device.IsTiler()) {
constexpr u64 TilerAllocationLimit = 1_GiB;
max_allocation_size = max_allocation_size != 0
? (std::min)(max_allocation_size, TilerAllocationLimit)
: TilerAllocationLimit;
}
const u64 max_allocation_size = device.GetMaxMemoryAllocationSize();
if (max_allocation_size != 0 && hardware_buffer_window > max_allocation_size) {
LOG_WARNING(Render_Vulkan,
"Hardware buffer windows of {} MiB exceed the {} MiB allocation limit",
hardware_buffer_window >> 20, max_allocation_size >> 20);
return false;
}
if (hardware_buffer_base >= size) {
@@ -487,15 +463,11 @@ namespace Vulkan {
imported_size += static_cast<size_t>(window_len);
}
if (windows.empty()) {
LOG_INFO(Render_Vulkan, "Hardware buffer import failed");
window_size = 0;
base_offset = 0;
return false;
}
foreign_ownership = true;
LOG_INFO(Render_Vulkan,
"Imported {} MiB of guest memory at {:#x} via hardware buffers in {} windows",
imported_size >> 20, base_offset, windows.size());
return true;
#else
return false;