|
|
|
@ -26,350 +26,329 @@ |
|
|
|
#include "common/settings.h"
|
|
|
|
|
|
|
|
namespace Vulkan { |
|
|
|
namespace { |
|
|
|
namespace { |
|
|
|
|
|
|
|
// Helpers translating MemoryUsage to flags/usage
|
|
|
|
|
|
|
|
[[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) { |
|
|
|
switch (usage) { |
|
|
|
case MemoryUsage::DeviceLocal: |
|
|
|
return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
|
|
|
case MemoryUsage::Upload: |
|
|
|
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
case MemoryUsage::Download: |
|
|
|
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_CACHED_BIT; |
|
|
|
case MemoryUsage::Stream: |
|
|
|
return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
} |
|
|
|
ASSERT_MSG(false, "Invalid memory usage={}", usage); |
|
|
|
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
} |
|
|
|
|
|
|
|
[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferredVmaFlags(MemoryUsage usage) { |
|
|
|
if (usage == MemoryUsage::Download) { |
|
|
|
return VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
} |
|
|
|
return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
|
|
: VkMemoryPropertyFlagBits{}; |
|
|
|
[[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) { |
|
|
|
switch (usage) { |
|
|
|
case MemoryUsage::DeviceLocal: |
|
|
|
return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
|
|
|
case MemoryUsage::Upload: |
|
|
|
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
case MemoryUsage::Download: |
|
|
|
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_CACHED_BIT; |
|
|
|
case MemoryUsage::Stream: |
|
|
|
return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
|
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
} |
|
|
|
ASSERT_MSG(false, "Invalid memory usage={}", usage); |
|
|
|
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
} |
|
|
|
|
|
|
|
[[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) { |
|
|
|
switch (usage) { |
|
|
|
case MemoryUsage::Upload: |
|
|
|
case MemoryUsage::Stream: |
|
|
|
return VMA_ALLOCATION_CREATE_MAPPED_BIT | |
|
|
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; |
|
|
|
case MemoryUsage::Download: |
|
|
|
return VMA_ALLOCATION_CREATE_MAPPED_BIT | |
|
|
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; |
|
|
|
case MemoryUsage::DeviceLocal: |
|
|
|
return {}; |
|
|
|
} |
|
|
|
return {}; |
|
|
|
[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferredVmaFlags(MemoryUsage usage) { |
|
|
|
if (usage == MemoryUsage::Download) { |
|
|
|
return VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
|
|
|
} |
|
|
|
return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
|
|
: VkMemoryPropertyFlagBits{}; |
|
|
|
} |
|
|
|
|
|
|
|
[[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) { |
|
|
|
switch (usage) { |
|
|
|
case MemoryUsage::DeviceLocal: |
|
|
|
case MemoryUsage::Stream: |
|
|
|
return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; |
|
|
|
case MemoryUsage::Upload: |
|
|
|
case MemoryUsage::Download: |
|
|
|
return VMA_MEMORY_USAGE_AUTO_PREFER_HOST; |
|
|
|
} |
|
|
|
return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; |
|
|
|
[[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) { |
|
|
|
switch (usage) { |
|
|
|
case MemoryUsage::Upload: |
|
|
|
case MemoryUsage::Stream: |
|
|
|
return VMA_ALLOCATION_CREATE_MAPPED_BIT | |
|
|
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; |
|
|
|
case MemoryUsage::Download: |
|
|
|
return VMA_ALLOCATION_CREATE_MAPPED_BIT | |
|
|
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; |
|
|
|
case MemoryUsage::DeviceLocal: |
|
|
|
return {}; |
|
|
|
} |
|
|
|
return {}; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// This avoids calling vkGetBufferMemoryRequirements* directly.
|
|
|
|
template<typename T> |
|
|
|
static VkBuffer GetVkHandleFromBuffer(const T &buf) { |
|
|
|
if constexpr (requires { static_cast<VkBuffer>(buf); }) { |
|
|
|
return static_cast<VkBuffer>(buf); |
|
|
|
} else if constexpr (requires {{ buf.GetHandle() } -> std::convertible_to<VkBuffer>; }) { |
|
|
|
return buf.GetHandle(); |
|
|
|
} else if constexpr (requires {{ buf.Handle() } -> std::convertible_to<VkBuffer>; }) { |
|
|
|
return buf.Handle(); |
|
|
|
} else if constexpr (requires {{ buf.vk_handle() } -> std::convertible_to<VkBuffer>; }) { |
|
|
|
return buf.vk_handle(); |
|
|
|
} else { |
|
|
|
static_assert(sizeof(T) == 0, "Cannot extract VkBuffer handle from vk::Buffer"); |
|
|
|
return VK_NULL_HANDLE; |
|
|
|
} |
|
|
|
[[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) { |
|
|
|
switch (usage) { |
|
|
|
case MemoryUsage::DeviceLocal: |
|
|
|
case MemoryUsage::Stream: |
|
|
|
return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; |
|
|
|
case MemoryUsage::Upload: |
|
|
|
case MemoryUsage::Download: |
|
|
|
return VMA_MEMORY_USAGE_AUTO_PREFER_HOST; |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace
|
|
|
|
return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; |
|
|
|
} |
|
|
|
} // namespace
|
|
|
|
|
|
|
|
//MemoryCommit is now VMA-backed
|
|
|
|
MemoryCommit::MemoryCommit(VmaAllocator alloc, VmaAllocation a, |
|
|
|
const VmaAllocationInfo &info) noexcept |
|
|
|
: allocator{alloc}, allocation{a}, memory{info.deviceMemory}, |
|
|
|
offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} { |
|
|
|
// Log GPU memory allocation
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue()) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( |
|
|
|
reinterpret_cast<uintptr_t>(memory), |
|
|
|
static_cast<u64>(size), |
|
|
|
0 // Memory property flags (not easily available from VMA)
|
|
|
|
); |
|
|
|
} |
|
|
|
MemoryCommit::MemoryCommit(VmaAllocator alloc, VmaAllocation a, |
|
|
|
const VmaAllocationInfo &info) noexcept |
|
|
|
: allocator{alloc}, allocation{a}, memory{info.deviceMemory}, |
|
|
|
offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} { |
|
|
|
// Log GPU memory allocation
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue()) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( |
|
|
|
reinterpret_cast<uintptr_t>(memory), |
|
|
|
static_cast<u64>(size), |
|
|
|
0 // Memory property flags (not easily available from VMA)
|
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
MemoryCommit::~MemoryCommit() { Release(); } |
|
|
|
|
|
|
|
MemoryCommit::MemoryCommit(MemoryCommit &&rhs) noexcept |
|
|
|
: allocator{std::exchange(rhs.allocator, nullptr)}, |
|
|
|
allocation{std::exchange(rhs.allocation, nullptr)}, |
|
|
|
memory{std::exchange(rhs.memory, VK_NULL_HANDLE)}, |
|
|
|
offset{std::exchange(rhs.offset, 0)}, |
|
|
|
size{std::exchange(rhs.size, 0)}, |
|
|
|
mapped_ptr{std::exchange(rhs.mapped_ptr, nullptr)} {} |
|
|
|
|
|
|
|
MemoryCommit &MemoryCommit::operator=(MemoryCommit &&rhs) noexcept { |
|
|
|
if (this != &rhs) { |
|
|
|
Release(); |
|
|
|
allocator = std::exchange(rhs.allocator, nullptr); |
|
|
|
allocation = std::exchange(rhs.allocation, nullptr); |
|
|
|
memory = std::exchange(rhs.memory, VK_NULL_HANDLE); |
|
|
|
offset = std::exchange(rhs.offset, 0); |
|
|
|
size = std::exchange(rhs.size, 0); |
|
|
|
mapped_ptr = std::exchange(rhs.mapped_ptr, nullptr); |
|
|
|
} |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
MemoryCommit::~MemoryCommit() { Release(); } |
|
|
|
|
|
|
|
MemoryCommit::MemoryCommit(MemoryCommit &&rhs) noexcept |
|
|
|
: allocator{std::exchange(rhs.allocator, nullptr)}, |
|
|
|
allocation{std::exchange(rhs.allocation, nullptr)}, |
|
|
|
memory{std::exchange(rhs.memory, VK_NULL_HANDLE)}, |
|
|
|
offset{std::exchange(rhs.offset, 0)}, |
|
|
|
size{std::exchange(rhs.size, 0)}, |
|
|
|
mapped_ptr{std::exchange(rhs.mapped_ptr, nullptr)} {} |
|
|
|
|
|
|
|
MemoryCommit &MemoryCommit::operator=(MemoryCommit &&rhs) noexcept { |
|
|
|
if (this != &rhs) { |
|
|
|
Release(); |
|
|
|
allocator = std::exchange(rhs.allocator, nullptr); |
|
|
|
allocation = std::exchange(rhs.allocation, nullptr); |
|
|
|
memory = std::exchange(rhs.memory, VK_NULL_HANDLE); |
|
|
|
offset = std::exchange(rhs.offset, 0); |
|
|
|
size = std::exchange(rhs.size, 0); |
|
|
|
mapped_ptr = std::exchange(rhs.mapped_ptr, nullptr); |
|
|
|
} |
|
|
|
|
|
|
|
std::span<u8> MemoryCommit::Map() |
|
|
|
{ |
|
|
|
if (!allocation) return {}; |
|
|
|
if (!mapped_ptr) { |
|
|
|
if (vmaMapMemory(allocator, allocation, &mapped_ptr) != VK_SUCCESS) return {}; |
|
|
|
} |
|
|
|
const size_t n = static_cast<size_t>(std::min<VkDeviceSize>(size, |
|
|
|
(std::numeric_limits<size_t>::max)())); |
|
|
|
return std::span<u8>{static_cast<u8 *>(mapped_ptr), n}; |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
std::span<u8> MemoryCommit::Map() |
|
|
|
{ |
|
|
|
if (!allocation) return {}; |
|
|
|
if (!mapped_ptr) { |
|
|
|
if (vmaMapMemory(allocator, allocation, &mapped_ptr) != VK_SUCCESS) return {}; |
|
|
|
} |
|
|
|
const size_t n = static_cast<size_t>(std::min<VkDeviceSize>(size, |
|
|
|
(std::numeric_limits<size_t>::max)())); |
|
|
|
return std::span<u8>{static_cast<u8 *>(mapped_ptr), n}; |
|
|
|
} |
|
|
|
|
|
|
|
std::span<const u8> MemoryCommit::Map() const |
|
|
|
{ |
|
|
|
if (!allocation) return {}; |
|
|
|
if (!mapped_ptr) { |
|
|
|
void *p = nullptr; |
|
|
|
if (vmaMapMemory(allocator, allocation, &p) != VK_SUCCESS) return {}; |
|
|
|
const_cast<MemoryCommit *>(this)->mapped_ptr = p; |
|
|
|
} |
|
|
|
const size_t n = static_cast<size_t>(std::min<VkDeviceSize>(size, |
|
|
|
(std::numeric_limits<size_t>::max)())); |
|
|
|
return std::span<const u8>{static_cast<const u8 *>(mapped_ptr), n}; |
|
|
|
} |
|
|
|
|
|
|
|
void MemoryCommit::Unmap() |
|
|
|
{ |
|
|
|
if (allocation && mapped_ptr) { |
|
|
|
vmaUnmapMemory(allocator, allocation); |
|
|
|
mapped_ptr = nullptr; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::span<const u8> MemoryCommit::Map() const |
|
|
|
{ |
|
|
|
if (!allocation) return {}; |
|
|
|
if (!mapped_ptr) { |
|
|
|
void *p = nullptr; |
|
|
|
if (vmaMapMemory(allocator, allocation, &p) != VK_SUCCESS) return {}; |
|
|
|
const_cast<MemoryCommit *>(this)->mapped_ptr = p; |
|
|
|
void MemoryCommit::Release() { |
|
|
|
if (allocation && allocator) { |
|
|
|
// Log GPU memory deallocation
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue() && |
|
|
|
memory != VK_NULL_HANDLE) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryDeallocation( |
|
|
|
reinterpret_cast<uintptr_t>(memory) |
|
|
|
); |
|
|
|
} |
|
|
|
const size_t n = static_cast<size_t>(std::min<VkDeviceSize>(size, |
|
|
|
(std::numeric_limits<size_t>::max)())); |
|
|
|
return std::span<const u8>{static_cast<const u8 *>(mapped_ptr), n}; |
|
|
|
} |
|
|
|
|
|
|
|
void MemoryCommit::Unmap() |
|
|
|
{ |
|
|
|
if (allocation && mapped_ptr) { |
|
|
|
if (mapped_ptr) { |
|
|
|
vmaUnmapMemory(allocator, allocation); |
|
|
|
mapped_ptr = nullptr; |
|
|
|
} |
|
|
|
vmaFreeMemory(allocator, allocation); |
|
|
|
} |
|
|
|
|
|
|
|
void MemoryCommit::Release() { |
|
|
|
if (allocation && allocator) { |
|
|
|
// Log GPU memory deallocation
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue() && |
|
|
|
memory != VK_NULL_HANDLE) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryDeallocation( |
|
|
|
reinterpret_cast<uintptr_t>(memory) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
if (mapped_ptr) { |
|
|
|
vmaUnmapMemory(allocator, allocation); |
|
|
|
mapped_ptr = nullptr; |
|
|
|
} |
|
|
|
vmaFreeMemory(allocator, allocation); |
|
|
|
} |
|
|
|
allocation = nullptr; |
|
|
|
allocator = nullptr; |
|
|
|
memory = VK_NULL_HANDLE; |
|
|
|
offset = 0; |
|
|
|
size = 0; |
|
|
|
} |
|
|
|
|
|
|
|
MemoryAllocator::MemoryAllocator(const Device &device_) |
|
|
|
: device{device_}, allocator{device.GetAllocator()}, |
|
|
|
properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, |
|
|
|
buffer_image_granularity{ |
|
|
|
device_.GetPhysical().GetProperties().limits.bufferImageGranularity} { |
|
|
|
|
|
|
|
// Preserve the previous "RenderDoc small heap" trimming behavior that we had in original vma minus the heap bug
|
|
|
|
if (device.HasDebuggingToolAttached()) |
|
|
|
{ |
|
|
|
using namespace Common::Literals; |
|
|
|
ForEachDeviceLocalHostVisibleHeap(device, [this](size_t heap_idx, VkMemoryHeap &heap) { |
|
|
|
if (heap.size <= 256_MiB) { |
|
|
|
for (u32 t = 0; t < properties.memoryTypeCount; ++t) { |
|
|
|
if (properties.memoryTypes[t].heapIndex == heap_idx) { |
|
|
|
valid_memory_types &= ~(1u << t); |
|
|
|
} |
|
|
|
allocation = nullptr; |
|
|
|
allocator = nullptr; |
|
|
|
memory = VK_NULL_HANDLE; |
|
|
|
offset = 0; |
|
|
|
size = 0; |
|
|
|
} |
|
|
|
|
|
|
|
MemoryAllocator::MemoryAllocator(const Device &device_) |
|
|
|
: device{device_}, allocator{device.GetAllocator()}, |
|
|
|
properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, |
|
|
|
buffer_image_granularity{ |
|
|
|
device_.GetPhysical().GetProperties().limits.bufferImageGranularity} { |
|
|
|
|
|
|
|
// Preserve the previous "RenderDoc small heap" trimming behavior that we had in original vma minus the heap bug
|
|
|
|
if (device.HasDebuggingToolAttached()) |
|
|
|
{ |
|
|
|
using namespace Common::Literals; |
|
|
|
ForEachDeviceLocalHostVisibleHeap(device, [this](size_t heap_idx, VkMemoryHeap &heap) { |
|
|
|
if (heap.size <= 256_MiB) { |
|
|
|
for (u32 t = 0; t < properties.memoryTypeCount; ++t) { |
|
|
|
if (properties.memoryTypes[t].heapIndex == heap_idx) { |
|
|
|
valid_memory_types &= ~(1u << t); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
MemoryAllocator::~MemoryAllocator() = default; |
|
|
|
|
|
|
|
vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo &ci) const |
|
|
|
{ |
|
|
|
const VmaAllocationCreateInfo alloc_ci = { |
|
|
|
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, |
|
|
|
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, |
|
|
|
.requiredFlags = 0, |
|
|
|
.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, |
|
|
|
.memoryTypeBits = 0, |
|
|
|
.pool = VK_NULL_HANDLE, |
|
|
|
.pUserData = nullptr, |
|
|
|
.priority = 0.f, |
|
|
|
}; |
|
|
|
|
|
|
|
VkImage handle{}; |
|
|
|
VmaAllocation allocation{}; |
|
|
|
VmaAllocationInfo alloc_info{}; |
|
|
|
vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); |
|
|
|
|
|
|
|
// Log GPU memory allocation for images
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue()) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( |
|
|
|
reinterpret_cast<uintptr_t>(alloc_info.deviceMemory), |
|
|
|
static_cast<u64>(alloc_info.size), |
|
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return vk::Image(handle, ci.usage, *device.GetLogical(), allocator, allocation, |
|
|
|
device.GetDispatchLoader()); |
|
|
|
} |
|
|
|
MemoryAllocator::~MemoryAllocator() = default; |
|
|
|
|
|
|
|
vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const { |
|
|
|
// MESA will do memcpy() if not marked as host cached, so just force mark it for most buffers
|
|
|
|
auto const anv_flags = (usage == MemoryUsage::Stream |
|
|
|
&& device.GetDriverID() == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA) |
|
|
|
? VK_MEMORY_PROPERTY_HOST_CACHED_BIT : 0; |
|
|
|
const VmaAllocationCreateInfo alloc_ci = { |
|
|
|
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), |
|
|
|
.usage = MemoryUsageVma(usage), |
|
|
|
vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo &ci) const |
|
|
|
{ |
|
|
|
const VmaAllocationCreateInfo alloc_ci = { |
|
|
|
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, |
|
|
|
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, |
|
|
|
.requiredFlags = 0, |
|
|
|
.preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags, |
|
|
|
.memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, |
|
|
|
.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, |
|
|
|
.memoryTypeBits = 0, |
|
|
|
.pool = VK_NULL_HANDLE, |
|
|
|
.pUserData = nullptr, |
|
|
|
.priority = 0.f, |
|
|
|
}; |
|
|
|
|
|
|
|
VkBuffer handle{}; |
|
|
|
VmaAllocationInfo alloc_info{}; |
|
|
|
VmaAllocation allocation{}; |
|
|
|
VkMemoryPropertyFlags property_flags{}; |
|
|
|
|
|
|
|
vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); |
|
|
|
vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); |
|
|
|
|
|
|
|
// Log GPU memory allocation for buffers
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue()) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( |
|
|
|
reinterpret_cast<uintptr_t>(alloc_info.deviceMemory), |
|
|
|
static_cast<u64>(alloc_info.size), |
|
|
|
property_flags |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
u8 *data = reinterpret_cast<u8 *>(alloc_info.pMappedData); |
|
|
|
const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{}; |
|
|
|
const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0; |
|
|
|
}; |
|
|
|
|
|
|
|
VkImage handle{}; |
|
|
|
VmaAllocation allocation{}; |
|
|
|
VmaAllocationInfo alloc_info{}; |
|
|
|
vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); |
|
|
|
|
|
|
|
// Log GPU memory allocation for images
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue()) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( |
|
|
|
reinterpret_cast<uintptr_t>(alloc_info.deviceMemory), |
|
|
|
static_cast<u64>(alloc_info.size), |
|
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, |
|
|
|
is_coherent, |
|
|
|
device.GetDispatchLoader()); |
|
|
|
return vk::Image(handle, ci.usage, *device.GetLogical(), allocator, allocation, |
|
|
|
device.GetDispatchLoader()); |
|
|
|
} |
|
|
|
|
|
|
|
vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const { |
|
|
|
// MESA will do memcpy() if not marked as host cached, so just force mark it for most buffers
|
|
|
|
auto const anv_flags = (usage == MemoryUsage::Stream |
|
|
|
&& device.GetDriverID() == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA) |
|
|
|
? VK_MEMORY_PROPERTY_HOST_CACHED_BIT : 0; |
|
|
|
const VmaAllocationCreateInfo alloc_ci = { |
|
|
|
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), |
|
|
|
.usage = MemoryUsageVma(usage), |
|
|
|
.requiredFlags = 0, |
|
|
|
.preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags, |
|
|
|
.memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, |
|
|
|
.pool = VK_NULL_HANDLE, |
|
|
|
.pUserData = nullptr, |
|
|
|
.priority = 0.f, |
|
|
|
}; |
|
|
|
|
|
|
|
VkBuffer handle{}; |
|
|
|
VmaAllocationInfo alloc_info{}; |
|
|
|
VmaAllocation allocation{}; |
|
|
|
VkMemoryPropertyFlags property_flags{}; |
|
|
|
|
|
|
|
vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); |
|
|
|
vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); |
|
|
|
|
|
|
|
// Log GPU memory allocation for buffers
|
|
|
|
if (GPU::Logging::IsActive() && |
|
|
|
Settings::values.gpu_log_memory_tracking.GetValue()) { |
|
|
|
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( |
|
|
|
reinterpret_cast<uintptr_t>(alloc_info.deviceMemory), |
|
|
|
static_cast<u64>(alloc_info.size), |
|
|
|
property_flags |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage) |
|
|
|
{ |
|
|
|
const auto vma_usage = MemoryUsageVma(usage); |
|
|
|
VmaAllocationCreateInfo ci{}; |
|
|
|
ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); |
|
|
|
ci.usage = vma_usage; |
|
|
|
ci.memoryTypeBits = reqs.memoryTypeBits & valid_memory_types; |
|
|
|
ci.requiredFlags = 0; |
|
|
|
ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); |
|
|
|
|
|
|
|
VmaAllocation a{}; |
|
|
|
VmaAllocationInfo info{}; |
|
|
|
|
|
|
|
VkResult res = vmaAllocateMemory(allocator, &reqs, &ci, &a, &info); |
|
|
|
|
|
|
|
if (res != VK_SUCCESS) { |
|
|
|
// Relax 1: drop budget constraint
|
|
|
|
auto ci2 = ci; |
|
|
|
ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; |
|
|
|
res = vmaAllocateMemory(allocator, &reqs, &ci2, &a, &info); |
|
|
|
|
|
|
|
// Relax 2: if we preferred DEVICE_LOCAL, drop that preference
|
|
|
|
if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { |
|
|
|
auto ci3 = ci2; |
|
|
|
ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
|
|
|
res = vmaAllocateMemory(allocator, &reqs, &ci3, &a, &info); |
|
|
|
} |
|
|
|
u8 *data = reinterpret_cast<u8 *>(alloc_info.pMappedData); |
|
|
|
const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{}; |
|
|
|
const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0; |
|
|
|
|
|
|
|
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, |
|
|
|
is_coherent, |
|
|
|
device.GetDispatchLoader()); |
|
|
|
} |
|
|
|
|
|
|
|
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage) |
|
|
|
{ |
|
|
|
const auto vma_usage = MemoryUsageVma(usage); |
|
|
|
VmaAllocationCreateInfo ci{}; |
|
|
|
ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); |
|
|
|
ci.usage = vma_usage; |
|
|
|
ci.memoryTypeBits = reqs.memoryTypeBits & valid_memory_types; |
|
|
|
ci.requiredFlags = 0; |
|
|
|
ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); |
|
|
|
|
|
|
|
VmaAllocation a{}; |
|
|
|
VmaAllocationInfo info{}; |
|
|
|
|
|
|
|
VkResult res = vmaAllocateMemory(allocator, &reqs, &ci, &a, &info); |
|
|
|
|
|
|
|
if (res != VK_SUCCESS) { |
|
|
|
// Relax 1: drop budget constraint
|
|
|
|
auto ci2 = ci; |
|
|
|
ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; |
|
|
|
res = vmaAllocateMemory(allocator, &reqs, &ci2, &a, &info); |
|
|
|
|
|
|
|
// Relax 2: if we preferred DEVICE_LOCAL, drop that preference
|
|
|
|
if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { |
|
|
|
auto ci3 = ci2; |
|
|
|
ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
|
|
|
res = vmaAllocateMemory(allocator, &reqs, &ci3, &a, &info); |
|
|
|
} |
|
|
|
|
|
|
|
vk::Check(res); |
|
|
|
return MemoryCommit(allocator, a, info); |
|
|
|
} |
|
|
|
|
|
|
|
MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) { |
|
|
|
// Allocate memory appropriate for this buffer automatically
|
|
|
|
const auto vma_usage = MemoryUsageVma(usage); |
|
|
|
vk::Check(res); |
|
|
|
return MemoryCommit(allocator, a, info); |
|
|
|
} |
|
|
|
|
|
|
|
VmaAllocationCreateInfo ci{}; |
|
|
|
ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); |
|
|
|
ci.usage = vma_usage; |
|
|
|
ci.requiredFlags = 0; |
|
|
|
ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); |
|
|
|
ci.pool = VK_NULL_HANDLE; |
|
|
|
ci.pUserData = nullptr; |
|
|
|
ci.priority = 0.0f; |
|
|
|
MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) { |
|
|
|
// Allocate memory appropriate for this buffer automatically
|
|
|
|
const auto vma_usage = MemoryUsageVma(usage); |
|
|
|
|
|
|
|
const VkBuffer raw = *buffer; |
|
|
|
VmaAllocationCreateInfo ci{}; |
|
|
|
ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); |
|
|
|
ci.usage = vma_usage; |
|
|
|
ci.requiredFlags = 0; |
|
|
|
ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); |
|
|
|
ci.pool = VK_NULL_HANDLE; |
|
|
|
ci.pUserData = nullptr; |
|
|
|
ci.priority = 0.0f; |
|
|
|
|
|
|
|
VmaAllocation a{}; |
|
|
|
VmaAllocationInfo info{}; |
|
|
|
const VkBuffer raw = *buffer; |
|
|
|
|
|
|
|
// Let VMA infer memory requirements from the buffer
|
|
|
|
VkResult res = vmaAllocateMemoryForBuffer(allocator, raw, &ci, &a, &info); |
|
|
|
VmaAllocation a{}; |
|
|
|
VmaAllocationInfo info{}; |
|
|
|
|
|
|
|
if (res != VK_SUCCESS) { |
|
|
|
auto ci2 = ci; |
|
|
|
ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; |
|
|
|
res = vmaAllocateMemoryForBuffer(allocator, raw, &ci2, &a, &info); |
|
|
|
// Let VMA infer memory requirements from the buffer
|
|
|
|
VkResult res = vmaAllocateMemoryForBuffer(allocator, raw, &ci, &a, &info); |
|
|
|
|
|
|
|
if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { |
|
|
|
auto ci3 = ci2; |
|
|
|
ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
|
|
|
res = vmaAllocateMemoryForBuffer(allocator, raw, &ci3, &a, &info); |
|
|
|
} |
|
|
|
} |
|
|
|
if (res != VK_SUCCESS) { |
|
|
|
auto ci2 = ci; |
|
|
|
ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; |
|
|
|
res = vmaAllocateMemoryForBuffer(allocator, raw, &ci2, &a, &info); |
|
|
|
|
|
|
|
vk::Check(res); |
|
|
|
vk::Check(vmaBindBufferMemory2(allocator, a, 0, raw, nullptr)); |
|
|
|
return MemoryCommit(allocator, a, info); |
|
|
|
if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { |
|
|
|
auto ci3 = ci2; |
|
|
|
ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
|
|
|
res = vmaAllocateMemoryForBuffer(allocator, raw, &ci3, &a, &info); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
vk::Check(res); |
|
|
|
vk::Check(vmaBindBufferMemory2(allocator, a, 0, raw, nullptr)); |
|
|
|
return MemoryCommit(allocator, a, info); |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace Vulkan
|