mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-15 13:41:33 +01:00
204 lines
6.5 KiB
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;
|
|
}
|