xash3d-fwgs/ref/vk/vk_devmem.c

204 lines
6.5 KiB
C

#include "vk_devmem.h"
#include "alolcator.h"
#define MAX_DEVMEM_ALLOCS 16
#define DEFAULT_ALLOCATION_SIZE (64 * 1024 * 1024)
typedef struct {
uint32_t type_index;
VkMemoryPropertyFlags property_flags; // device vs host
VkMemoryAllocateFlags allocate_flags;
VkDeviceMemory device_memory;
VkDeviceSize size;
void *map;
int refcount;
struct alo_pool_s *allocator;
} vk_device_memory_t;
static struct {
vk_device_memory_t allocs[MAX_DEVMEM_ALLOCS];
int num_allocs;
qboolean verbose;
} g_vk_devmem;
static int findMemoryWithType(uint32_t type_index_bits, VkMemoryPropertyFlags flags) {
for (int i = 0; i < (int)vk_core.physical_device.memory_properties2.memoryProperties.memoryTypeCount; ++i) {
if (!(type_index_bits & (1 << i)))
continue;
if ((vk_core.physical_device.memory_properties2.memoryProperties.memoryTypes[i].propertyFlags & flags) == flags)
return i;
}
return UINT32_MAX;
}
static VkDeviceSize optimalSize(VkDeviceSize size) {
if (size < DEFAULT_ALLOCATION_SIZE)
return DEFAULT_ALLOCATION_SIZE;
// TODO:
// 1. have a way to iterate for smaller sizes if allocation failed
// 2. bump to nearest power-of-two-ish based size (e.g. a multiple of 32Mb or something)
return size;
}
static int allocateDeviceMemory(VkMemoryRequirements req, int type_index, VkMemoryAllocateFlags allocate_flags) {
if (g_vk_devmem.num_allocs == MAX_DEVMEM_ALLOCS) {
gEngine.Host_Error("Ran out of device memory allocation slots\n");
return -1;
}
{
const VkMemoryAllocateFlagsInfo mafi = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
.flags = allocate_flags,
};
const VkMemoryAllocateInfo mai = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = allocate_flags ? &mafi : NULL,
.allocationSize = optimalSize(req.size),
.memoryTypeIndex = type_index,
};
if (g_vk_devmem.verbose) {
gEngine.Con_Reportf("allocateDeviceMemory size=%llu memoryTypeBits=0x%x allocate_flags=0x%x => typeIndex=%d\n",
(unsigned long long)mai.allocationSize, req.memoryTypeBits,
allocate_flags,
mai.memoryTypeIndex);
}
ASSERT(mai.memoryTypeIndex != UINT32_MAX);
vk_device_memory_t *device_memory = g_vk_devmem.allocs + g_vk_devmem.num_allocs;
XVK_CHECK(vkAllocateMemory(vk_core.device, &mai, NULL, &device_memory->device_memory));
device_memory->property_flags = vk_core.physical_device.memory_properties2.memoryProperties.memoryTypes[mai.memoryTypeIndex].propertyFlags;
device_memory->allocate_flags = allocate_flags;
device_memory->type_index = mai.memoryTypeIndex;
device_memory->refcount = 0;
device_memory->size = mai.allocationSize;
device_memory->allocator = aloPoolCreate(device_memory->size, 0, 16);
if (device_memory->property_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
XVK_CHECK(vkMapMemory(vk_core.device, device_memory->device_memory, 0, device_memory->size, 0, &device_memory->map));
} else {
device_memory->map = NULL;
}
}
return g_vk_devmem.num_allocs++;
}
vk_devmem_t VK_DevMemAllocate(const char *name, VkMemoryRequirements req, VkMemoryPropertyFlags prop_flags, VkMemoryAllocateFlags allocate_flags) {
vk_devmem_t ret = {0};
int device_memory_index = -1;
alo_block_t block;
const int type_index = findMemoryWithType(req.memoryTypeBits, prop_flags);
if (g_vk_devmem.verbose) {
gEngine.Con_Reportf("VK_DevMemAllocate name=\"%s\" size=%llu alignment=%llu memoryTypeBits=0x%x prop_flags=%c%c%c%c%c allocate_flags=0x%x => type_index=%d\n",
name, (unsigned long long)req.size, (unsigned long long)req.alignment, req.memoryTypeBits,
prop_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ? 'D' : '.',
prop_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ? 'V' : '.',
prop_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ? 'C' : '.',
prop_flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT ? '$' : '.',
prop_flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT ? 'L' : '.',
allocate_flags, type_index);
}
if (vk_core.rtx) {
// TODO this is needed only for the ray tracer and only while there's no proper staging
// Once staging is established, we can avoid forcing this on every devmem allocation
allocate_flags |= VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
}
for (int i = 0; i < g_vk_devmem.num_allocs; ++i) {
vk_device_memory_t *const device_memory = g_vk_devmem.allocs + i;
if (device_memory->type_index != type_index)
continue;
if ((device_memory->allocate_flags & allocate_flags) != allocate_flags)
continue;
if ((device_memory->property_flags & prop_flags) != prop_flags)
continue;
block = aloPoolAllocate(device_memory->allocator, req.size, req.alignment);
if (block.size == 0)
continue;
device_memory_index = i;
break;
}
if (device_memory_index < 0) {
device_memory_index = allocateDeviceMemory(req, type_index, allocate_flags);
ASSERT(device_memory_index >= 0);
if (device_memory_index < 0)
return ret;
block = aloPoolAllocate(g_vk_devmem.allocs[device_memory_index].allocator, req.size, req.alignment);
ASSERT(block.size != 0);
}
{
vk_device_memory_t *const device_memory = g_vk_devmem.allocs + device_memory_index;
ret.device_memory = device_memory->device_memory;
ret.offset = block.offset;
ret.mapped = device_memory->map ? (char*)device_memory->map + block.offset : NULL;
if (g_vk_devmem.verbose) {
gEngine.Con_Reportf("Allocated devmem=%d block=%d offset=%d size=%d\n", device_memory_index, block.index, (int)block.offset, (int)block.size);
}
device_memory->refcount++;
ret.priv_.devmem = device_memory_index;
ret.priv_.block = block.index;
return ret;
}
}
void VK_DevMemFree(const vk_devmem_t *mem) {
ASSERT(mem->priv_.devmem >= 0);
ASSERT(mem->priv_.devmem < g_vk_devmem.num_allocs);
vk_device_memory_t *const device_memory = g_vk_devmem.allocs + mem->priv_.devmem;
ASSERT(mem->device_memory == device_memory->device_memory);
if (g_vk_devmem.verbose) {
gEngine.Con_Reportf("Freeing devmem=%d block=%d\n", mem->priv_.devmem, mem->priv_.block);
}
aloPoolFree(device_memory->allocator, mem->priv_.block);
device_memory->refcount--;
}
qboolean VK_DevMemInit( void ) {
g_vk_devmem.verbose = !!gEngine.Sys_CheckParm("-vkdebugmem");
return true;
}
void VK_DevMemDestroy( void ) {
for (int i = 0; i < g_vk_devmem.num_allocs; ++i) {
const vk_device_memory_t *const device_memory = g_vk_devmem.allocs + i;
ASSERT(device_memory->refcount == 0);
// TODO check that everything has been freed
aloPoolDestroy(device_memory->allocator);
if (device_memory->map)
vkUnmapMemory(vk_core.device, device_memory->device_memory);
vkFreeMemory(vk_core.device, device_memory->device_memory, NULL);
}
g_vk_devmem.num_allocs = 0;
}