mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-21 23:39:55 +00:00
Initial Implementation AHB
This commit is contained in:
+164
-1
@@ -53,12 +53,19 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#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 <android/hardware_buffer.h>
|
||||
#include <cutils/native_handle.h>
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && __ANDROID_API__ < 30
|
||||
#include <sys/syscall.h>
|
||||
@@ -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<u8*>(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<AHardwareBuffer*> buffers;
|
||||
std::vector<int> 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<u32>(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<off_t>(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<u8*>(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<AHardwareBuffer* const> 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<off_t>(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<AHardwareBuffer*> ahb_windows;
|
||||
std::vector<int> 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<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
|
||||
#ifdef __ANDROID__
|
||||
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
|
||||
#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) {
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#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<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
|
||||
|
||||
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
|
||||
|
||||
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
||||
return virtual_base;
|
||||
}
|
||||
|
||||
@@ -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<AHardwareBuffer* const> 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<AHardwareBuffer* const> ahb_windows;
|
||||
const size_t ahb_window_size;
|
||||
DeviceInterface* device_inter;
|
||||
|
||||
struct TrackedEntry {
|
||||
|
||||
@@ -172,6 +172,8 @@ template <typename Traits>
|
||||
DeviceMemoryManager<Traits>::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)
|
||||
|
||||
@@ -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<HostMemoryImport>(device, base, size);
|
||||
void BufferCacheRuntime::TryEnableUnifiedMemory(void* base, size_t size,
|
||||
std::span<AHardwareBuffer* const> hardware_buffers,
|
||||
size_t hardware_buffer_window) {
|
||||
unified_memory = std::make_unique<HostMemoryImport>(device, base, size, hardware_buffers,
|
||||
hardware_buffer_window);
|
||||
if (!unified_memory->IsValid()) {
|
||||
unified_memory.reset();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
#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<Buffer>& slot_buffers) noexcept;
|
||||
|
||||
void TryEnableUnifiedMemory(void* base, size_t size);
|
||||
void TryEnableUnifiedMemory(void* base, size_t size,
|
||||
std::span<AHardwareBuffer* const> hardware_buffers,
|
||||
size_t hardware_buffer_window);
|
||||
|
||||
[[nodiscard]] bool HasUnifiedMemory() const noexcept {
|
||||
return unified_memory != nullptr && unified_memory->IsValid();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,9 +25,34 @@
|
||||
#include "video_core/gpu_logging/gpu_logging.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/hardware_buffer.h>
|
||||
#endif
|
||||
|
||||
namespace Vulkan {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] std::optional<u32> FindImportMemoryType(
|
||||
const VkPhysicalDeviceMemoryProperties &props, u32 type_mask) {
|
||||
const auto find = [&](VkMemoryPropertyFlags wanted) -> std::optional<u32> {
|
||||
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<AHardwareBuffer *const> 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<uintptr_t>(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<u32> {
|
||||
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<u8 *>(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<size_t>(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<AHardwareBuffer *const> 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<VkDeviceSize>(size - offset),
|
||||
static_cast<VkDeviceSize>(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<size_t>(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() {
|
||||
|
||||
@@ -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<AHardwareBuffer *const> 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<AHardwareBuffer *const> hardware_buffers,
|
||||
size_t hardware_buffer_window, size_t size);
|
||||
|
||||
const Device &device;
|
||||
std::vector<Window> windows;
|
||||
VkDeviceSize window_size{};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user