2016-09-11 18:42:30 +02:00
/*
* Vulkan Example - Sparse texture residency example
*
* Copyright ( C ) 2016 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
/*
2020-05-29 16:08:53 +01:00
todos :
2016-09-11 18:42:30 +02:00
- check sparse binding support on queue
- residencyNonResidentStrict
- meta data
- Run - time image data upload
*/
# 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-09 21:55:35 +01:00
# include "VulkanTexture.hpp"
2017-02-12 10:16:07 +01:00
# include "VulkanDevice.hpp"
2017-02-12 10:44:51 +01:00
# include "VulkanBuffer.hpp"
2017-02-12 13:44:57 +01:00
# include "VulkanHeightmap.hpp"
2016-09-11 18:42:30 +02:00
# define VERTEX_BUFFER_BIND_ID 0
# define ENABLE_VALIDATION false
// Vertex layout for this example
struct Vertex {
float pos [ 3 ] ;
float normal [ 3 ] ;
2016-10-20 21:30:37 +02:00
float uv [ 2 ] ;
2016-09-11 18:42:30 +02:00
} ;
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
}
// Allocate Vulkan memory for the virtual page
void allocate ( VkDevice device , uint32_t memoryTypeIndex )
{
if ( imageMemoryBind . memory ! = VK_NULL_HANDLE )
{
//std::cout << "Page " << index << " already allocated" << std::endl;
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 ;
//std::cout << "Page " << index << " released" << std::endl;
}
}
} ;
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-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 ( ) ) ;
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
2016-10-20 21:30:37 +02:00
sparseImageMemoryBinds . resize ( pages . size ( ) ) ;
uint32_t index = 0 ;
2016-09-14 19:01:44 +02:00
for ( auto page : pages )
{
2016-10-20 21:30:37 +02:00
sparseImageMemoryBinds [ index ] = page . imageMemoryBind ;
index + + ;
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
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 ;
// Opaque image memory binds (mip tail)
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
int32_t lastFilledMip = 0 ;
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 ;
2016-09-17 19:08:27 +02:00
struct {
2017-02-09 21:55:35 +01:00
vks : : Texture2D source ;
2016-09-17 19:08:27 +02:00
} textures ;
2017-02-12 13:35:26 +01:00
vks : : HeightMap * heightMap = nullptr ;
2016-10-01 17:37:11 +02:00
2016-09-11 18:42:30 +02:00
struct {
VkPipelineVertexInputStateCreateInfo inputState ;
std : : vector < VkVertexInputBindingDescription > bindingDescriptions ;
std : : vector < VkVertexInputAttributeDescription > attributeDescriptions ;
} vertices ;
uint32_t indexCount ;
2017-02-12 10:44:51 +01:00
vks : : Buffer uniformBufferVS ;
2016-09-11 18:42:30 +02:00
struct UboVS {
glm : : mat4 projection ;
glm : : mat4 model ;
glm : : vec4 viewPos ;
float lodBias = 0.0f ;
} uboVS ;
struct {
VkPipeline solid ;
} pipelines ;
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 ( " " ) ) ;
2016-10-01 17:37:11 +02:00
camera . type = Camera : : CameraType : : firstperson ;
2016-10-20 21:30:37 +02:00
camera . movementSpeed = 50.0f ;
2016-10-01 17:37:11 +02:00
# ifndef __ANDROID__
camera . rotationSpeed = 0.25f ;
# endif
2016-10-20 21:30:37 +02:00
camera . position = { 84.5f , 40.5f , 225.0f } ;
camera . setRotation ( glm : : vec3 ( - 8.5f , - 200.0f , 0.0f ) ) ;
camera . setPerspective ( 60.0f , ( float ) width / ( float ) height , 0.1f , 1024.0f ) ;
2017-11-01 14:22:10 +01:00
settings . overlay = true ;
2020-05-29 16:08:53 +01:00
// Device features to be enabled for this example
2016-12-25 12:55:11 +01:00
enabledFeatures . shaderResourceResidency = VK_TRUE ;
enabledFeatures . shaderResourceMinLod = VK_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
2016-10-20 21:30:37 +02:00
if ( heightMap )
delete heightMap ;
2016-09-11 18:42:30 +02:00
destroyTextureImage ( texture ) ;
vkDestroySemaphore ( device , bindSparseSemaphore , nullptr ) ;
vkDestroyPipeline ( device , pipelines . solid , nullptr ) ;
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
uniformBufferVS . destroy ( ) ;
}
2017-11-01 14:22:10 +01:00
virtual void getEnabledFeatures ( )
{
if ( deviceFeatures . sparseBinding & & deviceFeatures . sparseResidencyImage2D ) {
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 ;
res . x = extent . width / granularity . width + ( ( extent . width % granularity . width ) ? 1u : 0u ) ;
res . y = extent . height / granularity . height + ( ( extent . height % granularity . height ) ? 1u : 0u ) ;
res . z = extent . depth / granularity . depth + ( ( extent . depth % granularity . depth ) ? 1u : 0u ) ;
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 ) ;
// 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 ;
vkGetPhysicalDeviceSparseImageFormatProperties (
physicalDevice ,
format ,
VK_IMAGE_TYPE_2D ,
VK_SAMPLE_COUNT_1_BIT ,
VK_IMAGE_USAGE_SAMPLED_BIT ,
VK_IMAGE_TILING_OPTIMAL ,
& sparsePropertiesCount ,
2016-10-01 17:37:11 +02:00
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 ) ;
vkGetPhysicalDeviceSparseImageFormatProperties (
physicalDevice ,
format ,
VK_IMAGE_TYPE_2D ,
VK_SAMPLE_COUNT_1_BIT ,
VK_IMAGE_USAGE_SAMPLED_BIT ,
VK_IMAGE_TILING_OPTIMAL ,
& sparsePropertiesCount ,
sparseProperties . data ( ) ) ;
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 ( ) ;
2016-09-11 18:42:30 +02:00
sparseImageCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
sparseImageCreateInfo . format = texture . format ;
sparseImageCreateInfo . mipLevels = texture . mipLevels ;
sparseImageCreateInfo . arrayLayers = texture . layerCount ;
sparseImageCreateInfo . samples = VK_SAMPLE_COUNT_1_BIT ;
sparseImageCreateInfo . tiling = VK_IMAGE_TILING_OPTIMAL ;
sparseImageCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
sparseImageCreateInfo . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
sparseImageCreateInfo . extent = { texture . width , texture . height , 1 } ;
sparseImageCreateInfo . usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ;
sparseImageCreateInfo . flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT ;
VK_CHECK_RESULT ( vkCreateImage ( device , & sparseImageCreateInfo , nullptr , & texture . image ) ) ;
// 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-10-01 17:37:11 +02:00
lastFilledMip = texture . mipTailStart - 1 ;
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 ) ;
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 ;
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 ;
// Alllocate memory for some blocks
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
VirtualTexturePage * newPage = texture . addPage ( offset , extent , sparseImageMemoryReqs . alignment , mipLevel , layer ) ;
newPage - > imageMemoryBind . subresource = subResource ;
2016-09-11 18:42:30 +02:00
2016-09-14 19:01:44 +02:00
if ( ( x % 2 = = 1 ) | | ( y % 2 = = 1 ) )
{
// Allocate memory for this virtual page
2016-09-17 19:08:27 +02:00
//newPage->allocate(device, memoryTypeIndex);
2016-09-14 19:01:44 +02:00
}
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 ;
2016-10-01 17:37:11 +02:00
sampler . addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
sampler . addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
sampler . addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
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 ;
2016-10-20 21:30:37 +02:00
// Fill smallest (non-tail) mip map leve
fillVirtualTexture ( lastFilledMip ) ;
2016-09-11 18:42:30 +02:00
}
// 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 ] ;
2016-10-01 17:37:11 +02:00
clearValues [ 0 ] . color = { { 0.0f , 0.0f , 0.2f , 1.0f } } ;
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 )
{
// Set target frame buffer
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 ) ;
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelines . solid ) ;
VkDeviceSize offsets [ 1 ] = { 0 } ;
2016-10-20 21:30:37 +02:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & heightMap - > vertexBuffer . buffer , offsets ) ;
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , heightMap - > indexBuffer . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , heightMap - > 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 ( ) ;
// Sparse bindings
// vkQueueBindSparse(queue, 1, &bindSparseInfo, VK_NULL_HANDLE);
//todo: use sparse bind semaphore
// vkQueueWaitIdle(queue);
// Command buffer to be sumitted to the queue
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
// Submit to queue
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
2016-09-17 19:08:27 +02:00
void loadAssets ( )
{
2017-04-29 14:00:43 -07:00
textures . source . loadFromFile ( getAssetPath ( ) + " textures/ground_dry_bc3_unorm.ktx " , VK_FORMAT_BC3_UNORM_BLOCK , vulkanDevice , queue , VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ) ;
2016-09-17 19:08:27 +02:00
}
2016-10-20 21:30:37 +02:00
// Generate a terrain quad patch for feeding to the tessellation control shader
void generateTerrain ( )
2016-09-11 18:42:30 +02:00
{
2017-02-12 13:35:26 +01:00
heightMap = new vks : : HeightMap ( vulkanDevice , queue ) ;
2016-10-20 21:30:37 +02:00
# if defined(__ANDROID__)
2017-02-12 13:35:26 +01:00
heightMap - > loadFromFile ( getAssetPath ( ) + " textures/terrain_heightmap_r16.ktx " , 128 , glm : : vec3 ( 2.0f , 48.0f , 2.0f ) , vks : : HeightMap : : topologyTriangles , androidApp - > activity - > assetManager ) ;
2016-10-20 21:30:37 +02:00
# else
2017-02-12 13:35:26 +01:00
heightMap - > loadFromFile ( getAssetPath ( ) + " textures/terrain_heightmap_r16.ktx " , 128 , glm : : vec3 ( 2.0f , 48.0f , 2.0f ) , vks : : HeightMap : : topologyTriangles ) ;
2016-10-20 21:30:37 +02:00
# endif
2016-09-11 18:42:30 +02:00
}
void setupVertexDescriptions ( )
{
// Binding description
vertices . bindingDescriptions . resize ( 1 ) ;
vertices . bindingDescriptions [ 0 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputBindingDescription (
2020-05-29 16:08:53 +01:00
VERTEX_BUFFER_BIND_ID ,
sizeof ( Vertex ) ,
2016-09-11 18:42:30 +02:00
VK_VERTEX_INPUT_RATE_VERTEX ) ;
// Attribute descriptions
// Describes memory layout and shader positions
vertices . attributeDescriptions . resize ( 3 ) ;
// Location 0 : Position
vertices . attributeDescriptions [ 0 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription (
2016-09-11 18:42:30 +02:00
VERTEX_BUFFER_BIND_ID ,
0 ,
VK_FORMAT_R32G32B32_SFLOAT ,
2020-05-29 16:08:53 +01:00
offsetof ( Vertex , pos ) ) ;
2016-10-20 21:30:37 +02:00
// Location 1 : Vertex normal
2016-09-11 18:42:30 +02:00
vertices . attributeDescriptions [ 1 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription (
2016-09-11 18:42:30 +02:00
VERTEX_BUFFER_BIND_ID ,
1 ,
2016-10-20 21:30:37 +02:00
VK_FORMAT_R32G32B32_SFLOAT ,
offsetof ( Vertex , normal ) ) ;
// Location 1 : Texture coordinates
2016-09-11 18:42:30 +02:00
vertices . attributeDescriptions [ 2 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription (
2016-09-11 18:42:30 +02:00
VERTEX_BUFFER_BIND_ID ,
2 ,
2016-10-20 21:30:37 +02:00
VK_FORMAT_R32G32_SFLOAT ,
offsetof ( Vertex , uv ) ) ;
2016-09-11 18:42:30 +02:00
2017-02-12 11:12:42 +01:00
vertices . inputState = vks : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
2016-09-11 18:42:30 +02:00
vertices . inputState . vertexBindingDescriptionCount = static_cast < uint32_t > ( vertices . bindingDescriptions . size ( ) ) ;
vertices . inputState . pVertexBindingDescriptions = vertices . bindingDescriptions . data ( ) ;
vertices . inputState . vertexAttributeDescriptionCount = static_cast < uint32_t > ( vertices . attributeDescriptions . size ( ) ) ;
vertices . inputState . pVertexAttributeDescriptions = vertices . attributeDescriptions . data ( ) ;
}
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 ( )
{
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineInputAssemblyStateCreateInfo (
2016-09-11 18:42:30 +02:00
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST ,
0 ,
VK_FALSE ) ;
VkPipelineRasterizationStateCreateInfo rasterizationState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineRasterizationStateCreateInfo (
2016-09-11 18:42:30 +02:00
VK_POLYGON_MODE_FILL ,
2016-10-20 21:30:37 +02:00
VK_CULL_MODE_BACK_BIT ,
2016-09-11 18:42:30 +02:00
VK_FRONT_FACE_COUNTER_CLOCKWISE ,
0 ) ;
VkPipelineColorBlendAttachmentState blendAttachmentState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendAttachmentState (
2016-09-11 18:42:30 +02:00
0xf ,
VK_FALSE ) ;
VkPipelineColorBlendStateCreateInfo colorBlendState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendStateCreateInfo (
2020-05-29 16:08:53 +01:00
1 ,
2016-09-11 18:42:30 +02:00
& blendAttachmentState ) ;
VkPipelineDepthStencilStateCreateInfo depthStencilState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDepthStencilStateCreateInfo (
2016-09-11 18:42:30 +02:00
VK_TRUE ,
VK_TRUE ,
VK_COMPARE_OP_LESS_OR_EQUAL ) ;
VkPipelineViewportStateCreateInfo viewportState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineViewportStateCreateInfo ( 1 , 1 , 0 ) ;
2016-09-11 18:42:30 +02:00
VkPipelineMultisampleStateCreateInfo multisampleState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineMultisampleStateCreateInfo (
2016-09-11 18:42:30 +02:00
VK_SAMPLE_COUNT_1_BIT ,
0 ) ;
std : : vector < VkDynamicState > dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT ,
VK_DYNAMIC_STATE_SCISSOR
} ;
VkPipelineDynamicStateCreateInfo dynamicState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDynamicStateCreateInfo (
2016-09-11 18:42:30 +02:00
dynamicStateEnables . data ( ) ,
static_cast < uint32_t > ( dynamicStateEnables . size ( ) ) ,
0 ) ;
// Load shaders
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
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 ) ;
2016-09-11 18:42:30 +02:00
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineCreateInfo (
2016-09-11 18:42:30 +02:00
pipelineLayout ,
renderPass ,
0 ) ;
pipelineCreateInfo . pVertexInputState = & vertices . inputState ;
pipelineCreateInfo . pInputAssemblyState = & inputAssemblyState ;
pipelineCreateInfo . pRasterizationState = & rasterizationState ;
pipelineCreateInfo . pColorBlendState = & colorBlendState ;
pipelineCreateInfo . pMultisampleState = & multisampleState ;
pipelineCreateInfo . pViewportState = & viewportState ;
pipelineCreateInfo . pDepthStencilState = & depthStencilState ;
pipelineCreateInfo . pDynamicState = & dynamicState ;
pipelineCreateInfo . stageCount = static_cast < uint32_t > ( shaderStages . size ( ) ) ;
pipelineCreateInfo . pStages = shaderStages . data ( ) ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipelines . solid ) ) ;
}
// 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-10-20 21:30:37 +02:00
generateTerrain ( ) ;
2016-09-11 18:42:30 +02:00
setupVertexDescriptions ( ) ;
prepareUniformBuffers ( ) ;
2016-10-01 17:37:11 +02:00
// Create a virtual texture with max. possible dimension (does not take up any VRAM yet)
prepareSparseTexture ( 8192 , 8192 , 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 ( ) ;
}
virtual void viewChanged ( )
{
updateUniformBuffers ( ) ;
}
2016-09-14 19:01:44 +02:00
// Clear all pages of the virtual texture
// todo: just for testing
void flushVirtualTexture ( )
{
vkDeviceWaitIdle ( device ) ;
for ( auto & page : texture . pages )
{
page . release ( device ) ;
}
texture . updateSparseBindInfo ( ) ;
vkQueueBindSparse ( queue , 1 , & texture . bindSparseInfo , VK_NULL_HANDLE ) ;
//todo: use sparse bind semaphore
vkQueueWaitIdle ( queue ) ;
2016-10-01 17:37:11 +02:00
lastFilledMip = texture . mipTailStart - 1 ;
2016-09-14 19:01:44 +02:00
}
2016-10-01 17:37:11 +02:00
// Fill a complete mip level
2016-10-20 21:30:37 +02:00
void fillVirtualTexture ( int32_t & mipLevel )
2016-09-14 19:01:44 +02:00
{
vkDeviceWaitIdle ( device ) ;
2016-09-17 19:08:27 +02:00
std : : vector < VkImageBlit > imageBlits ;
2016-09-14 19:01:44 +02:00
for ( auto & page : texture . pages )
{
2016-10-01 17:37:11 +02:00
if ( ( page . mipLevel = = mipLevel ) & & /*(rndDist(rndEngine) < 0.5f) &&*/ ( page . imageMemoryBind . memory = = VK_NULL_HANDLE ) )
2016-09-14 19:01:44 +02:00
{
2016-09-17 19:08:27 +02:00
// Allocate page memory
2016-09-14 19:01:44 +02:00
page . allocate ( device , memoryTypeIndex ) ;
2016-09-17 19:08:27 +02:00
// Current mip level scaling
uint32_t scale = texture . width / ( texture . width > > page . mipLevel ) ;
for ( uint32_t x = 0 ; x < scale ; x + + )
{
for ( uint32_t y = 0 ; y < scale ; y + + )
{
// Image blit
VkImageBlit blit { } ;
// Source
blit . srcSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
2016-10-01 17:37:11 +02:00
blit . srcSubresource . baseArrayLayer = 0 ;
2016-09-17 19:08:27 +02:00
blit . srcSubresource . layerCount = 1 ;
blit . srcSubresource . mipLevel = 0 ;
blit . srcOffsets [ 0 ] = { 0 , 0 , 0 } ;
blit . srcOffsets [ 1 ] = { static_cast < int32_t > ( textures . source . width ) , static_cast < int32_t > ( textures . source . height ) , 1 } ;
// Dest
blit . dstSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
blit . dstSubresource . baseArrayLayer = 0 ;
blit . dstSubresource . layerCount = 1 ;
blit . dstSubresource . mipLevel = page . mipLevel ;
blit . dstOffsets [ 0 ] . x = static_cast < int32_t > ( page . offset . x + x * 128 / scale ) ;
blit . dstOffsets [ 0 ] . y = static_cast < int32_t > ( page . offset . y + y * 128 / scale ) ;
blit . dstOffsets [ 0 ] . z = 0 ;
blit . dstOffsets [ 1 ] . x = static_cast < int32_t > ( blit . dstOffsets [ 0 ] . x + page . extent . width / scale ) ;
blit . dstOffsets [ 1 ] . y = static_cast < int32_t > ( blit . dstOffsets [ 0 ] . y + page . extent . height / scale ) ;
blit . dstOffsets [ 1 ] . z = 1 ;
imageBlits . push_back ( blit ) ;
}
}
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 ( ) ;
vkQueueBindSparse ( queue , 1 , & texture . bindSparseInfo , VK_NULL_HANDLE ) ;
//todo: use sparse bind semaphore
vkQueueWaitIdle ( queue ) ;
2016-09-17 19:08:27 +02:00
// Issue blit commands
if ( imageBlits . size ( ) > 0 )
{
2016-10-01 17:37:11 +02:00
auto tStart = std : : chrono : : high_resolution_clock : : now ( ) ;
2016-09-17 19:08:27 +02:00
VkCommandBuffer copyCmd = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
vkCmdBlitImage (
copyCmd ,
textures . source . image ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
texture . image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
static_cast < uint32_t > ( imageBlits . size ( ) ) ,
imageBlits . data ( ) ,
VK_FILTER_LINEAR
) ;
vulkanDevice - > flushCommandBuffer ( copyCmd , queue ) ;
2016-10-01 17:37:11 +02:00
auto tEnd = std : : chrono : : high_resolution_clock : : now ( ) ;
auto tDiff = std : : chrono : : duration < double , std : : milli > ( tEnd - tStart ) . count ( ) ;
std : : cout < < " Image blits took " < < tDiff < < " ms " < < std : : endl ;
2016-09-17 19:08:27 +02:00
}
vkQueueWaitIdle ( queue ) ;
2016-10-20 21:30:37 +02:00
mipLevel - - ;
2016-09-14 19:01:44 +02:00
}
2017-11-01 14:22:10 +01: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 " ) ) {
if ( overlay - > sliderFloat ( " LOD bias " , & uboVS . lodBias , 0.0f , ( float ) texture . mipLevels ) ) {
updateUniformBuffers ( ) ;
}
overlay - > text ( " Last filled mip level: %d " , lastFilledMip ) ;
if ( overlay - > button ( " Fill next mip level " ) ) {
if ( lastFilledMip > = 0 ) {
fillVirtualTexture ( lastFilledMip ) ;
}
}
if ( overlay - > button ( " Flush virtual texture " ) ) {
flushVirtualTexture ( ) ;
2016-10-01 17:37:11 +02:00
}
2016-09-11 18:42:30 +02:00
}
2017-11-01 14:22:10 +01:00
if ( overlay - > header ( " Statistics " ) ) {
uint32_t respages = 0 ;
std : : for_each ( texture . pages . begin ( ) , texture . pages . end ( ) , [ & respages ] ( VirtualTexturePage page ) { respages + = ( page . imageMemoryBind . memory ! = VK_NULL_HANDLE ) ? 1 : 0 ; } ) ;
overlay - > text ( " Resident pages: %d of %d " , respages , static_cast < uint32_t > ( texture . pages . size ( ) ) ) ;
}
2016-09-11 18:42:30 +02:00
}
} ;
VULKAN_EXAMPLE_MAIN ( )