2016-02-16 15:07:25 +01:00
/*
* Vulkan Example - Texture loading ( and display ) example ( including mip maps )
*
* Copyright ( C ) 2016 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <vector>
# define GLM_FORCE_RADIANS
2016-03-08 21:52:40 +01:00
# define GLM_FORCE_DEPTH_ZERO_TO_ONE
2016-02-16 15:07:25 +01:00
# include <glm/glm.hpp>
# include <glm/gtc/matrix_transform.hpp>
# include <vulkan/vulkan.h>
# include "vulkanexamplebase.h"
2016-07-24 21:19:28 +02:00
# include "vulkandevice.hpp"
# include "vulkanbuffer.hpp"
2016-02-16 15:07:25 +01:00
# define VERTEX_BUFFER_BIND_ID 0
# define ENABLE_VALIDATION false
// Vertex layout for this example
struct Vertex {
float pos [ 3 ] ;
float uv [ 2 ] ;
2016-05-14 15:54:20 +02:00
float normal [ 3 ] ;
2016-02-16 15:07:25 +01:00
} ;
class VulkanExample : public VulkanExampleBase
{
public :
// Contains all Vulkan objects that are required to store and use a texture
// Note that this repository contains a texture loader (vulkantextureloader.h)
// that encapsulates texture loading functionality in a class that is used
// in subsequent demos
struct Texture {
VkSampler sampler ;
VkImage image ;
VkImageLayout imageLayout ;
VkDeviceMemory deviceMemory ;
VkImageView view ;
uint32_t width , height ;
uint32_t mipLevels ;
} texture ;
struct {
VkPipelineVertexInputStateCreateInfo inputState ;
std : : vector < VkVertexInputBindingDescription > bindingDescriptions ;
std : : vector < VkVertexInputAttributeDescription > attributeDescriptions ;
} vertices ;
2016-07-24 21:19:28 +02:00
vk : : Buffer vertexBuffer ;
vk : : Buffer indexBuffer ;
uint32_t indexCount ;
2016-02-16 15:07:25 +01:00
2016-07-24 21:19:28 +02:00
vk : : Buffer uniformBufferVS ;
2016-02-16 15:07:25 +01:00
struct {
glm : : mat4 projection ;
glm : : mat4 model ;
2016-05-14 15:54:20 +02:00
glm : : vec4 viewPos ;
2016-02-16 15:07:25 +01:00
float lodBias = 0.0f ;
} uboVS ;
struct {
VkPipeline solid ;
} pipelines ;
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
zoom = - 2.5f ;
2016-05-14 15:54:20 +02:00
rotation = { 0.0f , 15.0f , 0.0f } ;
2016-02-16 15:07:25 +01:00
title = " Vulkan Example - Texturing " ;
2016-05-15 20:11:28 +02:00
enableTextOverlay = true ;
2016-02-16 15:07:25 +01:00
}
~ VulkanExample ( )
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
2016-07-24 21:19:28 +02:00
destroyTextureImage ( texture ) ;
2016-02-16 15:07:25 +01:00
vkDestroyPipeline ( device , pipelines . solid , nullptr ) ;
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
2016-07-24 21:19:28 +02:00
vertexBuffer . destroy ( ) ;
indexBuffer . destroy ( ) ;
uniformBufferVS . destroy ( ) ;
2016-02-16 15:07:25 +01:00
}
// Create an image memory barrier for changing the layout of
// an image and put it into an active command buffer
2016-06-21 20:36:54 +02:00
void setImageLayout ( VkCommandBuffer cmdBuffer , VkImage image , VkImageAspectFlags aspectMask , VkImageLayout oldImageLayout , VkImageLayout newImageLayout , VkImageSubresourceRange subresourceRange )
2016-02-16 15:07:25 +01:00
{
// Create an image barrier object
VkImageMemoryBarrier imageMemoryBarrier = vkTools : : initializers : : imageMemoryBarrier ( ) ; ;
imageMemoryBarrier . oldLayout = oldImageLayout ;
imageMemoryBarrier . newLayout = newImageLayout ;
imageMemoryBarrier . image = image ;
2016-06-21 20:36:54 +02:00
imageMemoryBarrier . subresourceRange = subresourceRange ;
2016-02-16 15:07:25 +01:00
// Only sets masks for layouts used in this example
2016-06-21 20:36:54 +02:00
// For a more complete version that can be used with other layouts see vkTools::setImageLayout
2016-02-16 15:07:25 +01:00
2016-06-21 20:36:54 +02:00
// Source layouts (old)
switch ( oldImageLayout )
2016-03-13 13:03:04 +01:00
{
2016-06-21 20:36:54 +02:00
case VK_IMAGE_LAYOUT_UNDEFINED :
// Only valid as initial layout, memory contents are not preserved
// Can be accessed directly, no source dependency required
imageMemoryBarrier . srcAccessMask = 0 ;
break ;
case VK_IMAGE_LAYOUT_PREINITIALIZED :
// Only valid as initial layout for linear images, preserves memory contents
// Make sure host writes to the image have been finished
imageMemoryBarrier . srcAccessMask = VK_ACCESS_HOST_WRITE_BIT ;
break ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
// Old layout is transfer destination
// Make sure any writes to the image have been finished
imageMemoryBarrier . srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
break ;
2016-03-13 13:03:04 +01:00
}
2016-06-21 20:36:54 +02:00
2016-02-16 15:07:25 +01:00
// Target layouts (new)
2016-06-21 20:36:54 +02:00
switch ( newImageLayout )
2016-02-16 15:07:25 +01:00
{
2016-06-21 20:36:54 +02:00
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL :
// Transfer source (copy, blit)
// Make sure any reads from the image have been finished
imageMemoryBarrier . dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT ;
break ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
// Transfer destination (copy, blit)
// Make sure any writes to the image have been finished
imageMemoryBarrier . dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
break ;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
// Shader read (sampler, input attachment)
2016-02-16 15:07:25 +01:00
imageMemoryBarrier . dstAccessMask = VK_ACCESS_SHADER_READ_BIT ;
2016-06-21 20:36:54 +02:00
break ;
2016-02-16 15:07:25 +01:00
}
2016-06-21 20:36:54 +02:00
// Put barrier on top of pipeline
2016-02-16 15:07:25 +01:00
VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT ;
VkPipelineStageFlags destStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT ;
// Put barrier inside setup command buffer
vkCmdPipelineBarrier (
2016-04-29 21:49:37 +02:00
cmdBuffer ,
2016-02-16 15:07:25 +01:00
srcStageFlags ,
destStageFlags ,
VK_FLAGS_NONE ,
0 , nullptr ,
0 , nullptr ,
1 , & imageMemoryBarrier ) ;
}
2016-03-21 22:41:27 +01:00
void loadTexture ( std : : string fileName , VkFormat format , bool forceLinearTiling )
2016-02-16 15:07:25 +01:00
{
2016-03-21 22:41:27 +01:00
# 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 ) ;
2017-01-21 21:31:26 +01:00
gli : : texture2d tex2D ( gli : : load ( ( const char * ) textureData , size ) ) ;
2016-03-21 22:41:27 +01:00
# else
2017-01-21 21:31:26 +01:00
gli : : texture2d tex2D ( gli : : load ( fileName ) ) ;
2016-03-21 22:41:27 +01:00
# endif
2016-02-16 15:07:25 +01:00
assert ( ! tex2D . empty ( ) ) ;
2016-03-21 22:41:27 +01:00
VkFormatProperties formatProperties ;
2017-01-21 21:31:26 +01:00
texture . width = static_cast < uint32_t > ( tex2D [ 0 ] . extent ( ) . x ) ;
texture . height = static_cast < uint32_t > ( tex2D [ 0 ] . extent ( ) . y ) ;
2016-07-24 21:19:28 +02:00
texture . mipLevels = static_cast < uint32_t > ( tex2D . levels ( ) ) ;
2016-02-16 15:07:25 +01:00
// Get device properites for the requested texture format
vkGetPhysicalDeviceFormatProperties ( physicalDevice , format , & formatProperties ) ;
// Only use linear tiling if requested (and supported by the device)
// Support for linear tiling is mostly limited, so prefer to use
// optimal tiling instead
// On most implementations linear tiling will only support a very
// limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
VkBool32 useStaging = true ;
// Only use linear tiling if forced
if ( forceLinearTiling )
{
// Don't use linear if format is not supported for (linear) shader sampling
useStaging = ! ( formatProperties . linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT ) ;
}
VkMemoryAllocateInfo memAllocInfo = vkTools : : initializers : : memoryAllocateInfo ( ) ;
2016-04-29 21:49:37 +02:00
VkMemoryRequirements memReqs = { } ;
2016-02-16 15:07:25 +01:00
if ( useStaging )
{
2016-04-29 21:49:37 +02:00
// 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 ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & bufferCreateInfo , nullptr , & stagingBuffer ) ) ;
2016-04-29 21:49:37 +02:00
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements ( device , stagingBuffer , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
// Get memory type index for a host visible buffer
2016-07-23 20:42:03 +02:00
memAllocInfo . memoryTypeIndex = vulkanDevice - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ) ;
2016-04-29 21:49:37 +02:00
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAllocInfo , nullptr , & stagingMemory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , stagingBuffer , stagingMemory , 0 ) ) ;
2016-04-29 21:49:37 +02:00
// Copy texture data into staging buffer
uint8_t * data ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkMapMemory ( device , stagingMemory , 0 , memReqs . size , 0 , ( void * * ) & data ) ) ;
2016-04-29 21:49:37 +02:00
memcpy ( data , tex2D . data ( ) , tex2D . size ( ) ) ;
vkUnmapMemory ( device , stagingMemory ) ;
// Setup buffer copy regions for each mip level
std : : vector < VkBufferImageCopy > bufferCopyRegions ;
uint32_t offset = 0 ;
for ( uint32_t i = 0 ; i < texture . mipLevels ; i + + )
2016-02-16 15:07:25 +01:00
{
2016-04-29 21:49:37 +02:00
VkBufferImageCopy bufferCopyRegion = { } ;
bufferCopyRegion . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
bufferCopyRegion . imageSubresource . mipLevel = i ;
bufferCopyRegion . imageSubresource . baseArrayLayer = 0 ;
bufferCopyRegion . imageSubresource . layerCount = 1 ;
2017-01-21 21:31:26 +01:00
bufferCopyRegion . imageExtent . width = static_cast < uint32_t > ( tex2D [ i ] . extent ( ) . x ) ;
bufferCopyRegion . imageExtent . height = static_cast < uint32_t > ( tex2D [ i ] . extent ( ) . y ) ;
2016-04-29 21:49:37 +02:00
bufferCopyRegion . imageExtent . depth = 1 ;
bufferCopyRegion . bufferOffset = offset ;
bufferCopyRegions . push_back ( bufferCopyRegion ) ;
2016-07-24 21:19:28 +02:00
offset + = static_cast < uint32_t > ( tex2D [ i ] . size ( ) ) ;
2016-02-16 15:07:25 +01:00
}
2016-04-29 21:49:37 +02:00
// Create optimal tiled target image
VkImageCreateInfo imageCreateInfo = vkTools : : initializers : : imageCreateInfo ( ) ;
imageCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
imageCreateInfo . format = format ;
2016-02-16 15:07:25 +01:00
imageCreateInfo . mipLevels = texture . mipLevels ;
2016-04-29 21:49:37 +02:00
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 ;
2016-06-21 20:36:54 +02:00
// Set initial layout of the image to undefined
imageCreateInfo . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
2016-02-16 15:07:25 +01:00
imageCreateInfo . extent = { texture . width , texture . height , 1 } ;
2016-04-29 21:49:37 +02:00
imageCreateInfo . usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ;
2016-02-16 15:07:25 +01:00
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateImage ( device , & imageCreateInfo , nullptr , & texture . image ) ) ;
2016-02-16 15:07:25 +01:00
vkGetImageMemoryRequirements ( device , texture . image , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
2016-07-23 20:42:03 +02:00
memAllocInfo . memoryTypeIndex = vulkanDevice - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
2016-02-16 15:07:25 +01:00
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAllocInfo , nullptr , & texture . deviceMemory ) ) ;
VK_CHECK_RESULT ( vkBindImageMemory ( device , texture . image , texture . deviceMemory , 0 ) ) ;
2016-02-16 15:07:25 +01:00
2016-04-29 21:49:37 +02:00
VkCommandBuffer copyCmd = VulkanExampleBase : : createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
2016-06-21 20:36:54 +02:00
// Image barrier for optimal image
// The sub resource range describes the regions of the image we will be transition
VkImageSubresourceRange subresourceRange = { } ;
// Image only contains color data
subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
// Start at first mip level
subresourceRange . baseMipLevel = 0 ;
// We will transition on all mip levels
subresourceRange . levelCount = texture . mipLevels ;
// The 2D texture only has one layer
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
2016-02-16 15:07:25 +01:00
setImageLayout (
2016-04-29 21:49:37 +02:00
copyCmd ,
2016-02-16 15:07:25 +01:00
texture . image ,
VK_IMAGE_ASPECT_COLOR_BIT ,
2016-06-21 20:36:54 +02:00
VK_IMAGE_LAYOUT_UNDEFINED ,
2016-03-13 13:03:04 +01:00
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
2016-06-21 20:36:54 +02:00
subresourceRange ) ;
2016-02-16 15:07:25 +01:00
2016-04-29 21:49:37 +02:00
// Copy mip levels from staging buffer
vkCmdCopyBufferToImage (
copyCmd ,
stagingBuffer ,
texture . image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
2016-07-24 21:19:28 +02:00
static_cast < uint32_t > ( bufferCopyRegions . size ( ) ) ,
2016-06-21 20:36:54 +02:00
bufferCopyRegions . data ( ) ) ;
2016-02-16 15:07:25 +01:00
2016-03-13 13:03:04 +01:00
// Change texture image layout to shader read after all mip levels have been copied
texture . imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ;
setImageLayout (
2016-04-29 21:49:37 +02:00
copyCmd ,
2016-03-13 13:03:04 +01:00
texture . image ,
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
texture . imageLayout ,
2016-06-21 20:36:54 +02:00
subresourceRange ) ;
2016-03-13 13:03:04 +01:00
2016-04-29 21:49:37 +02:00
VulkanExampleBase : : flushCommandBuffer ( copyCmd , queue , true ) ;
2016-02-16 15:07:25 +01:00
2016-04-29 21:49:37 +02:00
// Clean up staging resources
vkFreeMemory ( device , stagingMemory , nullptr ) ;
vkDestroyBuffer ( device , stagingBuffer , nullptr ) ;
2016-02-16 15:07:25 +01:00
}
else
{
// Prefer using optimal tiling, as linear tiling
// may support only a small set of features
// depending on implementation (e.g. no mip maps, only one layer, etc.)
VkImage mappableImage ;
VkDeviceMemory mappableMemory ;
// Load mip map level 0 to linear tiling image
2016-04-29 21:49:37 +02:00
VkImageCreateInfo imageCreateInfo = vkTools : : initializers : : imageCreateInfo ( ) ;
imageCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
imageCreateInfo . format = format ;
imageCreateInfo . mipLevels = 1 ;
imageCreateInfo . arrayLayers = 1 ;
imageCreateInfo . samples = VK_SAMPLE_COUNT_1_BIT ;
imageCreateInfo . tiling = VK_IMAGE_TILING_LINEAR ;
imageCreateInfo . usage = VK_IMAGE_USAGE_SAMPLED_BIT ;
imageCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
imageCreateInfo . initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED ;
imageCreateInfo . extent = { texture . width , texture . height , 1 } ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateImage ( device , & imageCreateInfo , nullptr , & mappableImage ) ) ;
2016-02-16 15:07:25 +01:00
// Get memory requirements for this image
// like size and alignment
vkGetImageMemoryRequirements ( device , mappableImage , & memReqs ) ;
// Set memory allocation size to required memory size
memAllocInfo . allocationSize = memReqs . size ;
// Get memory type that can be mapped to host memory
2016-07-23 20:42:03 +02:00
memAllocInfo . memoryTypeIndex = vulkanDevice - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ) ;
2016-02-16 15:07:25 +01:00
// Allocate host memory
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAllocInfo , nullptr , & mappableMemory ) ) ;
2016-02-16 15:07:25 +01:00
// Bind allocated image for use
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkBindImageMemory ( device , mappableImage , mappableMemory , 0 ) ) ;
2016-02-16 15:07:25 +01:00
// Get sub resource layout
// Mip map count, array layer, etc.
VkImageSubresource subRes = { } ;
subRes . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
VkSubresourceLayout subResLayout ;
void * data ;
// Get sub resources layout
// Includes row pitch, size offsets, etc.
vkGetImageSubresourceLayout ( device , mappableImage , & subRes , & subResLayout ) ;
// Map image memory
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkMapMemory ( device , mappableMemory , 0 , memReqs . size , 0 , & data ) ) ;
2016-02-16 15:07:25 +01:00
// Copy image data into memory
memcpy ( data , tex2D [ subRes . mipLevel ] . data ( ) , tex2D [ subRes . mipLevel ] . size ( ) ) ;
vkUnmapMemory ( device , mappableMemory ) ;
// Linear tiled images don't need to be staged
// and can be directly used as textures
texture . image = mappableImage ;
texture . deviceMemory = mappableMemory ;
texture . imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ;
2016-04-29 21:49:37 +02:00
VkCommandBuffer copyCmd = VulkanExampleBase : : createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
// Setup image memory barrier transfer image to shader read layout
2016-06-21 20:36:54 +02:00
// The sub resource range describes the regions of the image we will be transition
VkImageSubresourceRange subresourceRange = { } ;
// Image only contains color data
subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
// Start at first mip level
subresourceRange . baseMipLevel = 0 ;
// Only one mip level, most implementations won't support more for linear tiled images
subresourceRange . levelCount = 1 ;
// The 2D texture only has one layer
subresourceRange . layerCount = 1 ;
2016-03-13 13:03:04 +01:00
setImageLayout (
2016-04-29 21:49:37 +02:00
copyCmd ,
texture . image ,
2016-03-13 13:03:04 +01:00
VK_IMAGE_ASPECT_COLOR_BIT ,
2016-04-29 21:49:37 +02:00
VK_IMAGE_LAYOUT_PREINITIALIZED ,
2016-03-13 13:03:04 +01:00
texture . imageLayout ,
2016-06-21 20:36:54 +02:00
subresourceRange ) ;
2016-04-29 21:49:37 +02:00
VulkanExampleBase : : flushCommandBuffer ( copyCmd , queue , true ) ;
2016-02-16 15:07:25 +01:00
}
// Create sampler
// In Vulkan textures are accessed by samplers
// This separates all the sampling information from the
// texture data
// This means you could have multiple sampler objects
// for the same texture with different settings
// Similar to the samplers available with OpenGL 3.3
VkSamplerCreateInfo sampler = vkTools : : initializers : : samplerCreateInfo ( ) ;
sampler . magFilter = VK_FILTER_LINEAR ;
sampler . minFilter = VK_FILTER_LINEAR ;
sampler . mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR ;
sampler . addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
sampler . addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
sampler . addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
sampler . mipLodBias = 0.0f ;
sampler . compareOp = VK_COMPARE_OP_NEVER ;
sampler . minLod = 0.0f ;
2016-07-24 21:19:28 +02:00
// Set max level-of-detail to mip level count of the texture
2016-02-16 15:07:25 +01:00
sampler . maxLod = ( useStaging ) ? ( float ) texture . mipLevels : 0.0f ;
// Enable anisotropic filtering
2016-07-24 21:19:28 +02:00
// This feature is optional, so we must check if it's supported on the device
if ( vulkanDevice - > features . samplerAnisotropy )
{
// Use max. level of anisotropy for this example
sampler . maxAnisotropy = vulkanDevice - > properties . limits . maxSamplerAnisotropy ;
sampler . anisotropyEnable = VK_TRUE ;
}
else
{
// The device does not support anisotropic filtering
sampler . maxAnisotropy = 1.0 ;
sampler . anisotropyEnable = VK_FALSE ;
}
2016-02-16 15:07:25 +01:00
sampler . borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateSampler ( device , & sampler , nullptr , & texture . sampler ) ) ;
2016-02-16 15:07:25 +01:00
// Create image view
// Textures are not directly accessed by the shaders and
// are abstracted by image views containing additional
// information and sub resource ranges
VkImageViewCreateInfo view = vkTools : : initializers : : imageViewCreateInfo ( ) ;
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 } ;
2016-07-24 21:19:28 +02:00
// The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view
// It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image
2016-02-16 15:07:25 +01:00
view . subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
view . subresourceRange . baseMipLevel = 0 ;
view . subresourceRange . baseArrayLayer = 0 ;
view . subresourceRange . layerCount = 1 ;
// Linear tiling usually won't support mip maps
// Only set mip map count if optimal tiling is used
view . subresourceRange . levelCount = ( useStaging ) ? texture . mipLevels : 1 ;
2017-01-04 21:49:26 +01:00
// The view will be based on the texture's image
2016-02-16 15:07:25 +01:00
view . image = texture . image ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateImageView ( device , & view , nullptr , & texture . view ) ) ;
2016-02-16 15:07:25 +01:00
}
2016-07-24 21:19:28 +02:00
// Free all Vulkan resources used a texture object
2016-02-16 15:07:25 +01:00
void destroyTextureImage ( Texture texture )
{
2016-07-24 21:19:28 +02:00
vkDestroyImageView ( device , texture . view , nullptr ) ;
2016-02-16 15:07:25 +01:00
vkDestroyImage ( device , texture . image , nullptr ) ;
2016-07-24 21:19:28 +02:00
vkDestroySampler ( device , texture . sampler , nullptr ) ;
2016-02-16 15:07:25 +01:00
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 ] ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
2016-02-16 15:07:25 +01:00
vkCmdBeginRenderPass ( drawCmdBuffers [ i ] , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
2016-05-14 13:50:10 +02:00
VkViewport viewport = vkTools : : initializers : : viewport ( ( float ) width , ( float ) height , 0.0f , 1.0f ) ;
2016-02-16 15:07:25 +01:00
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
2016-05-14 13:50:10 +02:00
VkRect2D scissor = vkTools : : initializers : : rect2D ( width , height , 0 , 0 ) ;
2016-02-16 15:07:25 +01: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-07-24 21:19:28 +02:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & vertexBuffer . buffer , offsets ) ;
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , indexBuffer . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
2016-02-16 15:07:25 +01:00
2016-07-24 21:19:28 +02:00
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , indexCount , 1 , 0 , 0 , 0 ) ;
2016-02-16 15:07:25 +01:00
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
2016-02-16 15:07:25 +01:00
}
}
void draw ( )
{
2016-05-15 20:11:28 +02:00
VulkanExampleBase : : prepareFrame ( ) ;
2016-03-06 20:15:05 +01:00
// Command buffer to be sumitted to the queue
2016-02-16 15:07:25 +01:00
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
2016-03-06 20:15:05 +01:00
// Submit to queue
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
2016-02-16 15:07:25 +01:00
2016-05-15 20:11:28 +02:00
VulkanExampleBase : : submitFrame ( ) ;
2016-02-16 15:07:25 +01:00
}
void generateQuad ( )
{
2016-07-24 21:19:28 +02:00
// Setup vertices for a single uv-mapped quad made from two triangles
std : : vector < Vertex > vertices =
2016-02-16 15:07:25 +01:00
{
2016-07-24 21:19:28 +02:00
{ { 1.0f , 1.0f , 0.0f } , { 1.0f , 1.0f } , { 0.0f , 0.0f , 1.0f } } ,
{ { - 1.0f , 1.0f , 0.0f } , { 0.0f , 1.0f } , { 0.0f , 0.0f , 1.0f } } ,
{ { - 1.0f , - 1.0f , 0.0f } , { 0.0f , 0.0f } , { 0.0f , 0.0f , 1.0f } } ,
{ { 1.0f , - 1.0f , 0.0f } , { 1.0f , 0.0f } , { 0.0f , 0.0f , 1.0f } }
2016-02-16 15:07:25 +01:00
} ;
// Setup indices
2016-07-24 21:19:28 +02:00
std : : vector < uint32_t > indices = { 0 , 1 , 2 , 2 , 3 , 0 } ;
indexCount = static_cast < uint32_t > ( indices . size ( ) ) ;
2016-02-16 15:07:25 +01:00
2016-07-24 21:19:28 +02:00
// Create buffers
// For the sake of simplicity we won't stage the vertex data to the gpu memory
// Vertex buffer
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& vertexBuffer ,
vertices . size ( ) * sizeof ( Vertex ) ,
vertices . data ( ) ) ) ;
// Index buffer
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
2016-02-16 15:07:25 +01:00
VK_BUFFER_USAGE_INDEX_BUFFER_BIT ,
2016-07-24 21:19:28 +02:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& indexBuffer ,
indices . size ( ) * sizeof ( uint32_t ) ,
indices . data ( ) ) ) ;
2016-02-16 15:07:25 +01:00
}
void setupVertexDescriptions ( )
{
// Binding description
vertices . bindingDescriptions . resize ( 1 ) ;
vertices . bindingDescriptions [ 0 ] =
vkTools : : initializers : : vertexInputBindingDescription (
VERTEX_BUFFER_BIND_ID ,
sizeof ( Vertex ) ,
VK_VERTEX_INPUT_RATE_VERTEX ) ;
// Attribute descriptions
// Describes memory layout and shader positions
2016-05-14 15:54:20 +02:00
vertices . attributeDescriptions . resize ( 3 ) ;
2016-02-16 15:07:25 +01:00
// Location 0 : Position
vertices . attributeDescriptions [ 0 ] =
vkTools : : initializers : : vertexInputAttributeDescription (
VERTEX_BUFFER_BIND_ID ,
0 ,
VK_FORMAT_R32G32B32_SFLOAT ,
2016-07-24 21:19:28 +02:00
offsetof ( Vertex , pos ) ) ;
2016-02-16 15:07:25 +01:00
// Location 1 : Texture coordinates
vertices . attributeDescriptions [ 1 ] =
vkTools : : initializers : : vertexInputAttributeDescription (
VERTEX_BUFFER_BIND_ID ,
1 ,
VK_FORMAT_R32G32_SFLOAT ,
2016-07-24 21:19:28 +02:00
offsetof ( Vertex , uv ) ) ;
2016-05-14 15:54:20 +02:00
// Location 1 : Vertex normal
vertices . attributeDescriptions [ 2 ] =
vkTools : : initializers : : vertexInputAttributeDescription (
VERTEX_BUFFER_BIND_ID ,
2 ,
VK_FORMAT_R32G32B32_SFLOAT ,
2016-07-24 21:19:28 +02:00
offsetof ( Vertex , normal ) ) ;
2016-02-16 15:07:25 +01:00
vertices . inputState = vkTools : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
2016-07-24 21:19:28 +02:00
vertices . inputState . vertexBindingDescriptionCount = static_cast < uint32_t > ( vertices . bindingDescriptions . size ( ) ) ;
2016-02-16 15:07:25 +01:00
vertices . inputState . pVertexBindingDescriptions = vertices . bindingDescriptions . data ( ) ;
2016-07-24 21:19:28 +02:00
vertices . inputState . vertexAttributeDescriptionCount = static_cast < uint32_t > ( vertices . attributeDescriptions . size ( ) ) ;
2016-02-16 15:07:25 +01:00
vertices . inputState . pVertexAttributeDescriptions = vertices . attributeDescriptions . data ( ) ;
}
void setupDescriptorPool ( )
{
// Example uses one ubo and one image sampler
std : : vector < VkDescriptorPoolSize > poolSizes =
{
vkTools : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 ) ,
vkTools : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 1 )
} ;
VkDescriptorPoolCreateInfo descriptorPoolInfo =
vkTools : : initializers : : descriptorPoolCreateInfo (
2016-07-24 21:19:28 +02:00
static_cast < uint32_t > ( poolSizes . size ( ) ) ,
2016-02-16 15:07:25 +01:00
poolSizes . data ( ) ,
2 ) ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSetLayout ( )
{
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings =
{
// Binding 0 : Vertex shader uniform buffer
vkTools : : initializers : : descriptorSetLayoutBinding (
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
VK_SHADER_STAGE_VERTEX_BIT ,
0 ) ,
// Binding 1 : Fragment shader image sampler
vkTools : : initializers : : descriptorSetLayoutBinding (
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
VK_SHADER_STAGE_FRAGMENT_BIT ,
1 )
} ;
VkDescriptorSetLayoutCreateInfo descriptorLayout =
vkTools : : initializers : : descriptorSetLayoutCreateInfo (
setLayoutBindings . data ( ) ,
2016-07-24 21:19:28 +02:00
static_cast < uint32_t > ( setLayoutBindings . size ( ) ) ) ;
2016-02-16 15:07:25 +01:00
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
2016-02-16 15:07:25 +01:00
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
vkTools : : initializers : : pipelineLayoutCreateInfo (
& descriptorSetLayout ,
1 ) ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSet ( )
{
VkDescriptorSetAllocateInfo allocInfo =
vkTools : : initializers : : descriptorSetAllocateInfo (
descriptorPool ,
& descriptorSetLayout ,
1 ) ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
2016-02-16 15:07:25 +01:00
2017-01-04 21:49:26 +01:00
// Setup a descriptor image info for the current texture to be used as a combined image sampler
VkDescriptorImageInfo textureDescriptor ;
textureDescriptor . imageView = texture . view ; // The image's view (images are never directly accessed by the shader, but rather through views defining subresources)
textureDescriptor . sampler = texture . sampler ; // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.)
textureDescriptor . imageLayout = texture . imageLayout ; // The current layout of the image (Note: Should always fit the actual use, e.g. shader read)
2016-02-16 15:07:25 +01:00
std : : vector < VkWriteDescriptorSet > writeDescriptorSets =
{
// Binding 0 : Vertex shader uniform buffer
vkTools : : initializers : : writeDescriptorSet (
descriptorSet ,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
0 ,
2016-07-24 21:19:28 +02:00
& uniformBufferVS . descriptor ) ,
2016-02-16 15:07:25 +01:00
// Binding 1 : Fragment shader texture sampler
2017-01-04 21:49:26 +01:00
// Fragment shader: layout (binding = 1) uniform sampler2D samplerColor;
2016-02-16 15:07:25 +01:00
vkTools : : initializers : : writeDescriptorSet (
2017-01-04 21:49:26 +01:00
descriptorSet ,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , // The descriptor set will use a combined image sampler (sampler and image could be split)
1 , // Shader binding point 1
& textureDescriptor ) // Pointer to the descriptor image for our texture
2016-02-16 15:07:25 +01:00
} ;
2016-07-24 21:19:28 +02:00
vkUpdateDescriptorSets ( device , static_cast < uint32_t > ( writeDescriptorSets . size ( ) ) , writeDescriptorSets . data ( ) , 0 , NULL ) ;
2016-02-16 15:07:25 +01:00
}
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_NONE ,
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 ( ) ,
2016-07-24 21:19:28 +02:00
static_cast < uint32_t > ( dynamicStateEnables . size ( ) ) ,
2016-02-16 15:07:25 +01:00
0 ) ;
// Load shaders
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
2016-05-14 15:54:20 +02:00
shaderStages [ 0 ] = loadShader ( getAssetPath ( ) + " shaders/texture/texture.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getAssetPath ( ) + " shaders/texture/texture.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2016-02-16 15:07:25 +01:00
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 ;
2016-07-24 21:19:28 +02:00
pipelineCreateInfo . stageCount = static_cast < uint32_t > ( shaderStages . size ( ) ) ;
2016-02-16 15:07:25 +01:00
pipelineCreateInfo . pStages = shaderStages . data ( ) ;
2016-05-14 13:50:10 +02:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipelines . solid ) ) ;
2016-02-16 15:07:25 +01:00
}
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers ( )
{
// Vertex shader uniform buffer block
2016-07-24 21:19:28 +02:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
2016-02-16 15:07:25 +01:00
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
2016-07-24 21:19:28 +02:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& uniformBufferVS ,
2016-02-16 15:07:25 +01:00
sizeof ( uboVS ) ,
2016-07-24 21:19:28 +02:00
& uboVS ) ) ;
2016-02-16 15:07:25 +01:00
updateUniformBuffers ( ) ;
}
void updateUniformBuffers ( )
{
// Vertex shader
2016-03-08 20:59:25 +01:00
uboVS . projection = glm : : perspective ( glm : : radians ( 60.0f ) , ( float ) width / ( float ) height , 0.001f , 256.0f ) ;
2016-05-14 15:54:20 +02:00
glm : : mat4 viewMatrix = glm : : translate ( glm : : mat4 ( ) , glm : : vec3 ( 0.0f , 0.0f , zoom ) ) ;
2016-02-16 15:07:25 +01:00
2016-05-14 15:54:20 +02:00
uboVS . model = viewMatrix * glm : : translate ( glm : : mat4 ( ) , cameraPos ) ;
2016-03-08 20:59:25 +01:00
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . x ) , glm : : vec3 ( 1.0f , 0.0f , 0.0f ) ) ;
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . y ) , glm : : vec3 ( 0.0f , 1.0f , 0.0f ) ) ;
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . z ) , glm : : vec3 ( 0.0f , 0.0f , 1.0f ) ) ;
2016-02-16 15:07:25 +01:00
2016-05-14 15:54:20 +02:00
uboVS . viewPos = glm : : vec4 ( 0.0f , 0.0f , - zoom , 0.0f ) ;
2016-07-24 21:19:28 +02:00
VK_CHECK_RESULT ( uniformBufferVS . map ( ) ) ;
memcpy ( uniformBufferVS . mapped , & uboVS , sizeof ( uboVS ) ) ;
uniformBufferVS . unmap ( ) ;
2016-02-16 15:07:25 +01:00
}
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
generateQuad ( ) ;
setupVertexDescriptions ( ) ;
prepareUniformBuffers ( ) ;
loadTexture (
2016-05-14 15:54:20 +02:00
getAssetPath ( ) + " textures/pattern_02_bc2.ktx " ,
VK_FORMAT_BC2_UNORM_BLOCK ,
2016-02-16 15:07:25 +01:00
false ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
}
virtual void viewChanged ( )
{
updateUniformBuffers ( ) ;
}
void changeLodBias ( float delta )
{
uboVS . lodBias + = delta ;
if ( uboVS . lodBias < 0.0f )
{
uboVS . lodBias = 0.0f ;
}
2016-06-04 12:03:54 +02:00
if ( uboVS . lodBias > texture . mipLevels )
2016-02-16 15:07:25 +01:00
{
2016-07-24 21:19:28 +02:00
uboVS . lodBias = ( float ) texture . mipLevels ;
2016-02-16 15:07:25 +01:00
}
updateUniformBuffers ( ) ;
2016-06-04 12:03:54 +02:00
updateTextOverlay ( ) ;
}
virtual void keyPressed ( uint32_t keyCode )
{
switch ( keyCode )
{
2016-08-11 13:15:49 +02:00
case KEY_KPADD :
2016-06-04 12:03:54 +02:00
case GAMEPAD_BUTTON_R1 :
changeLodBias ( 0.1f ) ;
break ;
2016-08-11 13:15:49 +02:00
case KEY_KPSUB :
2016-06-04 12:03:54 +02:00
case GAMEPAD_BUTTON_L1 :
changeLodBias ( - 0.1f ) ;
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 ) ;
# else
textOverlay - > addText ( " LOD bias: " + ss . str ( ) + " (numpad +/- to change) " , 5.0f , 85.0f , VulkanTextOverlay : : alignLeft ) ;
# endif
2016-02-16 15:07:25 +01:00
}
} ;
2016-08-11 13:15:49 +02:00
VULKAN_EXAMPLE_MAIN ( )