Last active
March 20, 2021 19:58
-
-
Save Eichenherz/4cf32e71e622ae6e8f15a1494a2b654e to your computer and use it in GitHub Desktop.
Vulkan Growing Linear Arena
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct vk_mem_view | |
{ | |
VkDeviceMemory device; | |
void* host = 0; | |
}; | |
struct vk_allocation | |
{ | |
VkDeviceMemory deviceMem; | |
u8* hostVisible = 0; | |
u64 dataOffset; | |
}; | |
struct vk_mem_arena | |
{ | |
VkDevice device; | |
vector<vk_mem_view> mem; | |
vector<vk_mem_view> dedicatedAllocs; | |
u64 minArenaSize = VK_MIN_DEVICE_BLOCK_SIZE; | |
u64 maxParentHeapSize; | |
u64 size; | |
u64 allocated; | |
VkMemoryPropertyFlags memType; | |
VkPhysicalDeviceMemoryProperties gpuMemProps; | |
}; | |
inline i32 | |
VkFindMemTypeIdx( | |
VkPhysicalDeviceMemoryProperties* pVkMemProps, | |
VkMemoryPropertyFlags requiredProps, | |
u32 memTypeBitsRequirement ) | |
{ | |
for( u64 memIdx = 0; memIdx < pVkMemProps->memoryTypeCount; ++memIdx ){ | |
u32 memTypeBits = ( 1 << memIdx ); | |
b32 isRequiredMemType = memTypeBitsRequirement & memTypeBits; | |
VkMemoryPropertyFlags props = pVkMemProps->memoryTypes[ memIdx ].propertyFlags; | |
b32 hasRequiredProps = ( props & requiredProps ) == requiredProps; | |
if( isRequiredMemType && hasRequiredProps ) | |
return (i32) memIdx; | |
} | |
VK_CHECK( "Memory type unmatch !" ); | |
return -1; | |
} | |
// TODO: move alloc flags ? | |
inline static vk_mem_view | |
VkTryAllocDeviceMem( | |
VkDevice vkDevice, | |
u64 size, | |
u32 memTypeIdx, | |
VkMemoryPropertyFlags memFlags, | |
VkMemoryAllocateFlags allocFlags, | |
const VkMemoryDedicatedAllocateInfo* dedicated ) | |
{ | |
VkMemoryAllocateFlagsInfo allocFlagsInfo = {}; | |
allocFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; | |
allocFlagsInfo.pNext = dedicated; | |
allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;// allocFlags; | |
VkMemoryAllocateInfo memoryAllocateInfo = {}; | |
memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | |
memoryAllocateInfo.pNext = &allocFlagsInfo; | |
memoryAllocateInfo.allocationSize = size; | |
memoryAllocateInfo.memoryTypeIndex = memTypeIdx; | |
vk_mem_view vkMemView; | |
VK_CHECK( vkAllocateMemory( vkDevice, &memoryAllocateInfo, 0, &vkMemView.device ) ); | |
if( memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ) | |
VK_CHECK( vkMapMemory( vkDevice, vkMemView.device, 0, VK_WHOLE_SIZE, 0, &vkMemView.host ) ); | |
return vkMemView; | |
} | |
inline static void | |
VkArenaInit( | |
vk_mem_arena* vkArena, | |
VkPhysicalDeviceMemoryProperties memProps, | |
VkMemoryPropertyFlags memType, | |
VkDevice vkDevice ) | |
{ | |
i32 i = 0; | |
for( ; i < memProps.memoryTypeCount; ++i ) | |
if( memProps.memoryTypes[ i ].propertyFlags == memType ) break; | |
VK_CHECK( i == memProps.memoryTypeCount ); | |
vkArena->allocated = 0; | |
vkArena->size = 0; | |
vkArena->maxParentHeapSize = memProps.memoryHeaps[ memProps.memoryTypes[ i ].heapIndex ].size; | |
vkArena->memType = memType; | |
vkArena->device = vkDevice; | |
vkArena->gpuMemProps = memProps; | |
vkArena->minArenaSize = | |
( vkArena->maxParentHeapSize < VK_MIN_DEVICE_BLOCK_SIZE ) ? | |
( 1 * MB ) : | |
VK_MIN_DEVICE_BLOCK_SIZE; | |
} | |
inline vk_allocation | |
VkArenaAlignAlloc( | |
vk_mem_arena* vkArena, | |
u64 size, | |
u64 align, | |
u32 memTypeIdx, | |
VkMemoryAllocateFlags allocFlags, | |
const VkMemoryDedicatedAllocateInfo* dedicated ) | |
{ | |
u64 allocatedWithOffset = FwdAlign( vkArena->allocated, align ); | |
vkArena->allocated = allocatedWithOffset; | |
assert( size <= vkArena->maxParentHeapSize ); | |
if( dedicated ){ | |
vkArena->dedicatedAllocs.push_back( | |
VkTryAllocDeviceMem( vkArena->device, size, memTypeIdx, vkArena->memType, allocFlags, dedicated ) ); | |
return { vkArena->dedicatedAllocs.back().device, (u8*) vkArena->dedicatedAllocs.back().host, 0 }; | |
} | |
if( ( vkArena->allocated + size ) > vkArena->size ){ | |
u64 newArenaSize = max( size, vkArena->minArenaSize ); | |
vkArena->mem.push_back( | |
VkTryAllocDeviceMem( vkArena->device, newArenaSize, memTypeIdx, vkArena->memType, allocFlags, 0 ) ); | |
vkArena->size = newArenaSize; | |
vkArena->allocated = 0; | |
} | |
assert( ( vkArena->allocated + size ) <= vkArena->size ); | |
assert( vkArena->allocated % align == 0 ); | |
vk_allocation allocId = { vkArena->mem.back().device, (u8*) vkArena->mem.back().host, vkArena->allocated }; | |
vkArena->allocated += size; | |
return allocId; | |
} | |
inline void | |
VkArenaTerimate( vk_mem_arena* vkArena ) | |
{ | |
for( u64 i = 0; i < vkArena->mem.size(); ++i ) | |
vkFreeMemory( vkArena->device, vkArena->mem[ i ].device, 0 ); | |
for( u64 i = 0; i < vkArena->dedicatedAllocs.size(); ++i ) | |
vkFreeMemory( vkArena->device, vkArena->dedicatedAllocs[ i ].device, 0 ); | |
} | |
inline static void | |
VkInitMemory( VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice ) | |
{ | |
VkPhysicalDeviceMemoryProperties memProps; | |
vkGetPhysicalDeviceMemoryProperties( vkPhysicalDevice, &memProps ); | |
VkArenaInit( &vkRscArena, | |
memProps, | |
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
vkDevice ); | |
VkArenaInit( &vkAlbumArena, | |
memProps, | |
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
vkDevice ); | |
VkArenaInit( &vkStagingArena, | |
memProps, | |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | | |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, | |
vkDevice ); | |
VkArenaInit( &vkHostComArena, | |
memProps, | |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | | |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | | |
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
vkDevice ); | |
VkArenaInit( &vkDbgArena, | |
memProps, | |
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
vkDevice ); | |
} | |
__forceinline u64 VkGetBufferDeviceAddress( VkDevice vkDevice, VkBuffer hndl ) | |
{ | |
#ifdef _VK_DEBUG_ | |
static_assert( std::is_same<VkDeviceAddress, u64>::value ); | |
#endif | |
VkBufferDeviceAddressInfo deviceAddrInfo = {}; | |
deviceAddrInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; | |
deviceAddrInfo.buffer = hndl; | |
return vkGetBufferDeviceAddress( vkDevice, &deviceAddrInfo ); | |
} | |
// TODO: return/use alignmed size ? | |
static buffer_data | |
VkCreateAllocBindBuffer( | |
u64 sizeInBytes, | |
VkBufferUsageFlags usage, | |
vk_mem_arena* vkArena ) | |
{ | |
VkBufferCreateInfo bufferInfo = {}; | |
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | |
bufferInfo.size = sizeInBytes; | |
bufferInfo.usage = usage; | |
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
VK_CHECK( vkCreateBuffer( vkArena->device, &bufferInfo, 0, &buffData.hndl ) ); | |
VkMemoryDedicatedRequirements dedicatedReqs = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; | |
VkMemoryRequirements2 memReqs2 = {}; | |
memReqs2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; | |
memReqs2.pNext = &dedicatedReqs; | |
VkBufferMemoryRequirementsInfo2 buffMemReqs2 = {}; | |
buffMemReqs2.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2; | |
buffMemReqs2.buffer = buffData.hndl; | |
vkGetBufferMemoryRequirements2( vkArena->device, &buffMemReqs2, &memReqs2 ); | |
i32 memTypeIdx = VkFindMemTypeIdx( &vkArena->gpuMemProps, vkArena->memType, memReqs2.memoryRequirements.memoryTypeBits ); | |
VkMemoryAllocateFlags allocFlags = | |
( usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT ) ? VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT : 0; | |
VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo = {}; | |
dedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; | |
dedicatedAllocateInfo.buffer = buffMemReqs2.buffer; | |
b32 dedicatedAlloc = dedicatedReqs.prefersDedicatedAllocation || dedicatedReqs.requiresDedicatedAllocation; | |
vk_allocation bufferMem = VkArenaAlignAlloc( vkArena, | |
memReqs2.memoryRequirements.size, | |
memReqs2.memoryRequirements.alignment, | |
memTypeIdx, | |
allocFlags, | |
dedicatedAlloc ? &dedicatedAllocateInfo : 0 ); | |
buffData.mem = bufferMem.deviceMem; | |
buffData.hostVisible = bufferMem.hostVisible; | |
buffData.size = sizeInBytes; | |
VK_CHECK( vkBindBufferMemory( vkArena->device, buffData.hndl, buffData.mem, bufferMem.dataOffset ) ); | |
if( allocFlags == VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT ) | |
buffData.devicePointer = VkGetBufferDeviceAddress( vkArena->device, buffData.hndl ); | |
return buffData; | |
} | |
inline static VkImageView | |
VkMakeImgView( | |
VkDevice vkDevice, | |
VkImage vkImg, | |
VkFormat imgFormat, | |
u32 mipLevel, | |
u32 levelCount, | |
VkImageViewType imgViewType = VK_IMAGE_VIEW_TYPE_2D, | |
u32 arrayLayer = 0, | |
u32 layerCount = 1 ) | |
{ | |
VkImageAspectFlags aspectMask = | |
( imgFormat == VK_FORMAT_D32_SFLOAT ) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; | |
VkImageViewCreateInfo viewInfo = {}; | |
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
viewInfo.viewType = imgViewType; | |
viewInfo.format = imgFormat; | |
viewInfo.subresourceRange.aspectMask = aspectMask; | |
viewInfo.subresourceRange.baseMipLevel = mipLevel; | |
viewInfo.subresourceRange.levelCount = levelCount; | |
viewInfo.subresourceRange.baseArrayLayer = arrayLayer; | |
viewInfo.subresourceRange.layerCount = layerCount; | |
viewInfo.image = vkImg; | |
VkImageView view; | |
VK_CHECK( vkCreateImageView( vkDevice, &viewInfo, 0, &view ) ); | |
return view; | |
} | |
// TODO: add more VkImageCreateInfo stuff | |
static image | |
VkCreateAllocBindImage( | |
VkFormat format, | |
VkImageUsageFlags usageFlags, | |
VkExtent3D extent, | |
u32 mipCount, | |
vk_mem_arena* vkArena, | |
VkPhysicalDevice gpu = dc.gpu ) | |
{ | |
VkFormatFeatureFlags imgType = VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM; | |
switch( usageFlags ){ | |
case VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT: imgType = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; | |
break; | |
case VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT: imgType = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; | |
break; | |
case VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT: | |
imgType = VK_FORMAT_FEATURE_TRANSFER_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; | |
break; | |
} | |
VkFormatProperties formatProps; | |
vkGetPhysicalDeviceFormatProperties( gpu, format, &formatProps ); | |
VK_CHECK( !( formatProps.optimalTilingFeatures & imgType ) ); | |
image img = {}; | |
VkImageCreateInfo imgInfo = {}; | |
imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; | |
imgInfo.imageType = VK_IMAGE_TYPE_2D; | |
imgInfo.format = img.nativeFormat = format; | |
imgInfo.extent = img.nativeRes = extent; | |
imgInfo.mipLevels = mipCount; | |
imgInfo.arrayLayers = 1; | |
imgInfo.samples = VK_SAMPLE_COUNT_1_BIT; | |
imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL; | |
imgInfo.usage = usageFlags; | |
imgInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
VK_CHECK( vkCreateImage( vkArena->device, &imgInfo, 0, &img.img ) ); | |
VkImageMemoryRequirementsInfo2 imgReqs2 = {}; | |
imgReqs2.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2; | |
imgReqs2.image = img.img; | |
VkMemoryDedicatedRequirements dedicatedReqs = {}; | |
dedicatedReqs.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; | |
VkMemoryRequirements2 memReqs2 = {}; | |
memReqs2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; | |
memReqs2.pNext = &dedicatedReqs; | |
vkGetImageMemoryRequirements2( vkArena->device, &imgReqs2, &memReqs2 ); | |
VkMemoryDedicatedAllocateInfo dedicatedAllocateInfo = {}; | |
dedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; | |
dedicatedAllocateInfo.image = imgReqs2.image; | |
b32 dedicatedAlloc = dedicatedReqs.prefersDedicatedAllocation || dedicatedReqs.requiresDedicatedAllocation; | |
i32 memTypeIdx = VkFindMemTypeIdx( &vkArena->gpuMemProps, vkArena->memType, memReqs2.memoryRequirements.memoryTypeBits ); | |
vk_allocation imgMem = VkArenaAlignAlloc( vkArena, | |
memReqs2.memoryRequirements.size, | |
memReqs2.memoryRequirements.alignment, | |
memTypeIdx, 0, | |
dedicatedAlloc ? &dedicatedAllocateInfo : 0 ); | |
img.mem = imgMem.deviceMem; | |
VK_CHECK( vkBindImageMemory( vkArena->device, img.img, img.mem, imgMem.dataOffset ) ); | |
VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM; | |
switch( imgInfo.imageType ){ | |
case VK_IMAGE_TYPE_1D: viewType = VK_IMAGE_VIEW_TYPE_1D; break; | |
case VK_IMAGE_TYPE_2D: viewType = VK_IMAGE_VIEW_TYPE_2D; break; | |
case VK_IMAGE_TYPE_3D: viewType = VK_IMAGE_VIEW_TYPE_3D; break; | |
default: VK_CHECK( "Uknown image type !" ); break; | |
} | |
img.view = VkMakeImgView( vkArena->device, img.img, imgInfo.format, 0, imgInfo.mipLevels, viewType, 0, imgInfo.arrayLayers ); | |
return img; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment