2016-09-11 18:42:30 +02:00
/*
* Vulkan Example - Sparse texture residency example
*
2020-07-11 13:01:02 +02:00
* Copyright ( C ) 2016 - 2020 by Sascha Willems - www . saschawillems . de
2016-09-11 18:42:30 +02:00
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
/*
2020-07-11 13:01:02 +02:00
* Note : This sample is work - in - progress and works basically , but it ' s not yet finished
2016-09-11 18:42:30 +02:00
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <vector>
# include <algorithm>
2016-09-14 19:01:44 +02:00
# include <random>
2016-10-01 17:37:11 +02:00
# include <chrono>
2016-09-11 18:42:30 +02:00
# define GLM_FORCE_RADIANS
# define GLM_FORCE_DEPTH_ZERO_TO_ONE
# include <glm/glm.hpp>
# include <glm/gtc/matrix_transform.hpp>
# include <vulkan/vulkan.h>
# include "vulkanexamplebase.h"
2017-02-12 10:16:07 +01:00
# include "VulkanDevice.hpp"
2017-02-12 10:44:51 +01:00
# include "VulkanBuffer.hpp"
2020-07-11 13:01:02 +02:00
# include "VulkanModel.hpp"
2016-09-11 18:42:30 +02:00
# define ENABLE_VALIDATION false
2016-09-14 19:01:44 +02:00
// Virtual texture page as a part of the partially resident texture
// Contains memory bindings, offsets and status information
struct VirtualTexturePage
2020-05-29 16:08:53 +01:00
{
2016-09-14 19:01:44 +02:00
VkOffset3D offset ;
VkExtent3D extent ;
VkSparseImageMemoryBind imageMemoryBind ; // Sparse image memory bind for this page
VkDeviceSize size ; // Page (memory) size in bytes
uint32_t mipLevel ; // Mip level that this page belongs to
uint32_t layer ; // Array layer that this page belongs to
2020-05-29 16:08:53 +01:00
uint32_t index ;
2016-09-14 19:01:44 +02:00
VirtualTexturePage ( )
{
imageMemoryBind . memory = VK_NULL_HANDLE ; // Page initially not backed up by memory
}
2020-07-11 13:01:02 +02:00
bool resident ( )
{
return ( imageMemoryBind . memory ! = VK_NULL_HANDLE ) ;
}
2016-09-14 19:01:44 +02:00
// Allocate Vulkan memory for the virtual page
void allocate ( VkDevice device , uint32_t memoryTypeIndex )
{
if ( imageMemoryBind . memory ! = VK_NULL_HANDLE )
{
return ;
} ;
imageMemoryBind = { } ;
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo allocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2016-09-14 19:01:44 +02:00
allocInfo . allocationSize = size ;
allocInfo . memoryTypeIndex = memoryTypeIndex ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & allocInfo , nullptr , & imageMemoryBind . memory ) ) ;
VkImageSubresource subResource { } ;
subResource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
subResource . mipLevel = mipLevel ;
subResource . arrayLayer = layer ;
// Sparse image memory binding
imageMemoryBind . subresource = subResource ;
imageMemoryBind . extent = extent ;
imageMemoryBind . offset = offset ;
}
// Release Vulkan memory allocated for this page
void release ( VkDevice device )
{
if ( imageMemoryBind . memory ! = VK_NULL_HANDLE )
{
vkFreeMemory ( device , imageMemoryBind . memory , nullptr ) ;
imageMemoryBind . memory = VK_NULL_HANDLE ;
}
}
} ;
2020-05-29 16:08:53 +01:00
// Virtual texture object containing all pages
2016-09-14 19:01:44 +02:00
struct VirtualTexture
{
VkDevice device ;
VkImage image ; // Texture image handle
VkBindSparseInfo bindSparseInfo ; // Sparse queue binding information
std : : vector < VirtualTexturePage > pages ; // Contains all virtual pages of the texture
std : : vector < VkSparseImageMemoryBind > sparseImageMemoryBinds ; // Sparse image memory bindings of all memory-backed virtual tables
2017-04-29 14:00:43 -07:00
std : : vector < VkSparseMemoryBind > opaqueMemoryBinds ; // Sparse ópaque memory bindings for the mip tail (if present)
2020-05-29 16:08:53 +01:00
VkSparseImageMemoryBindInfo imageMemoryBindInfo ; // Sparse image memory bind info
2016-09-14 19:01:44 +02:00
VkSparseImageOpaqueMemoryBindInfo opaqueMemoryBindInfo ; // Sparse image opaque memory bind info (mip tail)
2016-10-01 17:37:11 +02:00
uint32_t mipTailStart ; // First mip level in mip tail
2020-07-11 19:22:19 +02:00
VkSparseImageMemoryRequirements sparseImageMemoryRequirements ; // @todo: Comment
2020-05-29 16:08:53 +01:00
2016-09-14 19:01:44 +02:00
VirtualTexturePage * addPage ( VkOffset3D offset , VkExtent3D extent , const VkDeviceSize size , const uint32_t mipLevel , uint32_t layer )
{
VirtualTexturePage newPage ;
newPage . offset = offset ;
newPage . extent = extent ;
newPage . size = size ;
newPage . mipLevel = mipLevel ;
newPage . layer = layer ;
newPage . index = static_cast < uint32_t > ( pages . size ( ) ) ;
2020-07-11 13:01:02 +02:00
newPage . imageMemoryBind = { } ;
2016-09-14 19:01:44 +02:00
newPage . imageMemoryBind . offset = offset ;
newPage . imageMemoryBind . extent = extent ;
pages . push_back ( newPage ) ;
return & pages . back ( ) ;
}
// Call before sparse binding to update memory bind list etc.
void updateSparseBindInfo ( )
{
// Update list of memory-backed sparse image memory binds
2020-07-11 13:01:02 +02:00
//sparseImageMemoryBinds.resize(pages.size());
sparseImageMemoryBinds . clear ( ) ;
2016-09-14 19:01:44 +02:00
for ( auto page : pages )
{
2020-07-11 13:01:02 +02:00
sparseImageMemoryBinds . push_back ( page . imageMemoryBind ) ;
2016-09-14 19:01:44 +02:00
}
// Update sparse bind info
2017-02-12 11:12:42 +01:00
bindSparseInfo = vks : : initializers : : bindSparseInfo ( ) ;
2016-09-14 19:01:44 +02:00
// todo: Semaphore for queue submission
// bindSparseInfo.signalSemaphoreCount = 1;
// bindSparseInfo.pSignalSemaphores = &bindSparseSemaphore;
// Image memory binds
2020-07-11 13:01:02 +02:00
imageMemoryBindInfo = { } ;
2016-09-14 19:01:44 +02:00
imageMemoryBindInfo . image = image ;
imageMemoryBindInfo . bindCount = static_cast < uint32_t > ( sparseImageMemoryBinds . size ( ) ) ;
imageMemoryBindInfo . pBinds = sparseImageMemoryBinds . data ( ) ;
bindSparseInfo . imageBindCount = ( imageMemoryBindInfo . bindCount > 0 ) ? 1 : 0 ;
bindSparseInfo . pImageBinds = & imageMemoryBindInfo ;
2020-07-11 19:22:19 +02:00
// Opaque image memory binds for the mip tail
2016-09-14 19:01:44 +02:00
opaqueMemoryBindInfo . image = image ;
opaqueMemoryBindInfo . bindCount = static_cast < uint32_t > ( opaqueMemoryBinds . size ( ) ) ;
opaqueMemoryBindInfo . pBinds = opaqueMemoryBinds . data ( ) ;
bindSparseInfo . imageOpaqueBindCount = ( opaqueMemoryBindInfo . bindCount > 0 ) ? 1 : 0 ;
bindSparseInfo . pImageOpaqueBinds = & opaqueMemoryBindInfo ;
}
// Release all Vulkan resources
void destroy ( )
{
for ( auto page : pages )
{
page . release ( device ) ;
}
for ( auto bind : opaqueMemoryBinds )
{
vkFreeMemory ( device , bind . memory , nullptr ) ;
}
}
} ;
uint32_t memoryTypeIndex ;
2016-10-01 17:37:11 +02:00
2016-09-11 18:42:30 +02:00
class VulkanExample : public VulkanExampleBase
{
public :
//todo: comments
2016-09-14 19:01:44 +02:00
struct SparseTexture : VirtualTexture {
2016-09-11 18:42:30 +02:00
VkSampler sampler ;
VkImageLayout imageLayout ;
VkImageView view ;
VkDescriptorImageInfo descriptor ;
VkFormat format ;
uint32_t width , height ;
uint32_t mipLevels ;
uint32_t layerCount ;
} texture ;
2020-07-11 13:01:02 +02:00
vks : : VertexLayout vertexLayout = vks : : VertexLayout ( {
vks : : VERTEX_COMPONENT_POSITION ,
vks : : VERTEX_COMPONENT_NORMAL ,
vks : : VERTEX_COMPONENT_UV ,
} ) ;
vks : : Model plane ;
2016-09-11 18:42:30 +02:00
struct UboVS {
glm : : mat4 projection ;
glm : : mat4 model ;
glm : : vec4 viewPos ;
float lodBias = 0.0f ;
} uboVS ;
2020-07-11 13:01:02 +02:00
vks : : Buffer uniformBufferVS ;
2016-09-11 18:42:30 +02:00
2020-07-11 13:01:02 +02:00
VkPipeline pipeline ;
2016-09-11 18:42:30 +02:00
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
//todo: comment
VkSemaphore bindSparseSemaphore = VK_NULL_HANDLE ;
2016-12-14 20:17:15 +01:00
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
2016-09-11 18:42:30 +02:00
{
2017-11-01 14:22:10 +01:00
title = " Sparse texture residency " ;
2016-09-11 18:42:30 +02:00
std : : cout . imbue ( std : : locale ( " " ) ) ;
2020-07-11 13:01:02 +02:00
camera . type = Camera : : CameraType : : lookat ;
camera . setPosition ( glm : : vec3 ( 0.0f , 0.0f , - 20.0f ) ) ;
camera . setRotation ( glm : : vec3 ( 0.0f , 180.0f , 0.0f ) ) ;
camera . setPerspective ( 60.0f , ( float ) width / ( float ) height , 0.1f , 256.0f ) ;
2017-11-01 14:22:10 +01:00
settings . overlay = true ;
2016-09-11 18:42:30 +02:00
}
~ VulkanExample ( )
{
2020-05-29 16:08:53 +01:00
// Clean up used Vulkan resources
2016-09-11 18:42:30 +02:00
// Note : Inherited destructor cleans up resources stored in base class
destroyTextureImage ( texture ) ;
vkDestroySemaphore ( device , bindSparseSemaphore , nullptr ) ;
2020-07-11 13:01:02 +02:00
vkDestroyPipeline ( device , pipeline , nullptr ) ;
2016-09-11 18:42:30 +02:00
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
2020-07-11 13:01:02 +02:00
plane . destroy ( ) ;
2016-09-11 18:42:30 +02:00
uniformBufferVS . destroy ( ) ;
}
2017-11-01 14:22:10 +01:00
virtual void getEnabledFeatures ( )
{
if ( deviceFeatures . sparseBinding & & deviceFeatures . sparseResidencyImage2D ) {
2020-07-11 13:01:02 +02:00
enabledFeatures . shaderResourceResidency = VK_TRUE ;
enabledFeatures . shaderResourceMinLod = VK_TRUE ;
2017-11-01 14:22:10 +01:00
enabledFeatures . sparseBinding = VK_TRUE ;
enabledFeatures . sparseResidencyImage2D = VK_TRUE ;
}
else {
std : : cout < < " Sparse binding not supported " < < std : : endl ;
}
}
2017-05-09 10:05:03 +02:00
2016-09-11 18:42:30 +02:00
glm : : uvec3 alignedDivision ( const VkExtent3D & extent , const VkExtent3D & granularity )
{
glm : : uvec3 res ;
2020-07-11 13:01:02 +02:00
res . x = extent . width / granularity . width + ( ( extent . width % granularity . width ) ? 1u : 0u ) ;
2016-09-11 18:42:30 +02:00
res . y = extent . height / granularity . height + ( ( extent . height % granularity . height ) ? 1u : 0u ) ;
2020-07-11 13:01:02 +02:00
res . z = extent . depth / granularity . depth + ( ( extent . depth % granularity . depth ) ? 1u : 0u ) ;
2016-09-11 18:42:30 +02:00
return res ;
}
void prepareSparseTexture ( uint32_t width , uint32_t height , uint32_t layerCount , VkFormat format )
{
2016-09-14 19:01:44 +02:00
texture . device = vulkanDevice - > logicalDevice ;
2016-09-11 18:42:30 +02:00
texture . width = width ;
texture . height = height ;
2020-05-29 16:08:53 +01:00
texture . mipLevels = floor ( log2 ( std : : max ( width , height ) ) ) + 1 ;
2016-09-11 18:42:30 +02:00
texture . layerCount = layerCount ;
texture . format = format ;
// Get device properites for the requested texture format
VkFormatProperties formatProperties ;
vkGetPhysicalDeviceFormatProperties ( physicalDevice , format , & formatProperties ) ;
2020-07-11 13:01:02 +02:00
const VkImageType imageType = VK_IMAGE_TYPE_2D ;
const VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT ;
const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ;
const VkImageTiling imageTiling = VK_IMAGE_TILING_OPTIMAL ;
2016-09-11 18:42:30 +02:00
// Get sparse image properties
2016-10-01 17:37:11 +02:00
std : : vector < VkSparseImageFormatProperties > sparseProperties ;
2016-09-11 18:42:30 +02:00
// Sparse properties count for the desired format
uint32_t sparsePropertiesCount ;
2020-07-11 13:01:02 +02:00
vkGetPhysicalDeviceSparseImageFormatProperties ( physicalDevice , format , imageType , sampleCount , imageUsage , imageTiling , & sparsePropertiesCount , nullptr ) ;
2016-09-11 18:42:30 +02:00
// Check if sparse is supported for this format
if ( sparsePropertiesCount = = 0 )
{
std : : cout < < " Error: Requested format does not support sparse features! " < < std : : endl ;
return ;
}
2016-10-01 17:37:11 +02:00
// Get actual image format properties
sparseProperties . resize ( sparsePropertiesCount ) ;
2020-07-11 13:01:02 +02:00
vkGetPhysicalDeviceSparseImageFormatProperties ( physicalDevice , format , imageType , sampleCount , imageUsage , imageTiling , & sparsePropertiesCount , sparseProperties . data ( ) ) ;
2016-10-01 17:37:11 +02:00
2016-09-11 18:42:30 +02:00
std : : cout < < " Sparse image format properties: " < < sparsePropertiesCount < < std : : endl ;
for ( auto props : sparseProperties )
{
std : : cout < < " \t Image granularity: w = " < < props . imageGranularity . width < < " h = " < < props . imageGranularity . height < < " d = " < < props . imageGranularity . depth < < std : : endl ;
std : : cout < < " \t Aspect mask: " < < props . aspectMask < < std : : endl ;
std : : cout < < " \t Flags: " < < props . flags < < std : : endl ;
}
// Create sparse image
2017-02-12 11:12:42 +01:00
VkImageCreateInfo sparseImageCreateInfo = vks : : initializers : : imageCreateInfo ( ) ;
2020-07-11 13:01:02 +02:00
sparseImageCreateInfo . imageType = imageType ;
2016-09-11 18:42:30 +02:00
sparseImageCreateInfo . format = texture . format ;
sparseImageCreateInfo . mipLevels = texture . mipLevels ;
sparseImageCreateInfo . arrayLayers = texture . layerCount ;
2020-07-11 13:01:02 +02:00
sparseImageCreateInfo . samples = sampleCount ;
sparseImageCreateInfo . tiling = imageTiling ;
2016-09-11 18:42:30 +02:00
sparseImageCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
sparseImageCreateInfo . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
sparseImageCreateInfo . extent = { texture . width , texture . height , 1 } ;
2020-07-11 13:01:02 +02:00
sparseImageCreateInfo . usage = imageUsage ;
2016-09-11 18:42:30 +02:00
sparseImageCreateInfo . flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT ;
VK_CHECK_RESULT ( vkCreateImage ( device , & sparseImageCreateInfo , nullptr , & texture . image ) ) ;
2020-07-11 13:01:02 +02:00
VkCommandBuffer copyCmd = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
vks : : tools : : setImageLayout ( copyCmd , texture . image , VK_IMAGE_ASPECT_COLOR_BIT , VK_IMAGE_LAYOUT_UNDEFINED , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) ;
vulkanDevice - > flushCommandBuffer ( copyCmd , queue ) ;
2016-09-11 18:42:30 +02:00
// Get memory requirements
VkMemoryRequirements sparseImageMemoryReqs ;
2016-10-01 17:37:11 +02:00
// Sparse image memory requirement counts
2016-09-11 18:42:30 +02:00
vkGetImageMemoryRequirements ( device , texture . image , & sparseImageMemoryReqs ) ;
std : : cout < < " Image memory requirements: " < < std : : endl ;
std : : cout < < " \t Size: " < < sparseImageMemoryReqs . size < < std : : endl ;
std : : cout < < " \t Alignment: " < < sparseImageMemoryReqs . alignment < < std : : endl ;
// Check requested image size against hardware sparse limit
if ( sparseImageMemoryReqs . size > vulkanDevice - > properties . limits . sparseAddressSpaceSize )
{
std : : cout < < " Error: Requested sparse image size exceeds supportes sparse address space size! " < < std : : endl ;
return ;
} ;
// Get sparse memory requirements
2016-10-01 17:37:11 +02:00
// Count
2017-09-12 08:42:26 +02:00
uint32_t sparseMemoryReqsCount = 32 ;
std : : vector < VkSparseImageMemoryRequirements > sparseMemoryReqs ( sparseMemoryReqsCount ) ;
2016-09-11 18:42:30 +02:00
vkGetImageSparseMemoryRequirements ( device , texture . image , & sparseMemoryReqsCount , sparseMemoryReqs . data ( ) ) ;
if ( sparseMemoryReqsCount = = 0 )
{
std : : cout < < " Error: No memory requirements for the sparse image! " < < std : : endl ;
return ;
}
sparseMemoryReqs . resize ( sparseMemoryReqsCount ) ;
2016-10-01 17:37:11 +02:00
// Get actual requirements
vkGetImageSparseMemoryRequirements ( device , texture . image , & sparseMemoryReqsCount , sparseMemoryReqs . data ( ) ) ;
2016-09-11 18:42:30 +02:00
std : : cout < < " Sparse image memory requirements: " < < sparseMemoryReqsCount < < std : : endl ;
for ( auto reqs : sparseMemoryReqs )
{
std : : cout < < " \t Image granularity: w = " < < reqs . formatProperties . imageGranularity . width < < " h = " < < reqs . formatProperties . imageGranularity . height < < " d = " < < reqs . formatProperties . imageGranularity . depth < < std : : endl ;
std : : cout < < " \t Mip tail first LOD: " < < reqs . imageMipTailFirstLod < < std : : endl ;
std : : cout < < " \t Mip tail size: " < < reqs . imageMipTailSize < < std : : endl ;
std : : cout < < " \t Mip tail offset: " < < reqs . imageMipTailOffset < < std : : endl ;
std : : cout < < " \t Mip tail stride: " < < reqs . imageMipTailStride < < std : : endl ;
2016-10-01 17:37:11 +02:00
//todo:multiple reqs
texture . mipTailStart = reqs . imageMipTailFirstLod ;
2016-09-11 18:42:30 +02:00
}
2020-05-29 16:08:53 +01:00
2016-09-11 18:42:30 +02:00
// Get sparse image requirements for the color aspect
VkSparseImageMemoryRequirements sparseMemoryReq ;
bool colorAspectFound = false ;
for ( auto reqs : sparseMemoryReqs )
{
if ( reqs . formatProperties . aspectMask & VK_IMAGE_ASPECT_COLOR_BIT )
{
sparseMemoryReq = reqs ;
colorAspectFound = true ;
break ;
}
}
if ( ! colorAspectFound )
{
std : : cout < < " Error: Could not find sparse image memory requirements for color aspect bit! " < < std : : endl ;
return ;
}
// todo:
// Calculate number of required sparse memory bindings by alignment
assert ( ( sparseImageMemoryReqs . size % sparseImageMemoryReqs . alignment ) = = 0 ) ;
2016-09-14 19:01:44 +02:00
memoryTypeIndex = vulkanDevice - > getMemoryType ( sparseImageMemoryReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
2016-09-11 18:42:30 +02:00
// Get sparse bindings
2020-05-29 16:08:53 +01:00
uint32_t sparseBindsCount = static_cast < uint32_t > ( sparseImageMemoryReqs . size / sparseImageMemoryReqs . alignment ) ;
2016-09-11 18:42:30 +02:00
std : : vector < VkSparseMemoryBind > sparseMemoryBinds ( sparseBindsCount ) ;
2020-07-11 19:22:19 +02:00
texture . sparseImageMemoryRequirements = sparseMemoryReq ;
2016-09-12 19:53:39 +02:00
// Check if the format has a single mip tail for all layers or one mip tail for each layer
// The mip tail contains all mip levels > sparseMemoryReq.imageMipTailFirstLod
bool singleMipTail = sparseMemoryReq . formatProperties . flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT ;
2020-07-11 19:22:19 +02:00
// @todo: Comment
bool alingedMipSize = sparseMemoryReq . formatProperties . flags & VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT ;
2016-09-12 19:53:39 +02:00
2016-09-11 18:42:30 +02:00
// Sparse bindings for each mip level of all layers outside of the mip tail
for ( uint32_t layer = 0 ; layer < texture . layerCount ; layer + + )
{
2016-09-12 19:53:39 +02:00
// sparseMemoryReq.imageMipTailFirstLod is the first mip level that's stored inside the mip tail
2016-09-11 18:42:30 +02:00
for ( uint32_t mipLevel = 0 ; mipLevel < sparseMemoryReq . imageMipTailFirstLod ; mipLevel + + )
{
VkExtent3D extent ;
extent . width = std : : max ( sparseImageCreateInfo . extent . width > > mipLevel , 1u ) ;
extent . height = std : : max ( sparseImageCreateInfo . extent . height > > mipLevel , 1u ) ;
extent . depth = std : : max ( sparseImageCreateInfo . extent . depth > > mipLevel , 1u ) ;
VkImageSubresource subResource { } ;
subResource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
subResource . mipLevel = mipLevel ;
subResource . arrayLayer = layer ;
// Aligned sizes by image granularity
VkExtent3D imageGranularity = sparseMemoryReq . formatProperties . imageGranularity ;
glm : : uvec3 sparseBindCounts = alignedDivision ( extent , imageGranularity ) ;
glm : : uvec3 lastBlockExtent ;
lastBlockExtent . x = ( extent . width % imageGranularity . width ) ? extent . width % imageGranularity . width : imageGranularity . width ;
lastBlockExtent . y = ( extent . height % imageGranularity . height ) ? extent . height % imageGranularity . height : imageGranularity . height ;
lastBlockExtent . z = ( extent . depth % imageGranularity . depth ) ? extent . depth % imageGranularity . depth : imageGranularity . depth ;
2020-07-11 13:01:02 +02:00
// @todo: Comment
2016-09-11 18:42:30 +02:00
uint32_t index = 0 ;
for ( uint32_t z = 0 ; z < sparseBindCounts . z ; z + + )
{
for ( uint32_t y = 0 ; y < sparseBindCounts . y ; y + + )
{
for ( uint32_t x = 0 ; x < sparseBindCounts . x ; x + + )
{
2020-05-29 16:08:53 +01:00
// Offset
2016-09-11 18:42:30 +02:00
VkOffset3D offset ;
offset . x = x * imageGranularity . width ;
offset . y = y * imageGranularity . height ;
offset . z = z * imageGranularity . depth ;
2016-09-14 19:01:44 +02:00
// Size of the page
2016-09-11 18:42:30 +02:00
VkExtent3D extent ;
extent . width = ( x = = sparseBindCounts . x - 1 ) ? lastBlockExtent . x : imageGranularity . width ;
extent . height = ( y = = sparseBindCounts . y - 1 ) ? lastBlockExtent . y : imageGranularity . height ;
extent . depth = ( z = = sparseBindCounts . z - 1 ) ? lastBlockExtent . z : imageGranularity . depth ;
2016-09-14 19:01:44 +02:00
// Add new virtual page
2020-07-11 13:01:02 +02:00
VirtualTexturePage * newPage = texture . addPage ( offset , extent , sparseImageMemoryReqs . alignment , mipLevel , layer ) ;
2016-09-14 19:01:44 +02:00
newPage - > imageMemoryBind . subresource = subResource ;
2016-09-11 18:42:30 +02:00
2016-09-14 19:01:44 +02:00
index + + ;
2016-09-11 18:42:30 +02:00
}
}
}
}
2016-09-12 19:53:39 +02:00
// Check if format has one mip tail per layer
if ( ( ! singleMipTail ) & & ( sparseMemoryReq . imageMipTailFirstLod < texture . mipLevels ) )
2016-09-11 18:42:30 +02:00
{
2016-09-12 19:53:39 +02:00
// Allocate memory for the mip tail
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo allocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2016-09-12 19:53:39 +02:00
allocInfo . allocationSize = sparseMemoryReq . imageMipTailSize ;
allocInfo . memoryTypeIndex = memoryTypeIndex ;
VkDeviceMemory deviceMemory ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & allocInfo , nullptr , & deviceMemory ) ) ;
// (Opaque) sparse memory binding
VkSparseMemoryBind sparseMemoryBind { } ;
sparseMemoryBind . resourceOffset = sparseMemoryReq . imageMipTailOffset + layer * sparseMemoryReq . imageMipTailStride ;
sparseMemoryBind . size = sparseMemoryReq . imageMipTailSize ;
sparseMemoryBind . memory = deviceMemory ;
texture . opaqueMemoryBinds . push_back ( sparseMemoryBind ) ;
}
2016-09-11 18:42:30 +02:00
} // end layers and mips
2016-09-14 19:01:44 +02:00
std : : cout < < " Texture info: " < < std : : endl ;
std : : cout < < " \t Dim: " < < texture . width < < " x " < < texture . height < < std : : endl ;
std : : cout < < " \t Virtual pages: " < < texture . pages . size ( ) < < std : : endl ;
2016-09-12 19:53:39 +02:00
// Check if format has one mip tail for all layers
if ( ( sparseMemoryReq . formatProperties . flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT ) & & ( sparseMemoryReq . imageMipTailFirstLod < texture . mipLevels ) )
{
// Allocate memory for the mip tail
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo allocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2016-09-12 19:53:39 +02:00
allocInfo . allocationSize = sparseMemoryReq . imageMipTailSize ;
allocInfo . memoryTypeIndex = memoryTypeIndex ;
VkDeviceMemory deviceMemory ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & allocInfo , nullptr , & deviceMemory ) ) ;
// (Opaque) sparse memory binding
VkSparseMemoryBind sparseMemoryBind { } ;
sparseMemoryBind . resourceOffset = sparseMemoryReq . imageMipTailOffset ;
sparseMemoryBind . size = sparseMemoryReq . imageMipTailSize ;
sparseMemoryBind . memory = deviceMemory ;
texture . opaqueMemoryBinds . push_back ( sparseMemoryBind ) ;
}
2016-09-11 18:42:30 +02:00
// Create signal semaphore for sparse binding
2017-02-12 11:12:42 +01:00
VkSemaphoreCreateInfo semaphoreCreateInfo = vks : : initializers : : semaphoreCreateInfo ( ) ;
2016-09-11 18:42:30 +02:00
VK_CHECK_RESULT ( vkCreateSemaphore ( device , & semaphoreCreateInfo , nullptr , & bindSparseSemaphore ) ) ;
// Prepare bind sparse info for reuse in queue submission
2016-09-14 19:01:44 +02:00
texture . updateSparseBindInfo ( ) ;
2016-09-11 18:42:30 +02:00
// Bind to queue
// todo: in draw?
2016-09-14 19:01:44 +02:00
vkQueueBindSparse ( queue , 1 , & texture . bindSparseInfo , VK_NULL_HANDLE ) ;
2016-09-11 18:42:30 +02:00
//todo: use sparse bind semaphore
vkQueueWaitIdle ( queue ) ;
// Create sampler
2017-02-12 11:12:42 +01:00
VkSamplerCreateInfo sampler = vks : : initializers : : samplerCreateInfo ( ) ;
2016-09-11 18:42:30 +02:00
sampler . magFilter = VK_FILTER_LINEAR ;
sampler . minFilter = VK_FILTER_LINEAR ;
sampler . mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR ;
2020-07-11 13:01:02 +02:00
sampler . addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE ;
sampler . addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE ;
sampler . addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE ;
2016-09-11 18:42:30 +02:00
sampler . mipLodBias = 0.0f ;
sampler . compareOp = VK_COMPARE_OP_NEVER ;
sampler . minLod = 0.0f ;
2016-09-12 19:53:39 +02:00
sampler . maxLod = static_cast < float > ( texture . mipLevels ) ;
sampler . maxAnisotropy = vulkanDevice - > features . samplerAnisotropy ? vulkanDevice - > properties . limits . maxSamplerAnisotropy : 1.0f ;
2016-10-01 17:37:11 +02:00
sampler . anisotropyEnable = false ;
2016-09-11 18:42:30 +02:00
sampler . borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE ;
VK_CHECK_RESULT ( vkCreateSampler ( device , & sampler , nullptr , & texture . sampler ) ) ;
// Create image view
2017-02-12 11:12:42 +01:00
VkImageViewCreateInfo view = vks : : initializers : : imageViewCreateInfo ( ) ;
2016-09-11 18:42:30 +02:00
view . image = VK_NULL_HANDLE ;
view . viewType = VK_IMAGE_VIEW_TYPE_2D ;
view . format = format ;
view . components = { VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_G , VK_COMPONENT_SWIZZLE_B , VK_COMPONENT_SWIZZLE_A } ;
view . subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
view . subresourceRange . baseMipLevel = 0 ;
view . subresourceRange . baseArrayLayer = 0 ;
view . subresourceRange . layerCount = 1 ;
view . subresourceRange . levelCount = texture . mipLevels ;
view . image = texture . image ;
VK_CHECK_RESULT ( vkCreateImageView ( device , & view , nullptr , & texture . view ) ) ;
// Fill image descriptor image info that can be used during the descriptor set setup
2016-10-20 21:30:37 +02:00
texture . descriptor . imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ;
2016-09-11 18:42:30 +02:00
texture . descriptor . imageView = texture . view ;
texture . descriptor . sampler = texture . sampler ;
}
// Free all Vulkan resources used a texture object
void destroyTextureImage ( SparseTexture texture )
{
vkDestroyImageView ( device , texture . view , nullptr ) ;
vkDestroyImage ( device , texture . image , nullptr ) ;
vkDestroySampler ( device , texture . sampler , nullptr ) ;
2016-09-14 19:01:44 +02:00
texture . destroy ( ) ;
2016-09-11 18:42:30 +02:00
}
void buildCommandBuffers ( )
{
2017-02-12 11:12:42 +01:00
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
2016-09-11 18:42:30 +02:00
VkClearValue clearValues [ 2 ] ;
2020-07-11 13:01:02 +02:00
clearValues [ 0 ] . color = defaultClearColor ;
2016-09-11 18:42:30 +02:00
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
2017-02-12 11:12:42 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks : : initializers : : renderPassBeginInfo ( ) ;
2016-09-11 18:42:30 +02:00
renderPassBeginInfo . renderPass = renderPass ;
renderPassBeginInfo . renderArea . offset . x = 0 ;
renderPassBeginInfo . renderArea . offset . y = 0 ;
renderPassBeginInfo . renderArea . extent . width = width ;
renderPassBeginInfo . renderArea . extent . height = height ;
renderPassBeginInfo . clearValueCount = 2 ;
renderPassBeginInfo . pClearValues = clearValues ;
for ( int32_t i = 0 ; i < drawCmdBuffers . size ( ) ; + + i )
{
renderPassBeginInfo . framebuffer = frameBuffers [ i ] ;
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
vkCmdBeginRenderPass ( drawCmdBuffers [ i ] , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
2017-02-12 11:12:42 +01:00
VkViewport viewport = vks : : initializers : : viewport ( ( float ) width , ( float ) height , 0.0f , 1.0f ) ;
2016-09-11 18:42:30 +02:00
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
2017-02-12 11:12:42 +01:00
VkRect2D scissor = vks : : initializers : : rect2D ( width , height , 0 , 0 ) ;
2016-09-11 18:42:30 +02:00
vkCmdSetScissor ( drawCmdBuffers [ i ] , 0 , 1 , & scissor ) ;
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSet , 0 , NULL ) ;
2020-07-11 13:01:02 +02:00
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline ) ;
2016-09-11 18:42:30 +02:00
VkDeviceSize offsets [ 1 ] = { 0 } ;
2020-07-11 13:01:02 +02:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , 0 , 1 , & plane . vertices . buffer , offsets ) ;
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , plane . indices . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , plane . indexCount , 1 , 0 , 0 , 0 ) ;
2016-09-11 18:42:30 +02:00
2018-08-30 21:08:02 +02:00
drawUI ( drawCmdBuffers [ i ] ) ;
2016-09-11 18:42:30 +02:00
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
}
}
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
2016-09-17 19:08:27 +02:00
void loadAssets ( )
{
2020-07-11 13:01:02 +02:00
plane . loadFromFile ( getAssetPath ( ) + " models/plane_z.obj " , vertexLayout , 1.0f , vulkanDevice , queue ) ;
2016-09-11 18:42:30 +02:00
}
void setupDescriptorPool ( )
{
// Example uses one ubo and one image sampler
std : : vector < VkDescriptorPoolSize > poolSizes =
{
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 ) ,
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 1 )
2016-09-11 18:42:30 +02:00
} ;
2020-05-29 16:08:53 +01:00
VkDescriptorPoolCreateInfo descriptorPoolInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolCreateInfo (
2016-09-11 18:42:30 +02:00
static_cast < uint32_t > ( poolSizes . size ( ) ) ,
poolSizes . data ( ) ,
2 ) ;
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
}
void setupDescriptorSetLayout ( )
{
2020-05-29 16:08:53 +01:00
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings =
2016-09-11 18:42:30 +02:00
{
// Binding 0 : Vertex shader uniform buffer
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding (
2020-05-29 16:08:53 +01:00
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
VK_SHADER_STAGE_VERTEX_BIT ,
2016-09-11 18:42:30 +02:00
0 ) ,
// Binding 1 : Fragment shader image sampler
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding (
2020-05-29 16:08:53 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
VK_SHADER_STAGE_FRAGMENT_BIT ,
2016-09-11 18:42:30 +02:00
1 )
} ;
2020-05-29 16:08:53 +01:00
VkDescriptorSetLayoutCreateInfo descriptorLayout =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutCreateInfo (
2016-09-11 18:42:30 +02:00
setLayoutBindings . data ( ) ,
static_cast < uint32_t > ( setLayoutBindings . size ( ) ) ) ;
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineLayoutCreateInfo (
2016-09-11 18:42:30 +02:00
& descriptorSetLayout ,
1 ) ;
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
}
void setupDescriptorSet ( )
{
2020-05-29 16:08:53 +01:00
VkDescriptorSetAllocateInfo allocInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetAllocateInfo (
2016-09-11 18:42:30 +02:00
descriptorPool ,
& descriptorSetLayout ,
1 ) ;
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
std : : vector < VkWriteDescriptorSet > writeDescriptorSets =
{
// Binding 0 : Vertex shader uniform buffer
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2020-05-29 16:08:53 +01:00
descriptorSet ,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
0 ,
2016-09-11 18:42:30 +02:00
& uniformBufferVS . descriptor ) ,
// Binding 1 : Fragment shader texture sampler
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2020-05-29 16:08:53 +01:00
descriptorSet ,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
1 ,
2016-09-11 18:42:30 +02:00
& texture . descriptor )
} ;
vkUpdateDescriptorSets ( device , static_cast < uint32_t > ( writeDescriptorSets . size ( ) ) , writeDescriptorSets . data ( ) , 0 , NULL ) ;
}
void preparePipelines ( )
{
2020-07-11 13:01:02 +02:00
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks : : initializers : : pipelineInputAssemblyStateCreateInfo ( VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST , 0 , VK_FALSE ) ;
VkPipelineRasterizationStateCreateInfo rasterizationState = vks : : initializers : : pipelineRasterizationStateCreateInfo ( VK_POLYGON_MODE_FILL , VK_CULL_MODE_BACK_BIT , VK_FRONT_FACE_COUNTER_CLOCKWISE , 0 ) ;
VkPipelineColorBlendAttachmentState blendAttachmentState = vks : : initializers : : pipelineColorBlendAttachmentState ( 0xf , VK_FALSE ) ;
VkPipelineColorBlendStateCreateInfo colorBlendState = vks : : initializers : : pipelineColorBlendStateCreateInfo ( 1 , & blendAttachmentState ) ;
VkPipelineDepthStencilStateCreateInfo depthStencilState = vks : : initializers : : pipelineDepthStencilStateCreateInfo ( VK_TRUE , VK_TRUE , VK_COMPARE_OP_LESS_OR_EQUAL ) ;
VkPipelineViewportStateCreateInfo viewportState = vks : : initializers : : pipelineViewportStateCreateInfo ( 1 , 1 , 0 ) ;
VkPipelineMultisampleStateCreateInfo multisampleState = vks : : initializers : : pipelineMultisampleStateCreateInfo ( VK_SAMPLE_COUNT_1_BIT , 0 ) ;
std : : vector < VkDynamicState > dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT , VK_DYNAMIC_STATE_SCISSOR } ;
VkPipelineDynamicStateCreateInfo dynamicState = vks : : initializers : : pipelineDynamicStateCreateInfo ( dynamicStateEnables ) ;
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
VkGraphicsPipelineCreateInfo pipelineCI = vks : : initializers : : pipelineCreateInfo ( pipelineLayout , renderPass ) ;
pipelineCI . pInputAssemblyState = & inputAssemblyState ;
pipelineCI . pRasterizationState = & rasterizationState ;
pipelineCI . pColorBlendState = & colorBlendState ;
pipelineCI . pMultisampleState = & multisampleState ;
pipelineCI . pViewportState = & viewportState ;
pipelineCI . pDepthStencilState = & depthStencilState ;
pipelineCI . pDynamicState = & dynamicState ;
pipelineCI . stageCount = static_cast < uint32_t > ( shaderStages . size ( ) ) ;
pipelineCI . pStages = shaderStages . data ( ) ;
// Vertex bindings an attributes
std : : vector < VkVertexInputBindingDescription > vertexInputBindings = {
vks : : initializers : : vertexInputBindingDescription ( 0 , vertexLayout . stride ( ) , VK_VERTEX_INPUT_RATE_VERTEX ) ,
2016-09-11 18:42:30 +02:00
} ;
2020-07-11 13:01:02 +02:00
std : : vector < VkVertexInputAttributeDescription > vertexInputAttributes = {
vks : : initializers : : vertexInputAttributeDescription ( 0 , 0 , VK_FORMAT_R32G32B32_SFLOAT , 0 ) , // Location 0: Position
vks : : initializers : : vertexInputAttributeDescription ( 0 , 1 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 3 ) , // Location 1: Normal
vks : : initializers : : vertexInputAttributeDescription ( 0 , 2 , VK_FORMAT_R32G32_SFLOAT , sizeof ( float ) * 6 ) , // Location 0: Texture coordinates
} ;
VkPipelineVertexInputStateCreateInfo vertexInputState = vks : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
vertexInputState . vertexBindingDescriptionCount = static_cast < uint32_t > ( vertexInputBindings . size ( ) ) ;
vertexInputState . pVertexBindingDescriptions = vertexInputBindings . data ( ) ;
vertexInputState . vertexAttributeDescriptionCount = static_cast < uint32_t > ( vertexInputAttributes . size ( ) ) ;
vertexInputState . pVertexAttributeDescriptions = vertexInputAttributes . data ( ) ;
pipelineCI . pVertexInputState = & vertexInputState ;
2016-09-11 18:42:30 +02:00
2020-05-29 16:08:53 +01:00
shaderStages [ 0 ] = loadShader ( getShadersPath ( ) + " texturesparseresidency/sparseresidency.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getShadersPath ( ) + " texturesparseresidency/sparseresidency.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2020-07-11 13:01:02 +02:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCI , nullptr , & pipeline ) ) ;
2016-09-11 18:42:30 +02:00
}
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers ( )
{
// Vertex shader uniform buffer block
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& uniformBufferVS ,
sizeof ( uboVS ) ,
& uboVS ) ) ;
updateUniformBuffers ( ) ;
}
void updateUniformBuffers ( )
{
2016-10-01 17:37:11 +02:00
uboVS . projection = camera . matrices . perspective ;
uboVS . model = camera . matrices . view ;
2020-04-22 21:01:17 +02:00
uboVS . viewPos = camera . viewPos ;
2016-09-11 18:42:30 +02:00
VK_CHECK_RESULT ( uniformBufferVS . map ( ) ) ;
memcpy ( uniformBufferVS . mapped , & uboVS , sizeof ( uboVS ) ) ;
uniformBufferVS . unmap ( ) ;
}
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
2016-12-25 12:55:11 +01:00
// Check if the GPU supports sparse residency for 2D images
2018-01-21 18:28:17 +01:00
if ( ! vulkanDevice - > features . sparseResidencyImage2D ) {
vks : : tools : : exitFatal ( " Device does not support sparse residency for 2D images! " , VK_ERROR_FEATURE_NOT_PRESENT ) ;
2016-12-25 12:55:11 +01:00
}
2016-09-17 19:08:27 +02:00
loadAssets ( ) ;
2016-09-11 18:42:30 +02:00
prepareUniformBuffers ( ) ;
2016-10-01 17:37:11 +02:00
// Create a virtual texture with max. possible dimension (does not take up any VRAM yet)
2020-07-11 13:01:02 +02:00
prepareSparseTexture ( 4096 , 4096 , 1 , VK_FORMAT_R8G8B8A8_UNORM ) ;
2016-09-11 18:42:30 +02:00
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
2020-07-11 13:01:02 +02:00
if ( camera . updated ) {
updateUniformBuffers ( ) ;
}
2016-09-11 18:42:30 +02:00
}
2020-07-11 13:01:02 +02:00
void uploadContent ( VirtualTexturePage page , VkImage image )
2016-09-11 18:42:30 +02:00
{
2020-07-11 13:01:02 +02:00
// Generate some random image data and upload as a buffer
const size_t bufferSize = 4 * page . extent . width * page . extent . height ;
vks : : Buffer imageBuffer ;
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_TRANSFER_SRC_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& imageBuffer ,
bufferSize ) ) ;
imageBuffer . map ( ) ;
// Fill buffer with random colors
std : : random_device rd ;
std : : mt19937 rndEngine ( rd ( ) ) ;
std : : uniform_int_distribution < uint32_t > rndDist ( 0 , 255 ) ;
uint8_t * data = ( uint8_t * ) imageBuffer . mapped ;
uint8_t rndVal [ 4 ] ;
ZeroMemory ( & rndVal , sizeof ( uint32_t ) ) ;
while ( rndVal [ 0 ] + rndVal [ 1 ] + rndVal [ 2 ] < 10 ) {
rndVal [ 0 ] = ( uint8_t ) rndDist ( rndEngine ) ;
rndVal [ 1 ] = ( uint8_t ) rndDist ( rndEngine ) ;
rndVal [ 2 ] = ( uint8_t ) rndDist ( rndEngine ) ;
}
2020-07-11 19:22:19 +02:00
rndVal [ 3 ] = 255 ;
2020-07-11 13:01:02 +02:00
for ( uint32_t y = 0 ; y < page . extent . height ; y + + )
{
for ( uint32_t x = 0 ; x < page . extent . width ; x + + )
{
for ( uint32_t c = 0 ; c < 4 ; c + + , + + data )
{
* data = rndVal [ c ] ;
}
}
}
VkCommandBuffer copyCmd = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
vks : : tools : : setImageLayout ( copyCmd , image , VK_IMAGE_ASPECT_COLOR_BIT , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT , VK_PIPELINE_STAGE_TRANSFER_BIT ) ;
VkBufferImageCopy region { } ;
region . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
region . imageSubresource . layerCount = 1 ;
2020-07-11 21:23:05 +02:00
region . imageSubresource . mipLevel = page . mipLevel ;
2020-07-11 13:01:02 +02:00
region . imageOffset = page . offset ;
region . imageExtent = page . extent ;
vkCmdCopyBufferToImage ( copyCmd , imageBuffer . buffer , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1 , & region ) ;
vks : : tools : : setImageLayout ( copyCmd , image , VK_IMAGE_ASPECT_COLOR_BIT , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ) ;
vulkanDevice - > flushCommandBuffer ( copyCmd , queue ) ;
imageBuffer . destroy ( ) ;
}
void fillRandomPages ( )
2016-09-14 19:01:44 +02:00
{
vkDeviceWaitIdle ( device ) ;
2020-07-11 13:01:02 +02:00
std : : default_random_engine rndEngine ( std : : random_device { } ( ) ) ;
std : : uniform_real_distribution < float > rndDist ( 0.0f , 1.0f ) ;
std : : vector < VirtualTexturePage > updatedPages ;
2020-07-11 19:22:19 +02:00
for ( auto & page : texture . pages ) {
2020-07-11 13:01:02 +02:00
if ( rndDist ( rndEngine ) < 0.5f ) {
continue ;
}
page . allocate ( device , memoryTypeIndex ) ;
updatedPages . push_back ( page ) ;
2016-09-14 19:01:44 +02:00
}
2020-07-11 13:01:02 +02:00
// Update sparse queue binding
2016-09-14 19:01:44 +02:00
texture . updateSparseBindInfo ( ) ;
2020-07-11 13:01:02 +02:00
VkFenceCreateInfo fenceInfo = vks : : initializers : : fenceCreateInfo ( VK_FLAGS_NONE ) ;
VkFence fence ;
VK_CHECK_RESULT ( vkCreateFence ( device , & fenceInfo , nullptr , & fence ) ) ;
vkQueueBindSparse ( queue , 1 , & texture . bindSparseInfo , fence ) ;
vkWaitForFences ( device , 1 , & fence , VK_TRUE , UINT64_MAX ) ;
for ( auto & page : updatedPages ) {
uploadContent ( page , texture . image ) ;
}
2016-09-14 19:01:44 +02:00
}
2020-07-11 19:22:19 +02:00
void fillMipTail ( )
{
//@todo: WIP
VkDeviceSize imageMipTailSize = texture . sparseImageMemoryRequirements . imageMipTailSize ;
VkDeviceSize imageMipTailOffset = texture . sparseImageMemoryRequirements . imageMipTailOffset ;
// Stride between memory bindings for each mip level if not single mip tail (VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT not set)
VkDeviceSize imageMipTailStride = texture . sparseImageMemoryRequirements . imageMipTailStride ;
VkSparseImageMemoryBind mipTailimageMemoryBind { } ;
VkMemoryAllocateInfo allocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
allocInfo . allocationSize = imageMipTailSize ;
allocInfo . memoryTypeIndex = memoryTypeIndex ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & allocInfo , nullptr , & mipTailimageMemoryBind . memory ) ) ;
uint32_t mipLevel = texture . sparseImageMemoryRequirements . imageMipTailFirstLod ;
uint32_t width = std : : max ( texture . width > > texture . sparseImageMemoryRequirements . imageMipTailFirstLod , 1u ) ;
uint32_t height = std : : max ( texture . height > > texture . sparseImageMemoryRequirements . imageMipTailFirstLod , 1u ) ;
uint32_t depth = 1 ;
for ( uint32_t i = texture . mipTailStart ; i < texture . mipLevels ; i + + ) {
const uint32_t width = std : : max ( texture . width > > i , 1u ) ;
const uint32_t height = std : : max ( texture . height > > i , 1u ) ;
const uint32_t depth = 1 ;
// Generate some random image data and upload as a buffer
const size_t bufferSize = 4 * width * height ;
vks : : Buffer imageBuffer ;
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_TRANSFER_SRC_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& imageBuffer ,
bufferSize ) ) ;
imageBuffer . map ( ) ;
// Fill buffer with random colors
std : : random_device rd ;
std : : mt19937 rndEngine ( rd ( ) ) ;
std : : uniform_int_distribution < uint32_t > rndDist ( 0 , 255 ) ;
uint8_t * data = ( uint8_t * ) imageBuffer . mapped ;
uint8_t rndVal [ 4 ] ;
ZeroMemory ( & rndVal , sizeof ( uint32_t ) ) ;
while ( rndVal [ 0 ] + rndVal [ 1 ] + rndVal [ 2 ] < 10 ) {
rndVal [ 0 ] = ( uint8_t ) rndDist ( rndEngine ) ;
rndVal [ 1 ] = ( uint8_t ) rndDist ( rndEngine ) ;
rndVal [ 2 ] = ( uint8_t ) rndDist ( rndEngine ) ;
}
rndVal [ 3 ] = 255 ;
switch ( mipLevel ) {
case 0 :
rndVal [ 0 ] = rndVal [ 1 ] = rndVal [ 2 ] = 255 ;
break ;
case 1 :
rndVal [ 0 ] = rndVal [ 1 ] = rndVal [ 2 ] = 200 ;
break ;
case 2 :
rndVal [ 0 ] = rndVal [ 1 ] = rndVal [ 2 ] = 150 ;
break ;
}
for ( uint32_t y = 0 ; y < height ; y + + )
{
for ( uint32_t x = 0 ; x < width ; x + + )
{
for ( uint32_t c = 0 ; c < 4 ; c + + , + + data )
{
* data = rndVal [ c ] ;
}
}
}
VkCommandBuffer copyCmd = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
vks : : tools : : setImageLayout ( copyCmd , texture . image , VK_IMAGE_ASPECT_COLOR_BIT , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT , VK_PIPELINE_STAGE_TRANSFER_BIT ) ;
VkBufferImageCopy region { } ;
region . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
region . imageSubresource . layerCount = 1 ;
region . imageSubresource . mipLevel = i ;
region . imageOffset = { } ;
region . imageExtent = { width , height , depth } ;
vkCmdCopyBufferToImage ( copyCmd , imageBuffer . buffer , texture . image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1 , & region ) ;
vks : : tools : : setImageLayout ( copyCmd , texture . image , VK_IMAGE_ASPECT_COLOR_BIT , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ) ;
vulkanDevice - > flushCommandBuffer ( copyCmd , queue ) ;
imageBuffer . destroy ( ) ;
}
}
2020-07-11 13:01:02 +02:00
void flushRandomPages ( )
2016-09-14 19:01:44 +02:00
{
vkDeviceWaitIdle ( device ) ;
2016-09-17 19:08:27 +02:00
2020-07-11 13:01:02 +02:00
std : : default_random_engine rndEngine ( std : : random_device { } ( ) ) ;
std : : uniform_real_distribution < float > rndDist ( 0.0f , 1.0f ) ;
2016-09-17 19:08:27 +02:00
2020-07-11 13:01:02 +02:00
std : : vector < VirtualTexturePage > updatedPages ;
for ( auto & page : texture . pages )
{
if ( rndDist ( rndEngine ) < 0.5f ) {
continue ;
2016-09-14 19:01:44 +02:00
}
2020-07-11 13:01:02 +02:00
page . release ( device ) ;
2016-09-14 19:01:44 +02:00
}
2016-09-17 19:08:27 +02:00
// Update sparse queue binding
2016-09-14 19:01:44 +02:00
texture . updateSparseBindInfo ( ) ;
2020-07-11 13:01:02 +02:00
VkFenceCreateInfo fenceInfo = vks : : initializers : : fenceCreateInfo ( VK_FLAGS_NONE ) ;
VkFence fence ;
VK_CHECK_RESULT ( vkCreateFence ( device , & fenceInfo , nullptr , & fence ) ) ;
vkQueueBindSparse ( queue , 1 , & texture . bindSparseInfo , fence ) ;
vkWaitForFences ( device , 1 , & fence , VK_TRUE , UINT64_MAX ) ;
2016-09-14 19:01:44 +02:00
}
2020-07-11 13:01:02 +02:00
virtual void OnUpdateUIOverlay ( vks : : UIOverlay * overlay )
2016-09-11 18:42:30 +02:00
{
2017-11-01 14:22:10 +01:00
if ( overlay - > header ( " Settings " ) ) {
2020-07-11 19:22:19 +02:00
if ( overlay - > sliderFloat ( " LOD bias " , & uboVS . lodBias , - ( float ) texture . mipLevels , ( float ) texture . mipLevels ) ) {
2017-11-01 14:22:10 +01:00
updateUniformBuffers ( ) ;
}
2020-07-11 13:01:02 +02:00
if ( overlay - > button ( " Fill random pages " ) ) {
fillRandomPages ( ) ;
2017-11-01 14:22:10 +01:00
}
2020-07-11 13:01:02 +02:00
if ( overlay - > button ( " Flush random pages " ) ) {
flushRandomPages ( ) ;
2016-10-01 17:37:11 +02:00
}
2020-07-11 19:22:19 +02:00
if ( overlay - > button ( " Fill mip tail " ) ) {
fillMipTail ( ) ;
}
2016-09-11 18:42:30 +02:00
}
2017-11-01 14:22:10 +01:00
if ( overlay - > header ( " Statistics " ) ) {
uint32_t respages = 0 ;
2020-07-11 13:01:02 +02:00
std : : for_each ( texture . pages . begin ( ) , texture . pages . end ( ) , [ & respages ] ( VirtualTexturePage page ) { respages + = ( page . resident ( ) ) ? 1 : 0 ; } ) ;
2017-11-01 14:22:10 +01:00
overlay - > text ( " Resident pages: %d of %d " , respages , static_cast < uint32_t > ( texture . pages . size ( ) ) ) ;
2020-07-11 19:22:19 +02:00
overlay - > text ( " Mip tail starts at: %d " , texture . mipTailStart ) ;
2017-11-01 14:22:10 +01:00
}
2016-09-11 18:42:30 +02:00
}
} ;
VULKAN_EXAMPLE_MAIN ( )