diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 15e0a817ae..ce0b0c5c1c 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -51,6 +51,7 @@ #endif // ^^^ POSIX ^^^ +#include #include #include #include @@ -60,6 +61,7 @@ #include "common/free_region_manager.h" #include "common/host_memory.h" #include "common/logging.h" +#include "common/memory_detect.h" #include "common/settings.h" #ifdef __ANDROID__ @@ -104,6 +106,12 @@ namespace Common { [[maybe_unused]] constexpr size_t PageAlignment = 0x1000; [[maybe_unused]] constexpr size_t HugePageSize = 0x200000; +static std::atomic committed_backing_size{}; + +u64 GetCommittedBackingSize() noexcept { + return committed_backing_size.load(std::memory_order_relaxed); +} + #ifdef _WIN32 // Manually imported for MinGW compatibility @@ -605,6 +613,54 @@ public: } #ifdef __ANDROID__ + static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) { + const AHardwareBuffer_Desc desc{ + .width = static_cast(PageAlignment * 2), + .height = 1, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_BLOB, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | + AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER, + .stride = 0, + .rfu0 = 0, + .rfu1 = 0, + }; + 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; + } + const int probe_fd = handle->data[0]; + bool ok = true; + const auto try_map = [&](int prot, off_t offset, const char* what) { + if (!ok) { + return; + } + 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; + } + munmap(ptr, PageAlignment); + }; + try_map(PROT_READ | PROT_WRITE, 0, "shared mappings"); + try_map(PROT_READ | PROT_WRITE, static_cast(PageAlignment), "mappings at an offset"); +#ifdef ARCHITECTURE_arm64 + try_map(PROT_READ | PROT_EXEC, 0, "executable mappings"); +#endif + AHardwareBuffer_release(buffer); + return ok; + } + bool InitAhbBacking() { if (!Settings::values.use_unified_memory.GetValue()) { return false; @@ -615,6 +671,17 @@ public: LOG_WARNING(HW_Memory, "AHardwareBuffer_getNativeHandle is not available"); return false; } + const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory; + if (total_physical != 0 && backing_size > total_physical / 2) { + LOG_WARNING(HW_Memory, + "Hardware buffer backing would commit {} MiB on a {} MiB system, keeping " + "lazily committed memory", + backing_size >> 20, total_physical >> 20); + return false; + } + if (!ProbeAhbBacking(get_native_handle)) { + return false; + } constexpr size_t window_size = 1ULL << 30; const size_t num_windows = (backing_size + window_size - 1) / window_size; std::vector buffers; @@ -678,23 +745,14 @@ public: return false; } } - void* const exec_probe = - mmap(nullptr, PageAlignment, PROT_READ | PROT_EXEC, MAP_SHARED, buffer_fds[0], 0); - if (exec_probe == MAP_FAILED) { - LOG_WARNING(HW_Memory, "Hardware buffer backing rejects executable mappings: {}", - strerror(errno)); - munmap(base, backing_size); - cleanup(); - return false; - } - munmap(exec_probe, PageAlignment); backing_base = base; ahb_windows = std::move(buffers); ahb_fds = std::move(buffer_fds); ahb_window_size = window_size; ahb_backing = true; - LOG_INFO(HW_Memory, "Guest memory backed by {} hardware buffer windows", - ahb_windows.size()); + committed_backing_size.store(backing_size, std::memory_order_relaxed); + LOG_INFO(HW_Memory, "Guest memory backed by {} hardware buffer windows, {} MiB committed", + ahb_windows.size(), backing_size >> 20); return true; } @@ -823,6 +881,10 @@ private: } ahb_windows.clear(); ahb_fds.clear(); + if (ahb_backing) { + committed_backing_size.store(0, std::memory_order_relaxed); + ahb_backing = false; + } #endif } diff --git a/src/common/host_memory.h b/src/common/host_memory.h index b20e5013eb..5d385247d1 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -17,6 +17,8 @@ struct AHardwareBuffer; namespace Common { +[[nodiscard]] u64 GetCommittedBackingSize() noexcept; + enum class MemoryPermission : u32 { Read = 1 << 0, Write = 1 << 1, diff --git a/src/core/core.cpp b/src/core/core.cpp index 8b9133f963..f8383f01ac 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -118,6 +118,7 @@ struct System::Impl { is_multicore = Settings::values.use_multi_core.GetValue(); extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; + unified_memory = Settings::values.use_unified_memory.GetValue(); core_timing.SetMulticore(is_multicore); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); @@ -145,7 +146,8 @@ struct System::Impl { !device_memory.has_value() || is_multicore != Settings::values.use_multi_core.GetValue() || extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() != - Settings::MemoryLayout::Memory_4Gb); + Settings::MemoryLayout::Memory_4Gb) || + unified_memory != Settings::values.use_unified_memory.GetValue(); if (!must_reinitialize) { return; @@ -156,6 +158,7 @@ struct System::Impl { is_multicore = Settings::values.use_multi_core.GetValue(); extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; + unified_memory = Settings::values.use_unified_memory.GetValue(); Initialize(system); } @@ -503,6 +506,7 @@ struct System::Impl { std::atomic_bool is_powered_on{}; bool is_multicore : 1 = false; bool extended_memory_layout : 1 = false; + bool unified_memory : 1 = false; bool exit_locked : 1 = false; bool exit_requested : 1 = false; bool nvdec_active : 1 = false; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 1133132c30..3db07ad5e1 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -293,7 +293,7 @@ GraphicsPipeline::GraphicsPipeline( key.Hash(), descriptor_buffer_layout.size); uses_descriptor_buffer = false; descriptor_buffer_layout = {}; - descriptor_set_layout = builder.CreateDescriptorSetLayout(false); + descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); } } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index d1a88237d9..3d2cedbf30 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -21,6 +21,7 @@ #include "common/assert.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" +#include "common/host_memory.h" #include "common/literals.h" #include #include "common/settings.h" @@ -1088,6 +1089,17 @@ bool Device::GetSuitability(bool requires_swapchain) { extensions.robustness_2 = false; } +#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; + } +#endif + #undef FEATURE_EXTENSION #undef EXTENSION @@ -1669,12 +1681,27 @@ void Device::CollectPhysicalMemoryInfo() { device_access_memory = 0; u64 device_initial_usage = 0; u64 local_memory = 0; + const auto heap_has_usable_type = [&mem_properties](size_t heap) { + for (u32 index = 0; index < mem_properties.memoryTypeCount; ++index) { + if (mem_properties.memoryTypes[index].heapIndex != heap) { + continue; + } + if ((mem_properties.memoryTypes[index].propertyFlags & + VK_MEMORY_PROPERTY_PROTECTED_BIT) == 0) { + return true; + } + } + return false; + }; for (size_t element = 0; element < num_properties; ++element) { const bool is_heap_local = (mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0; if (!is_integrated && !is_heap_local) { continue; } + if (!heap_has_usable_type(element)) { + continue; + } valid_heap_memory.push_back(element); if (is_heap_local) { local_memory += mem_properties.memoryHeaps[element].size; @@ -1686,6 +1713,13 @@ void Device::CollectPhysicalMemoryInfo() { } device_access_memory += mem_properties.memoryHeaps[element].size; } + const u64 committed_backing = Common::GetCommittedBackingSize(); + if (committed_backing != 0) { + LOG_INFO(Render_Vulkan, "Discounting {} MiB of guest memory committed by the host", + committed_backing >> 20); + local_memory -= std::min(local_memory, committed_backing); + device_access_memory -= std::min(device_access_memory, committed_backing); + } if (is_integrated) { const bool aggressive = Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index f1e4d3a9bc..f6ab7d785d 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -120,6 +120,7 @@ VK_DEFINE_HANDLE(VmaAllocator) #ifdef __ANDROID__ #define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION) \ + EXTENSION(EXT, QUEUE_FAMILY_FOREIGN, queue_family_foreign) \ EXTENSION(ANDROID, EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER, external_memory_ahb) #else #define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION) @@ -914,7 +915,7 @@ FN_MAX_LIMIT_LIST bool IsExtExternalMemoryAhbSupported() const { #ifdef __ANDROID__ - return extensions.external_memory_ahb; + return extensions.external_memory_ahb && extensions.queue_family_foreign; #else return false; #endif diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index a4664de134..0ad90ccafd 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -180,6 +180,15 @@ namespace Vulkan { logical.DestroyBufferRaw(new_buffer); break; } + const u32 heap_index = memory_props.memoryTypes[*type_index].heapIndex; + const VkDeviceSize heap_size = memory_props.memoryHeaps[heap_index].size; + if (imported_size + window_len > heap_size / 2) { + 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); + logical.DestroyBufferRaw(new_buffer); + break; + } const VkImportMemoryHostPointerInfoEXT import_info{ .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT, .pNext = nullptr,