2016-08-15 15:17:01 +02:00
/*
* Vulkan Example - Runtime mip map generation
*
2019-08-03 10:20:36 +02:00
* Copyright ( C ) by Sascha Willems - www . saschawillems . de
2016-08-15 15:17:01 +02:00
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# include "vulkanexamplebase.h"
2020-07-28 20:20:38 +02:00
# include "VulkanglTFModel.h"
2019-08-03 10:20:36 +02:00
# include <ktx.h>
# include <ktxvulkan.h>
2016-08-15 15:17:01 +02:00
# define ENABLE_VALIDATION false
class VulkanExample : public VulkanExampleBase
{
public :
struct Texture {
VkImage image ;
VkDeviceMemory deviceMemory ;
VkImageView view ;
uint32_t width , height ;
uint32_t mipLevels ;
} texture ;
// To demonstrate mip mapping and filtering this example uses separate samplers
2017-11-01 14:22:10 +01:00
std : : vector < std : : string > samplerNames { " No mip maps " , " Mip maps (bilinear) " , " Mip maps (anisotropic) " } ;
2016-08-15 15:17:01 +02:00
std : : vector < VkSampler > samplers ;
2020-07-28 20:20:38 +02:00
vkglTF : : Model model ;
2016-08-15 15:17:01 +02:00
2017-02-12 10:44:51 +01:00
vks : : Buffer uniformBufferVS ;
2016-08-15 15:17:01 +02:00
struct uboVS {
glm : : mat4 projection ;
glm : : mat4 view ;
2016-08-15 16:25:54 +02:00
glm : : mat4 model ;
2016-09-04 14:26:33 +02:00
glm : : vec4 viewPos ;
2016-08-15 15:17:01 +02:00
float lodBias = 0.0f ;
2017-11-01 14:22:10 +01:00
int32_t samplerIndex = 2 ;
2016-08-15 15:17:01 +02:00
} uboVS ;
2020-07-28 20:20:38 +02:00
VkPipeline pipeline ;
2016-08-15 15:17:01 +02:00
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
2017-11-01 14:22:10 +01:00
title = " Runtime mip map generation " ;
2016-08-15 15:17:01 +02:00
camera . type = Camera : : CameraType : : firstperson ;
2016-09-03 22:31:12 +02:00
camera . setPerspective ( 60.0f , ( float ) width / ( float ) height , 0.1f , 1024.0f ) ;
2016-08-15 15:17:01 +02:00
camera . setRotation ( glm : : vec3 ( 0.0f , 90.0f , 0.0f ) ) ;
2016-09-03 22:31:12 +02:00
camera . setTranslation ( glm : : vec3 ( 40.75f , 0.0f , 0.0f ) ) ;
2016-08-15 15:17:01 +02:00
camera . movementSpeed = 2.5f ;
camera . rotationSpeed = 0.5f ;
2017-11-01 14:22:10 +01:00
settings . overlay = true ;
2016-08-27 14:03:13 +02:00
timerSpeed * = 0.05f ;
2016-08-15 15:17:01 +02:00
}
~ VulkanExample ( )
{
destroyTextureImage ( texture ) ;
2020-07-28 20:20:38 +02:00
vkDestroyPipeline ( device , pipeline , nullptr ) ;
2016-08-15 15:17:01 +02:00
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
uniformBufferVS . destroy ( ) ;
for ( auto sampler : samplers )
{
vkDestroySampler ( device , sampler , nullptr ) ;
}
}
2019-11-19 19:07:03 +01:00
virtual void getEnabledFeatures ( )
{
if ( deviceFeatures . samplerAnisotropy ) {
enabledFeatures . samplerAnisotropy = VK_TRUE ;
}
}
2019-12-08 08:58:35 +01:00
void loadTexture ( std : : string filename , VkFormat format , bool forceLinearTiling )
2016-08-15 15:17:01 +02:00
{
2019-08-03 10:20:36 +02:00
ktxResult result ;
ktxTexture * ktxTexture ;
2016-08-15 15:17:01 +02:00
# if defined(__ANDROID__)
// Textures are stored inside the apk on Android (compressed)
// So they need to be loaded via the asset manager
2019-08-03 10:20:36 +02:00
AAsset * asset = AAssetManager_open ( androidApp - > activity - > assetManager , filename . c_str ( ) , AASSET_MODE_STREAMING ) ;
if ( ! asset ) {
vks : : tools : : exitFatal ( " Could not load texture from " + filename + " \n \n The file may be part of the additional asset pack. \n \n Run \" download_assets.py \" in the repository root to download the latest version. " , - 1 ) ;
}
2016-08-15 15:17:01 +02:00
size_t size = AAsset_getLength ( asset ) ;
assert ( size > 0 ) ;
2019-08-03 18:20:27 +02:00
ktx_uint8_t * textureData = new ktx_uint8_t [ size ] ;
2016-08-15 15:17:01 +02:00
AAsset_read ( asset , textureData , size ) ;
AAsset_close ( asset ) ;
2019-08-03 18:20:27 +02:00
result = ktxTexture_CreateFromMemory ( textureData , size , KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT , & ktxTexture ) ;
delete [ ] textureData ;
2019-08-03 10:20:36 +02:00
# else
if ( ! vks : : tools : : fileExists ( filename ) ) {
vks : : tools : : exitFatal ( " Could not load texture from " + filename + " \n \n The file may be part of the additional asset pack. \n \n Run \" download_assets.py \" in the repository root to download the latest version. " , - 1 ) ;
}
result = ktxTexture_CreateFromNamedFile ( filename . c_str ( ) , KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT , & ktxTexture ) ;
2020-05-29 16:08:53 +01:00
# endif
2019-08-03 10:20:36 +02:00
assert ( result = = KTX_SUCCESS ) ;
2016-08-15 15:17:01 +02:00
2019-08-03 10:20:36 +02:00
texture . width = ktxTexture - > baseWidth ;
texture . height = ktxTexture - > baseHeight ;
ktx_uint8_t * ktxTextureData = ktxTexture_GetData ( ktxTexture ) ;
ktx_size_t ktxTextureSize = ktxTexture_GetImageSize ( ktxTexture , 0 ) ;
2016-08-15 15:17:01 +02:00
// calculate num of mip maps
// numLevels = 1 + floor(log2(max(w, h, d)))
// Calculated as log2(max(width, height, depth))c + 1 (see specs)
texture . mipLevels = floor ( log2 ( std : : max ( texture . width , texture . height ) ) ) + 1 ;
2020-08-09 13:16:35 +02:00
// Get device properties for the requested texture format
2019-12-08 08:58:35 +01:00
VkFormatProperties formatProperties ;
2016-08-15 15:17:01 +02:00
vkGetPhysicalDeviceFormatProperties ( physicalDevice , format , & formatProperties ) ;
2016-09-06 22:58:11 +02:00
// Mip-chain generation requires support for blit source and destination
assert ( formatProperties . optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT ) ;
assert ( formatProperties . optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ) ;
2016-08-15 15:17:01 +02:00
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo memAllocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2016-08-15 15:17:01 +02:00
VkMemoryRequirements memReqs = { } ;
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer ;
VkDeviceMemory stagingMemory ;
2017-02-12 11:12:42 +01:00
VkBufferCreateInfo bufferCreateInfo = vks : : initializers : : bufferCreateInfo ( ) ;
2019-08-03 10:20:36 +02:00
bufferCreateInfo . size = ktxTextureSize ;
2016-08-15 15:17:01 +02:00
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
2020-05-29 16:08:53 +01:00
bufferCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
2016-08-15 15:17:01 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & bufferCreateInfo , nullptr , & stagingBuffer ) ) ;
vkGetBufferMemoryRequirements ( device , stagingBuffer , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
2017-03-04 13:56:09 +01:00
memAllocInfo . memoryTypeIndex = vulkanDevice - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
2016-08-15 15:17:01 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAllocInfo , nullptr , & stagingMemory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , stagingBuffer , stagingMemory , 0 ) ) ;
// Copy texture data into staging buffer
uint8_t * data ;
VK_CHECK_RESULT ( vkMapMemory ( device , stagingMemory , 0 , memReqs . size , 0 , ( void * * ) & data ) ) ;
2019-08-03 10:20:36 +02:00
memcpy ( data , ktxTextureData , ktxTextureSize ) ;
2016-08-15 15:17:01 +02:00
vkUnmapMemory ( device , stagingMemory ) ;
// Create optimal tiled target image
2017-02-12 11:12:42 +01:00
VkImageCreateInfo imageCreateInfo = vks : : initializers : : imageCreateInfo ( ) ;
2016-08-15 15:17:01 +02:00
imageCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
imageCreateInfo . format = format ;
imageCreateInfo . mipLevels = texture . mipLevels ;
imageCreateInfo . arrayLayers = 1 ;
imageCreateInfo . samples = VK_SAMPLE_COUNT_1_BIT ;
imageCreateInfo . tiling = VK_IMAGE_TILING_OPTIMAL ;
imageCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
imageCreateInfo . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
imageCreateInfo . extent = { texture . width , texture . height , 1 } ;
imageCreateInfo . usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ;
VK_CHECK_RESULT ( vkCreateImage ( device , & imageCreateInfo , nullptr , & texture . image ) ) ;
vkGetImageMemoryRequirements ( device , texture . image , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
memAllocInfo . memoryTypeIndex = vulkanDevice - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAllocInfo , nullptr , & texture . deviceMemory ) ) ;
VK_CHECK_RESULT ( vkBindImageMemory ( device , texture . image , texture . deviceMemory , 0 ) ) ;
2020-04-20 22:13:51 +02:00
VkCommandBuffer copyCmd = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
2016-08-15 15:17:01 +02:00
VkImageSubresourceRange subresourceRange = { } ;
subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
subresourceRange . levelCount = 1 ;
subresourceRange . layerCount = 1 ;
// Optimal image will be used as destination for the copy, so we must transfer from our initial undefined image layout to the transfer destination layout
2019-11-19 19:07:03 +01:00
vks : : tools : : insertImageMemoryBarrier (
2017-04-23 11:51:31 +02:00
copyCmd ,
texture . image ,
2019-11-19 19:07:03 +01:00
0 ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
2017-04-23 11:51:31 +02:00
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
2019-11-19 19:07:03 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
2017-04-23 11:51:31 +02:00
subresourceRange ) ;
2016-08-15 15:17:01 +02:00
// Copy the first mip of the chain, remaining mips will be generated
VkBufferImageCopy bufferCopyRegion = { } ;
bufferCopyRegion . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
bufferCopyRegion . imageSubresource . mipLevel = 0 ;
bufferCopyRegion . imageSubresource . baseArrayLayer = 0 ;
bufferCopyRegion . imageSubresource . layerCount = 1 ;
bufferCopyRegion . imageExtent . width = texture . width ;
bufferCopyRegion . imageExtent . height = texture . height ;
bufferCopyRegion . imageExtent . depth = 1 ;
vkCmdCopyBufferToImage ( copyCmd , stagingBuffer , texture . image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1 , & bufferCopyRegion ) ;
// Transition first mip level to transfer source for read during blit
2019-11-19 19:07:03 +01:00
vks : : tools : : insertImageMemoryBarrier (
2016-08-15 15:17:01 +02:00
copyCmd ,
texture . image ,
2019-11-19 19:07:03 +01:00
VK_ACCESS_TRANSFER_WRITE_BIT ,
VK_ACCESS_TRANSFER_READ_BIT ,
2016-08-15 15:17:01 +02:00
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
2019-11-19 19:07:03 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
2016-08-15 15:17:01 +02:00
subresourceRange ) ;
2020-04-20 22:13:51 +02:00
vulkanDevice - > flushCommandBuffer ( copyCmd , queue , true ) ;
2016-08-15 15:17:01 +02:00
// Clean up staging resources
vkFreeMemory ( device , stagingMemory , nullptr ) ;
vkDestroyBuffer ( device , stagingBuffer , nullptr ) ;
2019-08-03 10:20:36 +02:00
ktxTexture_Destroy ( ktxTexture ) ;
2016-08-15 15:17:01 +02:00
// Generate the mip chain
// ---------------------------------------------------------------
2016-09-06 22:58:11 +02:00
// We copy down the whole mip chain doing a blit from mip-1 to mip
2016-08-15 15:17:01 +02:00
// An alternative way would be to always blit from the first mip level and sample that one down
2020-04-20 22:13:51 +02:00
VkCommandBuffer blitCmd = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
2016-08-15 15:17:01 +02:00
// Copy down mips from n-1 to n
for ( int32_t i = 1 ; i < texture . mipLevels ; i + + )
{
2020-05-29 16:08:53 +01:00
VkImageBlit imageBlit { } ;
2016-08-15 15:17:01 +02:00
// Source
imageBlit . srcSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
imageBlit . srcSubresource . layerCount = 1 ;
imageBlit . srcSubresource . mipLevel = i - 1 ;
imageBlit . srcOffsets [ 1 ] . x = int32_t ( texture . width > > ( i - 1 ) ) ;
imageBlit . srcOffsets [ 1 ] . y = int32_t ( texture . height > > ( i - 1 ) ) ;
imageBlit . srcOffsets [ 1 ] . z = 1 ;
// Destination
imageBlit . dstSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
imageBlit . dstSubresource . layerCount = 1 ;
imageBlit . dstSubresource . mipLevel = i ;
imageBlit . dstOffsets [ 1 ] . x = int32_t ( texture . width > > i ) ;
imageBlit . dstOffsets [ 1 ] . y = int32_t ( texture . height > > i ) ;
imageBlit . dstOffsets [ 1 ] . z = 1 ;
VkImageSubresourceRange mipSubRange = { } ;
mipSubRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
mipSubRange . baseMipLevel = i ;
mipSubRange . levelCount = 1 ;
mipSubRange . layerCount = 1 ;
2019-11-19 19:07:03 +01:00
// Prepare current mip level as image blit destination
vks : : tools : : insertImageMemoryBarrier (
2016-08-15 15:17:01 +02:00
blitCmd ,
texture . image ,
2019-11-19 19:07:03 +01:00
0 ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
2016-08-15 15:17:01 +02:00
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
2016-12-15 20:01:43 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
2019-11-19 19:07:03 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
mipSubRange ) ;
2016-08-15 15:17:01 +02:00
// Blit from previous level
vkCmdBlitImage (
blitCmd ,
texture . image ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
texture . image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
1 ,
& imageBlit ,
VK_FILTER_LINEAR ) ;
2019-11-19 19:07:03 +01:00
// Prepare current mip level as image blit source for next level
vks : : tools : : insertImageMemoryBarrier (
2016-08-15 15:17:01 +02:00
blitCmd ,
texture . image ,
2019-11-19 19:07:03 +01:00
VK_ACCESS_TRANSFER_WRITE_BIT ,
VK_ACCESS_TRANSFER_READ_BIT ,
2016-08-15 15:17:01 +02:00
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
2019-11-19 19:07:03 +01:00
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
mipSubRange ) ;
2016-08-15 15:17:01 +02:00
}
// After the loop, all mip layers are in TRANSFER_SRC layout, so transition all to SHADER_READ
subresourceRange . levelCount = texture . mipLevels ;
2019-11-19 19:07:03 +01:00
vks : : tools : : insertImageMemoryBarrier (
2016-08-15 15:17:01 +02:00
blitCmd ,
texture . image ,
2019-11-19 19:07:03 +01:00
VK_ACCESS_TRANSFER_READ_BIT ,
VK_ACCESS_SHADER_READ_BIT ,
2016-08-15 15:17:01 +02:00
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
2019-11-19 19:07:03 +01:00
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ,
2016-08-15 15:17:01 +02:00
subresourceRange ) ;
2020-04-20 22:13:51 +02:00
vulkanDevice - > flushCommandBuffer ( blitCmd , queue , true ) ;
2016-08-15 15:17:01 +02:00
// ---------------------------------------------------------------
// Create samplers
samplers . resize ( 3 ) ;
2017-02-12 11:12:42 +01:00
VkSamplerCreateInfo sampler = vks : : initializers : : samplerCreateInfo ( ) ;
2016-08-15 15:17:01 +02:00
sampler . magFilter = VK_FILTER_LINEAR ;
sampler . minFilter = VK_FILTER_LINEAR ;
sampler . mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR ;
2016-09-03 22:31:12 +02:00
sampler . addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT ;
sampler . addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT ;
sampler . addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT ;
2016-08-15 15:17:01 +02:00
sampler . mipLodBias = 0.0f ;
sampler . compareOp = VK_COMPARE_OP_NEVER ;
sampler . minLod = 0.0f ;
sampler . maxLod = 0.0f ;
sampler . borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE ;
sampler . maxAnisotropy = 1.0 ;
sampler . anisotropyEnable = VK_FALSE ;
// Without mip mapping
VK_CHECK_RESULT ( vkCreateSampler ( device , & sampler , nullptr , & samplers [ 0 ] ) ) ;
// With mip mapping
sampler . maxLod = ( float ) texture . mipLevels ;
VK_CHECK_RESULT ( vkCreateSampler ( device , & sampler , nullptr , & samplers [ 1 ] ) ) ;
// With mip mapping and anisotropic filtering
if ( vulkanDevice - > features . samplerAnisotropy )
{
sampler . maxAnisotropy = vulkanDevice - > properties . limits . maxSamplerAnisotropy ;
sampler . anisotropyEnable = VK_TRUE ;
}
VK_CHECK_RESULT ( vkCreateSampler ( device , & sampler , nullptr , & samplers [ 2 ] ) ) ;
// Create image view
2017-02-12 11:12:42 +01:00
VkImageViewCreateInfo view = vks : : initializers : : imageViewCreateInfo ( ) ;
2016-08-15 15:17:01 +02:00
view . image = texture . image ;
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 ;
VK_CHECK_RESULT ( vkCreateImageView ( device , & view , nullptr , & texture . view ) ) ;
}
// Free all Vulkan resources used a texture object
void destroyTextureImage ( Texture texture )
{
vkDestroyImageView ( device , texture . view , nullptr ) ;
vkDestroyImage ( device , texture . image , nullptr ) ;
vkFreeMemory ( device , texture . deviceMemory , nullptr ) ;
}
void buildCommandBuffers ( )
{
2017-02-12 11:12:42 +01:00
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
2016-08-15 15:17:01 +02:00
VkClearValue clearValues [ 2 ] ;
clearValues [ 0 ] . color = defaultClearColor ;
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
2017-02-12 11:12:42 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks : : initializers : : renderPassBeginInfo ( ) ;
2016-08-15 15:17:01 +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-08-15 15:17:01 +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-08-15 15:17:01 +02:00
vkCmdSetScissor ( drawCmdBuffers [ i ] , 0 , 1 , & scissor ) ;
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSet , 0 , NULL ) ;
2020-07-28 20:20:38 +02:00
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline ) ;
2016-08-15 15:17:01 +02:00
2020-07-28 20:20:38 +02:00
model . draw ( drawCmdBuffers [ i ] ) ;
2016-08-15 15:17:01 +02:00
2018-08-30 21:08:02 +02:00
drawUI ( drawCmdBuffers [ i ] ) ;
2016-08-15 15:17:01 +02:00
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
}
}
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
2020-08-09 13:16:35 +02:00
// Command buffer to be submitted to the queue
2016-08-15 15:17:01 +02:00
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
// Submit to queue
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
void loadAssets ( )
{
2020-07-28 20:20:38 +02:00
model . loadFromFile ( getAssetPath ( ) + " models/tunnel_cylinder.gltf " , vulkanDevice , queue , vkglTF : : FileLoadingFlags : : PreTransformVertices | vkglTF : : FileLoadingFlags : : FlipY ) ;
2016-09-03 22:31:12 +02:00
loadTexture ( getAssetPath ( ) + " textures/metalplate_nomips_rgba.ktx " , VK_FORMAT_R8G8B8A8_UNORM , false ) ;
2016-08-15 15:17:01 +02:00
}
void setupDescriptorPool ( )
{
std : : vector < VkDescriptorPoolSize > poolSizes =
{
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 ) , // Vertex shader UBO
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE , 1 ) , // Sampled image
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_SAMPLER , 3 ) , // 3 samplers (array)
2016-08-15 15:17:01 +02:00
} ;
2020-05-29 16:08:53 +01:00
VkDescriptorPoolCreateInfo descriptorPoolInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolCreateInfo (
2016-08-15 15:17:01 +02:00
static_cast < uint32_t > ( poolSizes . size ( ) ) ,
poolSizes . data ( ) ,
1 ) ;
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
}
void setupDescriptorSetLayout ( )
{
2020-07-28 20:20:38 +02:00
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings = {
// Binding 0: Vertex shader uniform buffer
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , VK_SHADER_STAGE_VERTEX_BIT , 0 ) ,
// Binding 1: Sampled image
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE , VK_SHADER_STAGE_FRAGMENT_BIT , 1 ) ,
// Binding 2: Sampler array (3 descriptors)
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_SAMPLER , VK_SHADER_STAGE_FRAGMENT_BIT , 2 , 3 ) ,
} ;
2016-08-15 15:17:01 +02:00
2020-07-28 20:20:38 +02:00
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks : : initializers : : descriptorSetLayoutCreateInfo ( setLayoutBindings ) ;
2016-08-15 15:17:01 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
2020-07-28 20:20:38 +02:00
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks : : initializers : : pipelineLayoutCreateInfo ( & descriptorSetLayout , 1 ) ;
2016-08-15 15:17:01 +02:00
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
}
void setupDescriptorSet ( )
{
2019-11-19 19:07:03 +01:00
VkDescriptorSetAllocateInfo allocInfo = vks : : initializers : : descriptorSetAllocateInfo ( descriptorPool , & descriptorSetLayout , 1 ) ;
2016-08-15 15:17:01 +02:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
2019-11-19 19:07:03 +01:00
VkDescriptorImageInfo textureDescriptor = vks : : initializers : : descriptorImageInfo ( VK_NULL_HANDLE , texture . view , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) ;
std : : vector < VkWriteDescriptorSet > writeDescriptorSets = {
2016-08-15 15:17:01 +02:00
2019-11-19 19:07:03 +01:00
// Binding 0: Vertex shader uniform buffer
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 0 , & uniformBufferVS . descriptor ) ,
// Binding 1: Sampled image
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE , 1 , & textureDescriptor )
} ;
2016-08-15 15:17:01 +02:00
// Binding 2: Sampler array
std : : vector < VkDescriptorImageInfo > samplerDescriptors ;
for ( auto i = 0 ; i < samplers . size ( ) ; i + + )
{
2017-02-12 11:12:42 +01:00
samplerDescriptors . push_back ( vks : : initializers : : descriptorImageInfo ( samplers [ i ] , VK_NULL_HANDLE , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) ) ;
2016-08-15 15:17:01 +02:00
}
VkWriteDescriptorSet samplerDescriptorWrite { } ;
samplerDescriptorWrite . sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ;
samplerDescriptorWrite . dstSet = descriptorSet ;
samplerDescriptorWrite . descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER ;
samplerDescriptorWrite . descriptorCount = static_cast < uint32_t > ( samplerDescriptors . size ( ) ) ;
samplerDescriptorWrite . pImageInfo = samplerDescriptors . data ( ) ;
samplerDescriptorWrite . dstBinding = 2 ;
samplerDescriptorWrite . dstArrayElement = 0 ;
writeDescriptorSets . push_back ( samplerDescriptorWrite ) ;
vkUpdateDescriptorSets ( device , static_cast < uint32_t > ( writeDescriptorSets . size ( ) ) , writeDescriptorSets . data ( ) , 0 , NULL ) ;
}
void preparePipelines ( )
{
2020-07-28 20:20:38 +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 ) ;
2016-08-15 15:17:01 +02:00
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
2020-05-29 16:08:53 +01:00
shaderStages [ 0 ] = loadShader ( getShadersPath ( ) + " texturemipmapgen/texture.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getShadersPath ( ) + " texturemipmapgen/texture.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2016-08-15 15:17:01 +02:00
2020-07-28 20:20:38 +02:00
VkGraphicsPipelineCreateInfo pipelineCI = vks : : initializers : : pipelineCreateInfo ( pipelineLayout , renderPass , 0 ) ;
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 ( ) ;
pipelineCI . pVertexInputState = vkglTF : : Vertex : : getPipelineVertexInputState ( { vkglTF : : VertexComponent : : Position , vkglTF : : VertexComponent : : UV , vkglTF : : VertexComponent : : Normal } ) ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCI , nullptr , & pipeline ) ) ;
2016-08-15 15:17:01 +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 ( )
{
uboVS . projection = camera . matrices . perspective ;
uboVS . view = camera . matrices . view ;
2017-09-24 18:17:07 +02:00
uboVS . model = glm : : rotate ( glm : : mat4 ( 1.0f ) , glm : : radians ( timer * 360.0f ) , glm : : vec3 ( 1.0f , 0.0f , 0.0f ) ) ;
2016-09-04 14:26:33 +02:00
uboVS . viewPos = glm : : vec4 ( camera . position , 0.0f ) * glm : : vec4 ( - 1.0f ) ;
2016-08-15 15:17:01 +02:00
VK_CHECK_RESULT ( uniformBufferVS . map ( ) ) ;
memcpy ( uniformBufferVS . mapped , & uboVS , sizeof ( uboVS ) ) ;
uniformBufferVS . unmap ( ) ;
}
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
loadAssets ( ) ;
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
2020-07-28 20:20:38 +02:00
if ( ! paused | | camera . updated )
2016-08-15 16:25:54 +02:00
{
updateUniformBuffers ( ) ;
}
2016-08-15 15:17:01 +02:00
}
2017-11-01 14:22:10 +01:00
virtual void OnUpdateUIOverlay ( vks : : UIOverlay * overlay )
2016-08-15 15:17:01 +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 ( ) ;
}
if ( overlay - > comboBox ( " Sampler type " , & uboVS . samplerIndex , samplerNames ) ) {
updateUniformBuffers ( ) ;
}
2016-08-15 15:17:01 +02:00
}
}
} ;
VULKAN_EXAMPLE_MAIN ( )