diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 445d34c444..f4426aadf8 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -53,12 +53,19 @@ #include #include +#include #include "common/alignment.h" #include "common/assert.h" #include "common/free_region_manager.h" #include "common/host_memory.h" #include "common/logging.h" +#include "common/settings.h" + +#ifdef __ANDROID__ +#include +#include +#endif #if defined(__ANDROID__) && __ANDROID_API__ < 30 #include @@ -509,6 +516,11 @@ public: bool Init() { long page_size = sysconf(_SC_PAGESIZE); ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size); +#ifdef __ANDROID__ + if (InitAhbBacking()) { + return InitVirtual(); + } +#endif // Backing memory initialization #if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__) fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); @@ -554,7 +566,10 @@ public: return false; } - // Virtual memory initialization + return InitVirtual(); + } + + bool InitVirtual() { virtual_base = virtual_map_base = static_cast(ChooseVirtualBase(virtual_size)); if (virtual_base == MAP_FAILED) { LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); @@ -567,6 +582,103 @@ public: return true; } +#ifdef __ANDROID__ + bool InitAhbBacking() { + if (!Settings::values.use_unified_memory.GetValue()) { + return false; + } + constexpr size_t window_size = 1ULL << 30; + const size_t num_windows = (backing_size + window_size - 1) / window_size; + std::vector buffers; + std::vector buffer_fds; + const auto cleanup = [&] { + for (AHardwareBuffer* buffer : buffers) { + AHardwareBuffer_release(buffer); + } + buffers.clear(); + buffer_fds.clear(); + }; + for (size_t i = 0; i < num_windows; ++i) { + const size_t len = (std::min)(window_size, backing_size - i * window_size); + const AHardwareBuffer_Desc desc{ + .width = static_cast(len), + .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 allocation failed for window {}", i); + cleanup(); + return false; + } + buffers.push_back(buffer); + const native_handle_t* handle = AHardwareBuffer_getNativeHandle(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(len)) { + LOG_WARNING(HW_Memory, "Hardware buffer descriptor smaller than requested"); + cleanup(); + return false; + } + buffer_fds.push_back(buffer_fd); + } + u8* const base = static_cast(mmap(nullptr, backing_size, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0)); + if (base == MAP_FAILED) { + cleanup(); + return false; + } + for (size_t i = 0; i < num_windows; ++i) { + const size_t len = (std::min)(window_size, backing_size - i * window_size); + if (mmap(base + i * window_size, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, + buffer_fds[i], 0) == MAP_FAILED) { + LOG_WARNING(HW_Memory, "Hardware buffer mmap failed: {}", strerror(errno)); + munmap(base, backing_size); + cleanup(); + 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()); + return true; + } + + std::span AhbWindows() const noexcept { + return ahb_windows; + } + + size_t AhbWindowSize() const noexcept { + return ahb_backing ? ahb_window_size : 0; + } +#endif + ~Impl() { Release(); } @@ -587,6 +699,26 @@ public: #ifdef ARCHITECTURE_arm64 if (True(perms & MemoryPermission::Execute)) prot_flags |= PROT_EXEC; +#endif +#ifdef __ANDROID__ + if (ahb_backing) { + size_t voff = virtual_offset; + size_t hoff = host_offset; + size_t remaining = length; + while (remaining > 0) { + const size_t window = hoff / ahb_window_size; + const size_t local = hoff % ahb_window_size; + const size_t chunk = (std::min)(remaining, ahb_window_size - local); + void* const ret = + mmap(virtual_base + voff, chunk, prot_flags, MAP_SHARED | MAP_FIXED, + ahb_fds[window], static_cast(local)); + ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno)); + voff += chunk; + hoff += chunk; + remaining -= chunk; + } + return; + } #endif int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED; void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset); @@ -656,6 +788,14 @@ private: int ret = close(fd); ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno)); } + +#ifdef __ANDROID__ + for (AHardwareBuffer* buffer : ahb_windows) { + AHardwareBuffer_release(buffer); + } + ahb_windows.clear(); + ahb_fds.clear(); +#endif } void AdjustMap(size_t* virtual_offset, size_t* length) { @@ -681,6 +821,13 @@ private: int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create FreeRegionManager free_manager{}; + +#ifdef __ANDROID__ + bool ahb_backing{}; + std::vector ahb_windows; + std::vector ahb_fds; + size_t ahb_window_size{}; +#endif }; #endif // ^^^ POSIX ^^^ @@ -767,6 +914,22 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f std::memset(backing_base + physical_offset, fill_value, length); } +std::span HostMemory::BackingHardwareBuffers() const noexcept { +#ifdef __ANDROID__ + return impl ? impl->AhbWindows() : std::span{}; +#else + return {}; +#endif +} + +size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept { +#ifdef __ANDROID__ + return impl ? impl->AhbWindowSize() : 0; +#else + return 0; +#endif +} + void HostMemory::EnableDirectMappedAddress() { #if !(defined(__OPENORBIS__) || defined(__managarm__)) if (impl) { diff --git a/src/common/host_memory.h b/src/common/host_memory.h index b2fe9f5560..b20e5013eb 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -8,10 +8,13 @@ #include #include +#include #include "common/common_funcs.h" #include "common/common_types.h" #include "common/virtual_buffer.h" +struct AHardwareBuffer; + namespace Common { enum class MemoryPermission : u32 { @@ -66,6 +69,10 @@ public: return backing_size; } + [[nodiscard]] std::span BackingHardwareBuffers() const noexcept; + + [[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept; + [[nodiscard]] u8* VirtualBasePointer() noexcept { return virtual_base; } diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 6e5d44a9e2..98cc7babcd 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -20,6 +20,8 @@ #include "common/scratch_buffer.h" #include "common/virtual_buffer.h" +struct AHardwareBuffer; + namespace Core { constexpr size_t DEVICE_PAGEBITS = 12ULL; @@ -107,6 +109,14 @@ public: return physical_size; } + std::span GetBackingHardwareBuffers() const noexcept { + return ahb_windows; + } + + size_t GetBackingHardwareBufferWindowSize() const noexcept { + return ahb_window_size; + } + PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const { PAddr subbits = PAddr(address & page_mask); auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr; @@ -188,6 +198,8 @@ private: const uintptr_t physical_base; const size_t physical_size; + const std::span ahb_windows; + const size_t ahb_window_size; DeviceInterface* device_inter; struct TrackedEntry { diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index f022f594d4..8c247e228b 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -172,6 +172,8 @@ template DeviceMemoryManager::DeviceMemoryManager(const DeviceMemory& device_memory_) : physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())} , physical_size{device_memory_.buffer.BackingSize()} + , ahb_windows{device_memory_.buffer.BackingHardwareBuffers()} + , ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()} , device_inter{nullptr} , compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS)) , tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 15f53c51fc..6c9ae97b03 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -364,8 +364,11 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m scheduler_, staging_pool_); } -void BufferCacheRuntime::TryEnableUnifiedMemory(void* base, size_t size) { - unified_memory = std::make_unique(device, base, size); +void BufferCacheRuntime::TryEnableUnifiedMemory(void* base, size_t size, + std::span hardware_buffers, + size_t hardware_buffer_window) { + unified_memory = std::make_unique(device, base, size, hardware_buffers, + hardware_buffer_window); if (!unified_memory->IsValid()) { unified_memory.reset(); } diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 004666a4ec..9cf435a8ac 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -8,6 +8,7 @@ #include #include +#include #include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/buffer_cache/memory_tracker_base.h" @@ -98,7 +99,9 @@ public: void TickFrame(Common::SlotVector& slot_buffers) noexcept; - void TryEnableUnifiedMemory(void* base, size_t size); + void TryEnableUnifiedMemory(void* base, size_t size, + std::span hardware_buffers, + size_t hardware_buffer_window); [[nodiscard]] bool HasUnifiedMemory() const noexcept { return unified_memory != nullptr && unified_memory->IsValid(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fa7112879f..32a0a9ca55 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -240,8 +240,10 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra wfi_event(device.GetLogical().CreateEvent()) { scheduler.SetQueryCache(query_cache); if (Settings::values.use_unified_memory.GetValue()) { - buffer_cache_runtime.TryEnableUnifiedMemory(device_memory.GetPhysicalBase(), - device_memory.GetPhysicalSize()); + buffer_cache_runtime.TryEnableUnifiedMemory( + device_memory.GetPhysicalBase(), device_memory.GetPhysicalSize(), + device_memory.GetBackingHardwareBuffers(), + device_memory.GetBackingHardwareBufferWindowSize()); } memory_allocator.SetReclaimCallback([this](u64 bytes) -> u64 { u64 freed = staging_pool.ReclaimMemory(bytes); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index bb697af26d..d1a88237d9 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1075,6 +1075,7 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); + FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION); if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) { loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index e87d3e88b8..f1e4d3a9bc 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -118,6 +118,13 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(IMG, FILTER_CUBIC, filter_cubic_img) \ EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) +#ifdef __ANDROID__ +#define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION) \ + EXTENSION(ANDROID, EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER, external_memory_ahb) +#else +#define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION) +#endif + // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \ @@ -905,6 +912,14 @@ FN_MAX_LIMIT_LIST return extensions.external_memory_host; } + bool IsExtExternalMemoryAhbSupported() const { +#ifdef __ANDROID__ + return extensions.external_memory_ahb; +#else + return false; +#endif + } + bool IsDescriptorBindingPartiallyBoundSupported() const { return features.descriptor_indexing.descriptorBindingPartiallyBound; } @@ -1233,6 +1248,7 @@ private: FOR_EACH_VK_FEATURE_1_4(FEATURE); FOR_EACH_VK_FEATURE_EXT(FEATURE); FOR_EACH_VK_EXTENSION(EXTENSION); + FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION); #undef EXTENSION #undef FEATURE diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 5a9524997e..a4664de134 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -25,9 +25,34 @@ #include "video_core/gpu_logging/gpu_logging.h" #include "common/settings.h" +#ifdef __ANDROID__ +#include +#endif + namespace Vulkan { namespace { + [[nodiscard]] std::optional FindImportMemoryType( + const VkPhysicalDeviceMemoryProperties &props, u32 type_mask) { + const auto find = [&](VkMemoryPropertyFlags wanted) -> std::optional { + for (u32 i = 0; i < props.memoryTypeCount; ++i) { + if (((type_mask >> i) & 1u) != 0 && + (props.memoryTypes[i].propertyFlags & wanted) == wanted) { + return i; + } + } + return std::nullopt; + }; + auto type_index = find(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT); + if (!type_index) { + type_index = find(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + } + return type_index; + } + // Helpers translating MemoryUsage to flags/usage [[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferredVmaFlags(MemoryUsage usage) { @@ -68,12 +93,24 @@ namespace Vulkan { } // namespace - HostMemoryImport::HostMemoryImport(const Device &device_, void *base, size_t size) + HostMemoryImport::HostMemoryImport(const Device &device_, void *base, size_t size, + std::span hardware_buffers, + size_t hardware_buffer_window) : device{device_} { + if (ImportHostPointer(base, size)) { + return; + } + ImportHardwareBuffers(hardware_buffers, hardware_buffer_window, size); + if (windows.empty()) { + LOG_INFO(Render_Vulkan, "Unified memory disabled, no host memory import path"); + } + } + + 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; + return false; } const u64 alignment = device.GetMinImportedHostPointerAlignment(); if (alignment == 0 || !Common::IsAligned(reinterpret_cast(base), alignment) || @@ -81,7 +118,7 @@ namespace Vulkan { LOG_INFO(Render_Vulkan, "Unified memory disabled, host allocation does not satisfy alignment {}", alignment); - return; + return false; } using namespace Common::Literals; VkDeviceSize candidate_window = 1_GiB; @@ -90,22 +127,12 @@ namespace Vulkan { candidate_window = Common::AlignDown(max_buffer_size, alignment); } if (candidate_window == 0) { - return; + return false; } window_size = candidate_window; const auto &logical = device.GetLogical(); const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties; - const auto find_type = [&](u32 type_mask, VkMemoryPropertyFlags wanted) - -> std::optional { - for (u32 i = 0; i < memory_props.memoryTypeCount; ++i) { - if (((type_mask >> i) & 1u) != 0 && - (memory_props.memoryTypes[i].propertyFlags & wanted) == wanted) { - return i; - } - } - return std::nullopt; - }; for (size_t offset = 0; offset < size; offset += window_size) { u8 *const window_base = static_cast(base) + offset; @@ -148,13 +175,7 @@ namespace Vulkan { logical.DestroyBufferRaw(new_buffer); break; } - auto type_index = find_type(type_mask, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | - VK_MEMORY_PROPERTY_HOST_CACHED_BIT); - if (!type_index) { - type_index = find_type(type_mask, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - } + const auto type_index = FindImportMemoryType(memory_props, type_mask); if (!type_index) { logical.DestroyBufferRaw(new_buffer); break; @@ -187,12 +208,115 @@ namespace Vulkan { imported_size += static_cast(window_len); } if (windows.empty()) { - LOG_INFO(Render_Vulkan, "Unified memory disabled, host memory import failed"); - return; + 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; + } + + void HostMemoryImport::ImportHardwareBuffers( + [[maybe_unused]] std::span hardware_buffers, + [[maybe_unused]] size_t hardware_buffer_window, [[maybe_unused]] size_t size) { +#ifdef __ANDROID__ + if (hardware_buffers.empty() || hardware_buffer_window == 0 || + !device.IsExtExternalMemoryAhbSupported()) { + return; + } + const auto &logical = device.GetLogical(); + const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties; + window_size = hardware_buffer_window; + for (size_t i = 0; i < hardware_buffers.size(); ++i) { + const size_t offset = i * hardware_buffer_window; + if (offset >= size) { + break; + } + const VkDeviceSize window_len = (std::min)( + static_cast(size - offset), + static_cast(hardware_buffer_window)); + VkAndroidHardwareBufferPropertiesANDROID ahb_props{ + .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID, + .pNext = nullptr, + .allocationSize = 0, + .memoryTypeBits = 0, + }; + if (logical.GetAndroidHardwareBufferPropertiesANDROID(hardware_buffers[i], + &ahb_props) != VK_SUCCESS || + ahb_props.memoryTypeBits == 0 || ahb_props.allocationSize < window_len) { + break; + } + const VkExternalMemoryBufferCreateInfo external_info{ + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, + .pNext = nullptr, + .handleTypes = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, + }; + const VkBufferCreateInfo buffer_ci{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = &external_info, + .flags = 0, + .size = window_len, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + VkBuffer new_buffer{}; + if (logical.CreateBufferRaw(buffer_ci, &new_buffer) != VK_SUCCESS) { + break; + } + const VkMemoryRequirements requirements = + logical.GetBufferMemoryRequirements(new_buffer); + const u32 type_mask = requirements.memoryTypeBits & ahb_props.memoryTypeBits; + if (type_mask == 0 || requirements.size > ahb_props.allocationSize) { + logical.DestroyBufferRaw(new_buffer); + break; + } + const auto type_index = FindImportMemoryType(memory_props, type_mask); + if (!type_index) { + logical.DestroyBufferRaw(new_buffer); + break; + } + const VkImportAndroidHardwareBufferInfoANDROID import_info{ + .sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID, + .pNext = nullptr, + .buffer = hardware_buffers[i], + }; + const VkMemoryDedicatedAllocateInfo dedicated_info{ + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = &import_info, + .image = VK_NULL_HANDLE, + .buffer = new_buffer, + }; + const VkMemoryAllocateInfo alloc_info{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &dedicated_info, + .allocationSize = ahb_props.allocationSize, + .memoryTypeIndex = *type_index, + }; + vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info); + if (!memory) { + logical.DestroyBufferRaw(new_buffer); + break; + } + if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) { + logical.DestroyBufferRaw(new_buffer); + break; + } + windows.push_back(Window{ + .memory = std::move(memory), + .buffer = new_buffer, + }); + imported_size += static_cast(window_len); + } + if (!windows.empty()) { + LOG_INFO(Render_Vulkan, + "Imported {} MiB of guest memory via hardware buffers in {} windows", + imported_size >> 20, windows.size()); + } +#endif } HostMemoryImport::~HostMemoryImport() { diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index 57d5286ff8..411836fe85 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -16,6 +16,8 @@ #include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vma.h" +struct AHardwareBuffer; + namespace Vulkan { class Device; @@ -42,7 +44,9 @@ namespace Vulkan { class HostMemoryImport { public: - explicit HostMemoryImport(const Device &device_, void *base, size_t size); + explicit HostMemoryImport(const Device &device_, void *base, size_t size, + std::span hardware_buffers, + size_t hardware_buffer_window); ~HostMemoryImport(); @@ -76,6 +80,11 @@ namespace Vulkan { VkBuffer buffer{}; }; + bool ImportHostPointer(void *base, size_t size); + + void ImportHardwareBuffers(std::span hardware_buffers, + size_t hardware_buffer_window, size_t size); + const Device &device; std::vector windows; VkDeviceSize window_size{}; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index e840197e0a..024aca07d4 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -284,6 +284,9 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkGetMemoryFdKHR); #ifdef _WIN32 X(vkGetMemoryWin32HandleKHR); +#endif +#ifdef __ANDROID__ + X(vkGetAndroidHardwareBufferPropertiesANDROID); #endif X(vkGetQueryPoolResults); X(vkGetPipelineExecutablePropertiesKHR); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 1795596a2e..77a4bda15c 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -351,6 +351,9 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{}; #ifdef _WIN32 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; +#endif +#ifdef __ANDROID__ + PFN_vkGetAndroidHardwareBufferPropertiesANDROID vkGetAndroidHardwareBufferPropertiesANDROID{}; #endif PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{}; PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{}; @@ -1102,6 +1105,14 @@ public: out_properties); } +#ifdef __ANDROID__ + VkResult GetAndroidHardwareBufferPropertiesANDROID( + const struct AHardwareBuffer* buffer, + VkAndroidHardwareBufferPropertiesANDROID* out_properties) const noexcept { + return dld->vkGetAndroidHardwareBufferPropertiesANDROID(handle, buffer, out_properties); + } +#endif + VkResult CreateBufferRaw(const VkBufferCreateInfo& ci, VkBuffer* out_buffer) const noexcept { return dld->vkCreateBuffer(handle, &ci, nullptr, out_buffer); }