2016-08-15 15:17:01 +02:00
/*
* Vulkan Example - Runtime mip map generation
*
* Copyright ( C ) 2016 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
// todo: Fallback for sampler selection on devices that don't support shaderSampledImageArrayDynamicIndexing
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <vector>
# include <algorithm>
# 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"
# include "vulkandevice.hpp"
# include "vulkanbuffer.hpp"
# define VERTEX_BUFFER_BIND_ID 0
# define ENABLE_VALIDATION false
std : : vector < vkMeshLoader : : VertexLayout > vertexLayout =
{
vkMeshLoader : : VERTEX_LAYOUT_POSITION ,
vkMeshLoader : : VERTEX_LAYOUT_UV ,
vkMeshLoader : : VERTEX_LAYOUT_NORMAL
} ;
class VulkanExample : public VulkanExampleBase
{
public :
struct Texture {
VkImage image ;
VkImageLayout imageLayout ;
VkDeviceMemory deviceMemory ;
VkImageView view ;
uint32_t width , height ;
uint32_t mipLevels ;
} texture ;
// To demonstrate mip mapping and filtering this example uses separate samplers
std : : vector < std : : string > samplerNames { " No mip maps " , " With mip maps (bilinear) " , " With mip maps (anisotropic) " } ;
std : : vector < VkSampler > samplers ;
struct {
vkMeshLoader : : MeshBuffer tunnel ;
} meshes ;
struct {
VkPipelineVertexInputStateCreateInfo inputState ;
std : : vector < VkVertexInputBindingDescription > bindingDescriptions ;
std : : vector < VkVertexInputAttributeDescription > attributeDescriptions ;
} vertices ;
vk : : Buffer uniformBufferVS ;
struct uboVS {
glm : : mat4 projection ;
glm : : mat4 view ;
2016-08-15 16:25:54 +02:00
glm : : mat4 model ;
2016-08-15 15:17:01 +02:00
float lodBias = 0.0f ;
2016-08-30 20:45:23 +02:00
uint32_t samplerIndex = 2 ;
2016-08-15 15:17:01 +02:00
} uboVS ;
struct {
VkPipeline solid ;
} pipelines ;
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
2016-08-27 14:03:13 +02:00
title = " Vulkan Example - Runtime mip map generation " ;
2016-08-15 15:17:01 +02:00
enableTextOverlay = true ;
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 ;
2016-08-27 14:03:13 +02:00
timerSpeed * = 0.05f ;
2016-08-15 16:25:54 +02:00
paused = true ;
2016-08-15 15:17:01 +02:00
}
~ VulkanExample ( )
{
destroyTextureImage ( texture ) ;
vkDestroyPipeline ( device , pipelines . solid , nullptr ) ;
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
uniformBufferVS . destroy ( ) ;
for ( auto sampler : samplers )
{
vkDestroySampler ( device , sampler , nullptr ) ;
}
vkMeshLoader : : freeMeshBufferResources ( device , & meshes . tunnel ) ;
}
void loadTexture ( std : : string fileName , VkFormat format , bool forceLinearTiling )
{
# if defined(__ANDROID__)
// Textures are stored inside the apk on Android (compressed)
// So they need to be loaded via the asset manager
AAsset * asset = AAssetManager_open ( androidApp - > activity - > assetManager , fileName . c_str ( ) , AASSET_MODE_STREAMING ) ;
assert ( asset ) ;
size_t size = AAsset_getLength ( asset ) ;
assert ( size > 0 ) ;
void * textureData = malloc ( size ) ;
AAsset_read ( asset , textureData , size ) ;
AAsset_close ( asset ) ;
gli : : texture2D tex2D ( gli : : load ( ( const char * ) textureData , size ) ) ;
# else
gli : : texture2D tex2D ( gli : : load ( fileName ) ) ;
# endif
assert ( ! tex2D . empty ( ) ) ;
VkFormatProperties formatProperties ;
texture . width = static_cast < uint32_t > ( tex2D [ 0 ] . dimensions ( ) . x ) ;
texture . height = static_cast < uint32_t > ( tex2D [ 0 ] . dimensions ( ) . y ) ;
// 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 ;
// Get device properites for the requested texture format
vkGetPhysicalDeviceFormatProperties ( physicalDevice , format , & formatProperties ) ;
// todo check blit flags
VkMemoryAllocateInfo memAllocInfo = vkTools : : initializers : : memoryAllocateInfo ( ) ;
VkMemoryRequirements memReqs = { } ;
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer ;
VkDeviceMemory stagingMemory ;
VkBufferCreateInfo bufferCreateInfo = vkTools : : initializers : : bufferCreateInfo ( ) ;
bufferCreateInfo . size = tex2D . size ( ) ;
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
bufferCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
VK_CHECK_RESULT ( vkCreateBuffer ( device , & bufferCreateInfo , nullptr , & stagingBuffer ) ) ;
vkGetBufferMemoryRequirements ( device , stagingBuffer , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
memAllocInfo . memoryTypeIndex = vulkanDevice - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ) ;
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 ) ) ;
memcpy ( data , tex2D . data ( ) , tex2D . size ( ) ) ;
vkUnmapMemory ( device , stagingMemory ) ;
// Create optimal tiled target image
VkImageCreateInfo imageCreateInfo = vkTools : : initializers : : imageCreateInfo ( ) ;
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 . usage = VK_IMAGE_USAGE_SAMPLED_BIT ;
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 ) ) ;
VkCommandBuffer copyCmd = VulkanExampleBase : : createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
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
vkTools : : setImageLayout ( copyCmd , texture . image , VK_IMAGE_ASPECT_COLOR_BIT , VK_IMAGE_LAYOUT_UNDEFINED , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , subresourceRange ) ;
// 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
texture . imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ;
vkTools : : setImageLayout (
copyCmd ,
texture . image ,
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
subresourceRange ) ;
VulkanExampleBase : : flushCommandBuffer ( copyCmd , queue , true ) ;
// Clean up staging resources
vkFreeMemory ( device , stagingMemory , nullptr ) ;
vkDestroyBuffer ( device , stagingBuffer , nullptr ) ;
// Generate the mip chain
// ---------------------------------------------------------------
// We copy down the whole mip chain doint a blit from mip-1 to mip
// An alternative way would be to always blit from the first mip level and sample that one down
// todo: comment
VkCommandBuffer blitCmd = VulkanExampleBase : : createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
// Copy down mips from n-1 to n
for ( int32_t i = 1 ; i < texture . mipLevels ; i + + )
{
int32_t mipWidth = texture . width > > i ;
int32_t mipHeight = texture . height > > i ;
VkImageBlit imageBlit { } ;
// 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 ;
// Transiton current mip level to transfer dest
vkTools : : setImageLayout (
blitCmd ,
texture . image ,
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
mipSubRange ) ;
// 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 ) ;
// Transiton current mip level to transfer source for read in next iteration
vkTools : : setImageLayout (
blitCmd ,
texture . image ,
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
mipSubRange ) ;
}
// After the loop, all mip layers are in TRANSFER_SRC layout, so transition all to SHADER_READ
subresourceRange . levelCount = texture . mipLevels ;
vkTools : : setImageLayout (
blitCmd ,
texture . image ,
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
texture . imageLayout ,
subresourceRange ) ;
VulkanExampleBase : : flushCommandBuffer ( blitCmd , queue , true ) ;
// ---------------------------------------------------------------
// Create samplers
samplers . resize ( 3 ) ;
VkSamplerCreateInfo sampler = vkTools : : initializers : : samplerCreateInfo ( ) ;
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
VkImageViewCreateInfo view = vkTools : : initializers : : imageViewCreateInfo ( ) ;
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 ( )
{
VkCommandBufferBeginInfo cmdBufInfo = vkTools : : initializers : : commandBufferBeginInfo ( ) ;
VkClearValue clearValues [ 2 ] ;
clearValues [ 0 ] . color = defaultClearColor ;
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
VkRenderPassBeginInfo renderPassBeginInfo = vkTools : : initializers : : renderPassBeginInfo ( ) ;
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 ) ;
VkViewport viewport = vkTools : : initializers : : viewport ( ( float ) width , ( float ) height , 0.0f , 1.0f ) ;
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
VkRect2D scissor = vkTools : : initializers : : rect2D ( width , height , 0 , 0 ) ;
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 } ;
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & meshes . tunnel . vertices . buf , offsets ) ;
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , meshes . tunnel . indices . buf , 0 , VK_INDEX_TYPE_UINT32 ) ;
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , meshes . tunnel . indexCount , 1 , 0 , 0 , 0 ) ;
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
}
}
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
// 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 ( ) ;
}
void loadAssets ( )
{
loadMesh ( getAssetPath ( ) + " models/tunnel.dae " , & meshes . tunnel , vertexLayout , 1.0f ) ;
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 setupVertexDescriptions ( )
{
// Binding description
vertices . bindingDescriptions . resize ( 1 ) ;
vertices . bindingDescriptions [ 0 ] =
vkTools : : initializers : : vertexInputBindingDescription (
VERTEX_BUFFER_BIND_ID ,
2016-08-15 16:25:54 +02:00
vkMeshLoader : : vertexSize ( vertexLayout ) ,
2016-08-15 15:17:01 +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 ] =
vkTools : : initializers : : vertexInputAttributeDescription (
VERTEX_BUFFER_BIND_ID ,
0 ,
VK_FORMAT_R32G32B32_SFLOAT ,
2016-08-15 16:25:54 +02:00
0 ) ;
2016-08-15 15:17:01 +02:00
// Location 1 : Texture coordinates
vertices . attributeDescriptions [ 1 ] =
vkTools : : initializers : : vertexInputAttributeDescription (
VERTEX_BUFFER_BIND_ID ,
1 ,
VK_FORMAT_R32G32_SFLOAT ,
2016-08-15 16:25:54 +02:00
3 * sizeof ( float ) ) ;
2016-08-15 15:17:01 +02:00
// Location 1 : Vertex normal
vertices . attributeDescriptions [ 2 ] =
vkTools : : initializers : : vertexInputAttributeDescription (
VERTEX_BUFFER_BIND_ID ,
2 ,
VK_FORMAT_R32G32B32_SFLOAT ,
2016-08-15 16:25:54 +02:00
5 * sizeof ( float ) ) ;
2016-08-15 15:17:01 +02:00
vertices . inputState = vkTools : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
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 ( )
{
std : : vector < VkDescriptorPoolSize > poolSizes =
{
vkTools : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 ) , // Vertex shader UBO
vkTools : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE , 1 ) , // Sampled image
vkTools : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_SAMPLER , 3 ) , // 3 samplers (array)
} ;
VkDescriptorPoolCreateInfo descriptorPoolInfo =
vkTools : : initializers : : descriptorPoolCreateInfo (
static_cast < uint32_t > ( poolSizes . size ( ) ) ,
poolSizes . data ( ) ,
1 ) ;
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
}
void setupDescriptorSetLayout ( )
{
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings ;
// Binding 0: Vertex shader uniform buffer
setLayoutBindings . push_back ( vkTools : : initializers : : descriptorSetLayoutBinding (
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
VK_SHADER_STAGE_VERTEX_BIT ,
0 ) ) ;
// Binding 1: Sampled image
setLayoutBindings . push_back ( vkTools : : initializers : : descriptorSetLayoutBinding (
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ,
VK_SHADER_STAGE_FRAGMENT_BIT ,
1 ) ) ;
// Binding 2: Sampler array (3 descriptors)
setLayoutBindings . push_back ( vkTools : : initializers : : descriptorSetLayoutBinding (
VK_DESCRIPTOR_TYPE_SAMPLER ,
VK_SHADER_STAGE_FRAGMENT_BIT ,
2 ,
3 ) ) ;
VkDescriptorSetLayoutCreateInfo descriptorLayout =
vkTools : : initializers : : descriptorSetLayoutCreateInfo (
setLayoutBindings . data ( ) ,
static_cast < uint32_t > ( setLayoutBindings . size ( ) ) ) ;
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
vkTools : : initializers : : pipelineLayoutCreateInfo (
& descriptorSetLayout ,
1 ) ;
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
}
void setupDescriptorSet ( )
{
VkDescriptorSetAllocateInfo allocInfo =
vkTools : : initializers : : descriptorSetAllocateInfo (
descriptorPool ,
& descriptorSetLayout ,
1 ) ;
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
std : : vector < VkWriteDescriptorSet > writeDescriptorSets ;
// Binding 0: Vertex shader uniform buffer
writeDescriptorSets . push_back ( vkTools : : initializers : : writeDescriptorSet (
descriptorSet ,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
0 ,
& uniformBufferVS . descriptor ) ) ;
// Binding 1: Sampled image
VkDescriptorImageInfo texDescriptor = vkTools : : initializers : : descriptorImageInfo ( VK_NULL_HANDLE , texture . view , VK_IMAGE_LAYOUT_GENERAL ) ;
writeDescriptorSets . push_back ( vkTools : : initializers : : writeDescriptorSet (
descriptorSet ,
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ,
1 ,
& texDescriptor ) ) ;
// Binding 2: Sampler array
std : : vector < VkDescriptorImageInfo > samplerDescriptors ;
for ( auto i = 0 ; i < samplers . size ( ) ; i + + )
{
samplerDescriptors . push_back ( vkTools : : initializers : : descriptorImageInfo ( samplers [ i ] , VK_NULL_HANDLE , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) ) ;
}
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 ( )
{
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
vkTools : : initializers : : pipelineInputAssemblyStateCreateInfo (
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST ,
0 ,
VK_FALSE ) ;
VkPipelineRasterizationStateCreateInfo rasterizationState =
vkTools : : initializers : : pipelineRasterizationStateCreateInfo (
VK_POLYGON_MODE_FILL ,
VK_CULL_MODE_BACK_BIT ,
VK_FRONT_FACE_COUNTER_CLOCKWISE ,
0 ) ;
VkPipelineColorBlendAttachmentState blendAttachmentState =
vkTools : : initializers : : pipelineColorBlendAttachmentState (
0xf ,
VK_FALSE ) ;
VkPipelineColorBlendStateCreateInfo colorBlendState =
vkTools : : initializers : : pipelineColorBlendStateCreateInfo (
1 ,
& blendAttachmentState ) ;
VkPipelineDepthStencilStateCreateInfo depthStencilState =
vkTools : : initializers : : pipelineDepthStencilStateCreateInfo (
VK_TRUE ,
VK_TRUE ,
VK_COMPARE_OP_LESS_OR_EQUAL ) ;
VkPipelineViewportStateCreateInfo viewportState =
vkTools : : initializers : : pipelineViewportStateCreateInfo ( 1 , 1 , 0 ) ;
VkPipelineMultisampleStateCreateInfo multisampleState =
vkTools : : initializers : : pipelineMultisampleStateCreateInfo (
VK_SAMPLE_COUNT_1_BIT ,
0 ) ;
std : : vector < VkDynamicState > dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT ,
VK_DYNAMIC_STATE_SCISSOR
} ;
VkPipelineDynamicStateCreateInfo dynamicState =
vkTools : : initializers : : pipelineDynamicStateCreateInfo (
dynamicStateEnables . data ( ) ,
static_cast < uint32_t > ( dynamicStateEnables . size ( ) ) ,
0 ) ;
// Load shaders
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
shaderStages [ 0 ] = loadShader ( getAssetPath ( ) + " shaders/texturemipmapgen/texture.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getAssetPath ( ) + " shaders/texturemipmapgen/texture.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
vkTools : : initializers : : pipelineCreateInfo (
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 ( )
{
uboVS . projection = camera . matrices . perspective ;
uboVS . view = camera . matrices . view ;
2016-09-03 22:31:12 +02:00
uboVS . model = glm : : rotate ( glm : : mat4 ( ) , glm : : radians ( ( timer + 45.0f / 360.0f ) * 360.0f ) , glm : : vec3 ( 1.0f , 0.0f , 0.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 ( ) ;
setupVertexDescriptions ( ) ;
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
2016-08-15 16:25:54 +02:00
if ( ! paused )
{
updateUniformBuffers ( ) ;
}
2016-08-15 15:17:01 +02:00
}
virtual void viewChanged ( )
{
updateUniformBuffers ( ) ;
}
void changeLodBias ( float delta )
{
uboVS . lodBias + = delta ;
if ( uboVS . lodBias < 0.0f )
{
uboVS . lodBias = 0.0f ;
}
if ( uboVS . lodBias > texture . mipLevels )
{
uboVS . lodBias = ( float ) texture . mipLevels ;
}
updateUniformBuffers ( ) ;
updateTextOverlay ( ) ;
}
void toggleSampler ( )
{
uboVS . samplerIndex = ( uboVS . samplerIndex < static_cast < uint32_t > ( samplers . size ( ) ) - 1 ) ? uboVS . samplerIndex + 1 : 0 ;
updateUniformBuffers ( ) ;
2016-08-27 14:03:13 +02:00
updateTextOverlay ( ) ;
2016-08-15 15:17:01 +02:00
}
virtual void keyPressed ( uint32_t keyCode )
{
switch ( keyCode )
{
case KEY_KPADD :
case GAMEPAD_BUTTON_R1 :
changeLodBias ( 0.1f ) ;
break ;
case KEY_KPSUB :
case GAMEPAD_BUTTON_L1 :
changeLodBias ( - 0.1f ) ;
break ;
case KEY_F :
case GAMEPAD_BUTTON_A :
toggleSampler ( ) ;
break ;
}
}
virtual void getOverlayText ( VulkanTextOverlay * textOverlay )
{
std : : stringstream ss ;
ss < < std : : setprecision ( 2 ) < < std : : fixed < < uboVS . lodBias ;
# if defined(__ANDROID__)
textOverlay - > addText ( " LOD bias: " + ss . str ( ) + " (Buttons L1/R1 to change) " , 5.0f , 85.0f , VulkanTextOverlay : : alignLeft ) ;
textOverlay - > addText ( " Sampler: " + samplerNames [ uboVS . samplerIndex ] + " ( \" Button A \" to toggle) " , 5.0f , 105.0f , VulkanTextOverlay : : alignLeft ) ;
# else
textOverlay - > addText ( " LOD bias: " + ss . str ( ) + " (numpad +/- to change) " , 5.0f , 85.0f , VulkanTextOverlay : : alignLeft ) ;
textOverlay - > addText ( " Sampler: " + samplerNames [ uboVS . samplerIndex ] + " ( \" f \" to toggle) " , 5.0f , 105.0f , VulkanTextOverlay : : alignLeft ) ;
# endif
}
} ;
VULKAN_EXAMPLE_MAIN ( )