1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-03-14 04:29:15 +01:00

[dxvk] Clean up internal memory allocation API

This commit is contained in:
Philip Rebohle 2022-08-25 18:20:11 +02:00
parent 5117210c93
commit a2a21cb4d3
4 changed files with 165 additions and 136 deletions

View File

@ -65,25 +65,32 @@ namespace dxvk {
DxvkBufferHandle handle;
if (vkd->vkCreateBuffer(vkd->device(),
&info, nullptr, &handle.buffer) != VK_SUCCESS) {
if (vkd->vkCreateBuffer(vkd->device(), &info, nullptr, &handle.buffer)) {
throw DxvkError(str::format(
"DxvkBuffer: Failed to create buffer:"
"\n size: ", info.size,
"\n usage: ", info.usage));
}
VkMemoryDedicatedRequirements dedicatedRequirements = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS };
VkMemoryRequirements2 memReq = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements };
VkBufferMemoryRequirementsInfo2 memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 };
memReqInfo.buffer = handle.buffer;
VkMemoryDedicatedAllocateInfo dedMemoryAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO };
dedMemoryAllocInfo.buffer = handle.buffer;
vkd->vkGetBufferMemoryRequirements2(
vkd->device(), &memReqInfo, &memReq);
// Query memory requirements and whether to use a dedicated allocation
DxvkMemoryRequirements memoryRequirements = { };
memoryRequirements.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS };
memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &memoryRequirements.dedicated };
VkBufferMemoryRequirementsInfo2 memoryRequirementInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 };
memoryRequirementInfo.buffer = handle.buffer;
vkd->vkGetBufferMemoryRequirements2(vkd->device(),
&memoryRequirementInfo, &memoryRequirements.core);
// Fill in desired memory properties
DxvkMemoryProperties memoryProperties = { };
memoryProperties.flags = m_memFlags;
if (memoryRequirements.dedicated.prefersDedicatedAllocation) {
memoryProperties.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO };
memoryProperties.dedicated.buffer = handle.buffer;
}
// Use high memory priority for GPU-writable resources
bool isGpuWritable = (m_info.access & (
@ -102,9 +109,7 @@ namespace dxvk {
&& (m_info.usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
hints.set(DxvkMemoryFlag::Transient);
// Ask driver whether we should be using a dedicated allocation
handle.memory = m_memAlloc->alloc(&memReq.memoryRequirements,
dedicatedRequirements, dedMemoryAllocInfo, m_memFlags, hints);
handle.memory = m_memAlloc->alloc(memoryRequirements, memoryProperties, hints);
if (vkd->vkBindBufferMemory(vkd->device(), handle.buffer,
handle.memory.memory(), handle.memory.offset()) != VK_SUCCESS)

View File

@ -22,7 +22,10 @@ namespace dxvk {
VkImageFormatListCreateInfo formatList = { VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO };
formatList.viewFormatCount = createInfo.viewFormatCount;
formatList.pViewFormats = createInfo.viewFormats;
VkExternalMemoryImageCreateInfo externalInfo = { VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO };
externalInfo.handleTypes = createInfo.sharing.type;
VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, &formatList };
info.flags = createInfo.flags;
info.imageType = createInfo.type;
@ -36,17 +39,10 @@ namespace dxvk {
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.initialLayout = createInfo.initialLayout;
m_shared = canShareImage(info, createInfo.sharing);
VkExternalMemoryImageCreateInfo externalInfo = { VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO };
if (m_shared) {
if ((m_shared = canShareImage(info, createInfo.sharing)))
externalInfo.pNext = std::exchange(info.pNext, &externalInfo);
externalInfo.handleTypes = createInfo.sharing.type;
}
if (m_vkd->vkCreateImage(m_vkd->device(),
&info, nullptr, &m_image.image) != VK_SUCCESS) {
if (m_vkd->vkCreateImage(m_vkd->device(), &info, nullptr, &m_image.image)) {
throw DxvkError(str::format(
"DxvkImage: Failed to create image:",
"\n Type: ", info.imageType,
@ -61,40 +57,52 @@ namespace dxvk {
"\n Tiling: ", info.tiling));
}
// Get memory requirements for the image. We may enforce strict
// alignment on non-linear images in order not to violate the
// bufferImageGranularity limit, which may be greater than the
// required resource memory alignment on some GPUs.
VkMemoryDedicatedRequirements dedicatedRequirements = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS };
VkMemoryRequirements2 memReq = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements };
VkImageMemoryRequirementsInfo2 memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 };
memReqInfo.image = m_image.image;
// Get memory requirements for the image and ask driver
// whether we need to use a dedicated allocation.
DxvkMemoryRequirements memoryRequirements = { };
memoryRequirements.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS };
memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &memoryRequirements.dedicated };
VkMemoryDedicatedAllocateInfo dedMemoryAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO };
dedMemoryAllocInfo.image = m_image.image;
VkImageMemoryRequirementsInfo2 memoryRequirementInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 };
memoryRequirementInfo.image = m_image.image;
VkExportMemoryAllocateInfo exportInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO };
if (m_shared && createInfo.sharing.mode == DxvkSharedHandleMode::Export) {
exportInfo.pNext = std::exchange(dedMemoryAllocInfo.pNext, &exportInfo);
exportInfo.handleTypes = createInfo.sharing.type;
m_vkd->vkGetImageMemoryRequirements2(m_vkd->device(),
&memoryRequirementInfo, &memoryRequirements.core);
// Fill in desired memory properties
DxvkMemoryProperties memoryProperties = { };
memoryProperties.flags = m_memFlags;
if (m_shared) {
memoryRequirements.dedicated.prefersDedicatedAllocation = VK_TRUE;
memoryRequirements.dedicated.requiresDedicatedAllocation = VK_TRUE;
if (createInfo.sharing.mode == DxvkSharedHandleMode::Export) {
memoryProperties.sharedExport = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO };
memoryProperties.sharedExport.handleTypes = createInfo.sharing.type;
}
if (createInfo.sharing.mode == DxvkSharedHandleMode::Import) {
memoryProperties.sharedImportWin32 = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR };
memoryProperties.sharedImportWin32.handleType = createInfo.sharing.type;
memoryProperties.sharedImportWin32.handle = createInfo.sharing.handle;
}
}
#ifdef _WIN32
VkImportMemoryWin32HandleInfoKHR importInfo = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR };
if (m_shared && createInfo.sharing.mode == DxvkSharedHandleMode::Import) {
importInfo.pNext = std::exchange(dedMemoryAllocInfo.pNext, &importInfo);
importInfo.handleType = createInfo.sharing.type;
importInfo.handle = createInfo.sharing.handle;
if (memoryRequirements.dedicated.prefersDedicatedAllocation) {
memoryProperties.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO };
memoryProperties.dedicated.image = m_image.image;
}
#endif
m_vkd->vkGetImageMemoryRequirements2(
m_vkd->device(), &memReqInfo, &memReq);
// If there's a chance we won't create the image with a dedicated
// allocation, enforce strict alignment for tiled images to not
// violate the bufferImageGranularity requirement on some GPUs.
if (info.tiling != VK_IMAGE_TILING_LINEAR && !memoryRequirements.dedicated.requiresDedicatedAllocation) {
VkDeviceSize granularity = memAlloc.bufferImageGranularity();
if (info.tiling != VK_IMAGE_TILING_LINEAR && !dedicatedRequirements.prefersDedicatedAllocation) {
memReq.memoryRequirements.size = align(memReq.memoryRequirements.size, memAlloc.bufferImageGranularity());
memReq.memoryRequirements.alignment = align(memReq.memoryRequirements.alignment , memAlloc.bufferImageGranularity());
auto& core = memoryRequirements.core.memoryRequirements;
core.size = align(core.size, granularity);
core.alignment = align(core.alignment, granularity);
}
// Use high memory priority for GPU-writable resources
@ -110,14 +118,7 @@ namespace dxvk {
if (isGpuWritable)
hints.set(DxvkMemoryFlag::GpuWritable);
if (m_shared) {
dedicatedRequirements.prefersDedicatedAllocation = VK_TRUE;
dedicatedRequirements.requiresDedicatedAllocation = VK_TRUE;
}
// Ask driver whether we should be using a dedicated allocation
m_image.memory = memAlloc.alloc(&memReq.memoryRequirements,
dedicatedRequirements, dedMemoryAllocInfo, memFlags, hints);
m_image.memory = memAlloc.alloc(memoryRequirements, memoryProperties, hints);
// Try to bind the allocated memory slice to the image
if (m_vkd->vkBindImageMemory(m_vkd->device(), m_image.image,

View File

@ -211,63 +211,66 @@ namespace dxvk {
DxvkMemory DxvkMemoryAllocator::alloc(
const VkMemoryRequirements* req,
const VkMemoryDedicatedRequirements& dedAllocReq,
const VkMemoryDedicatedAllocateInfo& dedAllocInfo,
VkMemoryPropertyFlags flags,
const DxvkMemoryRequirements& req,
DxvkMemoryProperties info,
DxvkMemoryFlags hints) {
std::lock_guard<dxvk::mutex> lock(m_mutex);
// Keep small allocations together to avoid fragmenting
// chunks for larger resources with lots of small gaps,
// as well as resources with potentially weird lifetimes
if (req->size <= SmallAllocationThreshold) {
if (req.core.memoryRequirements.size <= SmallAllocationThreshold) {
hints.set(DxvkMemoryFlag::Small);
hints.clr(DxvkMemoryFlag::GpuWritable, DxvkMemoryFlag::GpuReadable);
}
// Ignore most hints for host-visible allocations since they
// usually don't make much sense for those resources
if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
if (info.flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
hints = hints & DxvkMemoryFlag::Transient;
// Try to allocate from a memory type which supports the given flags exactly
auto dedAllocPtr = dedAllocReq.prefersDedicatedAllocation ? &dedAllocInfo : nullptr;
DxvkMemory result = this->tryAlloc(req, dedAllocPtr, flags, hints);
DxvkMemory result = this->tryAlloc(req, info, hints);
// If the first attempt failed, try ignoring the dedicated allocation
if (!result && dedAllocPtr && !dedAllocReq.requiresDedicatedAllocation) {
result = this->tryAlloc(req, nullptr, flags, hints);
dedAllocPtr = nullptr;
if (!result && !req.dedicated.requiresDedicatedAllocation) {
// If that failed, try without a dedicated allocation
if (info.dedicated.image || info.dedicated.buffer) {
info.dedicated.image = VK_NULL_HANDLE;
info.dedicated.buffer = VK_NULL_HANDLE;
result = this->tryAlloc(req, info, hints);
}
}
// Retry without the hint constraints
if (!result) {
// Retry without the hint constraints
hints.set(DxvkMemoryFlag::IgnoreConstraints);
result = this->tryAlloc(req, nullptr, flags, hints);
result = this->tryAlloc(req, info, hints);
}
// If that still didn't work, probe slower memory types as well
VkMemoryPropertyFlags optFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
VkMemoryPropertyFlags remFlags = 0;
while (!result && (flags & optFlags)) {
remFlags |= optFlags & -optFlags;
optFlags &= ~remFlags;
if (!result) {
// If that still didn't work, probe slower memory types as well
VkMemoryPropertyFlags optFlags = info.flags & (
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
result = this->tryAlloc(req, dedAllocPtr, flags & ~remFlags, hints);
while (!result && optFlags) {
VkMemoryPropertyFlags bit = optFlags & -optFlags;
optFlags &= ~bit;
info.flags &= ~bit;
result = this->tryAlloc(req, info, hints);
}
}
if (!result) {
DxvkAdapterMemoryInfo memHeapInfo = m_device->adapter()->getMemoryHeapInfo();
Logger::err(str::format(
"DxvkMemoryAllocator: Memory allocation failed",
"\n Size: ", req->size,
"\n Alignment: ", req->alignment,
"\n Mem flags: ", "0x", std::hex, flags,
"\n Mem types: ", "0x", std::hex, req->memoryTypeBits));
"\n Size: ", req.core.memoryRequirements.size,
"\n Alignment: ", req.core.memoryRequirements.alignment,
"\n Mem types: ", "0x", std::hex, req.core.memoryRequirements.memoryTypeBits));
for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) {
Logger::err(str::format("Heap ", i, ": ",
@ -290,19 +293,20 @@ namespace dxvk {
DxvkMemory DxvkMemoryAllocator::tryAlloc(
const VkMemoryRequirements* req,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
VkMemoryPropertyFlags flags,
const DxvkMemoryRequirements& req,
const DxvkMemoryProperties& info,
DxvkMemoryFlags hints) {
DxvkMemory result;
for (uint32_t i = 0; i < m_memProps.memoryTypeCount && !result; i++) {
const bool supported = (req->memoryTypeBits & (1u << i)) != 0;
const bool adequate = (m_memTypes[i].memType.propertyFlags & flags) == flags;
const bool supported = (req.core.memoryRequirements.memoryTypeBits & (1u << i)) != 0;
const bool adequate = (m_memTypes[i].memType.propertyFlags & info.flags) == info.flags;
if (supported && adequate) {
result = this->tryAllocFromType(&m_memTypes[i],
flags, req->size, req->alignment, hints, dedAllocInfo);
req.core.memoryRequirements.size,
req.core.memoryRequirements.alignment,
info, hints);
}
}
@ -312,27 +316,25 @@ namespace dxvk {
DxvkMemory DxvkMemoryAllocator::tryAllocFromType(
DxvkMemoryType* type,
VkMemoryPropertyFlags flags,
VkDeviceSize size,
VkDeviceSize align,
DxvkMemoryFlags hints,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo) {
const DxvkMemoryProperties& info,
DxvkMemoryFlags hints) {
VkDeviceSize chunkSize = pickChunkSize(type->memTypeId, hints);
DxvkMemory memory;
if (size >= chunkSize || dedAllocInfo) {
if (size >= chunkSize || info.dedicated.buffer || info.dedicated.image) {
if (this->shouldFreeEmptyChunks(type->heap, size))
this->freeEmptyChunks(type->heap);
DxvkDeviceMemory devMem = this->tryAllocDeviceMemory(
type, flags, size, hints, dedAllocInfo);
DxvkDeviceMemory devMem = this->tryAllocDeviceMemory(type, size, info, hints);
if (devMem.memHandle != VK_NULL_HANDLE)
memory = DxvkMemory(this, nullptr, type, devMem.memHandle, 0, size, devMem.memPointer);
} else {
for (uint32_t i = 0; i < type->chunks.size() && !memory; i++)
memory = type->chunks[i]->alloc(flags, size, align, hints);
memory = type->chunks[i]->alloc(info.flags, size, align, hints);
if (!memory) {
DxvkDeviceMemory devMem;
@ -341,11 +343,11 @@ namespace dxvk {
this->freeEmptyChunks(type->heap);
for (uint32_t i = 0; i < 6 && (chunkSize >> i) >= size && !devMem.memHandle; i++)
devMem = tryAllocDeviceMemory(type, flags, chunkSize >> i, hints, nullptr);
devMem = tryAllocDeviceMemory(type, chunkSize >> i, info, hints);
if (devMem.memHandle) {
Rc<DxvkMemoryChunk> chunk = new DxvkMemoryChunk(this, type, devMem, hints);
memory = chunk->alloc(flags, size, align, hints);
memory = chunk->alloc(info.flags, size, align, hints);
type->chunks.push_back(std::move(chunk));
}
@ -361,11 +363,10 @@ namespace dxvk {
DxvkDeviceMemory DxvkMemoryAllocator::tryAllocDeviceMemory(
DxvkMemoryType* type,
VkMemoryPropertyFlags flags,
VkDeviceSize size,
DxvkMemoryFlags hints,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo) {
bool useMemoryPriority = (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
DxvkMemoryProperties info,
DxvkMemoryFlags hints) {
bool useMemoryPriority = (info.flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
&& (m_device->features().extMemoryPriority.memoryPriority);
if (type->heap->budget && type->heap->stats.memoryAllocated + size > type->heap->budget)
@ -380,23 +381,32 @@ namespace dxvk {
DxvkDeviceMemory result;
result.memSize = size;
result.memFlags = flags;
result.memFlags = info.flags;
result.priority = priority;
VkMemoryPriorityAllocateInfoEXT prio = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
prio.priority = priority;
VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
priorityInfo.priority = priority;
VkMemoryAllocateInfo info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, dedAllocInfo };
info.allocationSize = size;
info.memoryTypeIndex = type->memTypeId;
VkMemoryAllocateInfo memoryInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
memoryInfo.allocationSize = size;
memoryInfo.memoryTypeIndex = type->memTypeId;
if (info.sharedExport.handleTypes)
info.sharedExport.pNext = std::exchange(memoryInfo.pNext, &info.sharedExport);
if (info.sharedImportWin32.handleType)
info.sharedImportWin32.pNext = std::exchange(memoryInfo.pNext, &info.sharedImportWin32);
if (info.dedicated.buffer || info.dedicated.image)
info.dedicated.pNext = std::exchange(memoryInfo.pNext, &info.dedicated);
if (useMemoryPriority)
prio.pNext = std::exchange(info.pNext, &prio);
priorityInfo.pNext = std::exchange(memoryInfo.pNext, &priorityInfo);
if (m_vkd->vkAllocateMemory(m_vkd->device(), &info, nullptr, &result.memHandle) != VK_SUCCESS)
if (m_vkd->vkAllocateMemory(m_vkd->device(), &memoryInfo, nullptr, &result.memHandle) != VK_SUCCESS)
return DxvkDeviceMemory();
if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
if (info.flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
VkResult status = m_vkd->vkMapMemory(m_vkd->device(), result.memHandle, 0, VK_WHOLE_SIZE, 0, &result.memPointer);
if (status != VK_SUCCESS) {

View File

@ -271,8 +271,28 @@ namespace dxvk {
bool checkHints(DxvkMemoryFlags hints) const;
};
/**
* \brief Memory requirement info
*/
struct DxvkMemoryRequirements {
VkMemoryDedicatedRequirements dedicated;
VkMemoryRequirements2 core;
};
/**
* \brief Memory allocation info
*/
struct DxvkMemoryProperties {
VkExportMemoryAllocateInfo sharedExport;
VkImportMemoryWin32HandleInfoKHR sharedImportWin32;
VkMemoryDedicatedAllocateInfo dedicated;
VkMemoryPropertyFlags flags;
};
/**
* \brief Memory allocator
*
@ -305,17 +325,13 @@ namespace dxvk {
* \brief Allocates device memory
*
* \param [in] req Memory requirements
* \param [in] dedAllocReq Dedicated allocation requirements
* \param [in] dedAllocInfo Dedicated allocation info
* \param [in] flags Memory type flags
* \param [in] info Memory properties
* \param [in] hints Memory hints
* \returns Allocated memory slice
*/
DxvkMemory alloc(
const VkMemoryRequirements* req,
const VkMemoryDedicatedRequirements& dedAllocReq,
const VkMemoryDedicatedAllocateInfo& dedAllocInfo,
VkMemoryPropertyFlags flags,
const DxvkMemoryRequirements& req,
DxvkMemoryProperties info,
DxvkMemoryFlags hints);
/**
@ -342,25 +358,22 @@ namespace dxvk {
std::array<DxvkMemoryType, VK_MAX_MEMORY_TYPES> m_memTypes;
DxvkMemory tryAlloc(
const VkMemoryRequirements* req,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
VkMemoryPropertyFlags flags,
const DxvkMemoryRequirements& req,
const DxvkMemoryProperties& info,
DxvkMemoryFlags hints);
DxvkMemory tryAllocFromType(
DxvkMemoryType* type,
VkMemoryPropertyFlags flags,
VkDeviceSize size,
VkDeviceSize align,
DxvkMemoryFlags hints,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo);
const DxvkMemoryProperties& info,
DxvkMemoryFlags hints);
DxvkDeviceMemory tryAllocDeviceMemory(
DxvkMemoryType* type,
VkMemoryPropertyFlags flags,
VkDeviceSize size,
DxvkMemoryFlags hints,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo);
DxvkMemoryProperties info,
DxvkMemoryFlags hints);
void free(
const DxvkMemory& memory);