2016-02-16 15:07:25 +01:00
/*
* Vulkan Example - Basic indexed triangle rendering
*
2016-08-09 23:52:06 +02:00
* Note :
2016-02-16 15:07:25 +01:00
* This is a " pedal to the metal " example to show off how to get Vulkan up an displaying something
* Contrary to the other examples , this one won ' t make use of helper functions or initializers
* Except in a few cases ( swap chain setup e . g . )
*
2017-01-21 22:05:21 +01:00
* Copyright ( C ) 2016 - 2017 by Sascha Willems - www . saschawillems . de
2016-02-16 15:07:25 +01:00
*
* 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>
2016-12-23 17:18:53 +01:00
# include <fstream>
2016-02-16 15:07:25 +01:00
# include <vector>
2016-07-18 20:32:25 +02:00
# include <exception>
2016-02-16 15:07:25 +01:00
# 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-08-09 23:52:06 +02:00
// Set to "true" to enable Vulkan's validation layers (see vulkandebug.cpp for details)
2016-02-16 15:07:25 +01:00
# define ENABLE_VALIDATION false
2016-08-09 23:52:06 +02:00
// Set to "true" to use staging buffers for uploading vertex and index data to device local memory
// See "prepareVertices" for details on what's staging and on why to use it
2016-03-17 22:32:15 +01:00
# define USE_STAGING true
2016-02-16 15:07:25 +01:00
class VulkanExample : public VulkanExampleBase
{
public :
2017-01-21 22:05:21 +01:00
// Vertex layout used in this example
struct Vertex {
float position [ 3 ] ;
float color [ 3 ] ;
} ;
2016-08-09 23:52:06 +02:00
// Vertex buffer and attributes
2016-02-16 15:07:25 +01:00
struct {
2016-08-09 23:52:06 +02:00
VkDeviceMemory memory ; // Handle to the device memory for this buffer
VkBuffer buffer ; // Handle to the Vulkan buffer object that the memory is bound to
2016-02-16 15:07:25 +01:00
} vertices ;
2016-08-09 23:52:06 +02:00
// Index buffer
struct
{
VkDeviceMemory memory ;
VkBuffer buffer ;
uint32_t count ;
2016-02-16 15:07:25 +01:00
} indices ;
2016-12-24 12:43:37 +01:00
// Uniform buffer block object
2016-02-16 15:07:25 +01:00
struct {
2016-08-09 23:52:06 +02:00
VkDeviceMemory memory ;
VkBuffer buffer ;
2016-02-16 15:07:25 +01:00
VkDescriptorBufferInfo descriptor ;
2016-12-24 12:43:37 +01:00
} uniformBufferVS ;
2016-02-16 15:07:25 +01:00
2016-06-03 12:19:53 +02:00
// For simplicity we use the same uniform block layout as in the shader:
//
// layout(set = 0, binding = 0) uniform UBO
// {
// mat4 projectionMatrix;
// mat4 modelMatrix;
// mat4 viewMatrix;
// } ubo;
//
// This way we can just memcopy the ubo data to the ubo
2016-08-09 23:52:06 +02:00
// Note: You should use data types that align with the GPU in order to avoid manual padding (vec4, mat4)
2016-02-16 15:07:25 +01:00
struct {
glm : : mat4 projectionMatrix ;
glm : : mat4 modelMatrix ;
glm : : mat4 viewMatrix ;
} uboVS ;
2016-08-09 23:52:06 +02:00
// The pipeline layout is used by a pipline to access the descriptor sets
// It defines interface (without binding any actual data) between the shader stages used by the pipeline and the shader resources
// A pipeline layout can be shared among multiple pipelines as long as their interfaces match
VkPipelineLayout pipelineLayout ;
// Pipelines (often called "pipeline state objects") are used to bake all states that affect a pipeline
// While in OpenGL every state can be changed at (almost) any time, Vulkan requires to layout the graphics (and compute) pipeline states upfront
// So for each combination of non-dynamic pipeline states you need a new pipeline (there are a few exceptions to this not discussed here)
// Even though this adds a new dimension of planing ahead, it's a great opportunity for performance optimizations by the driver
2016-06-03 12:19:53 +02:00
VkPipeline pipeline ;
2016-08-09 23:52:06 +02:00
// The descriptor set layout describes the shader binding layout (without actually referencing descriptor)
// Like the pipeline layout it's pretty much a blueprint and can be used with different descriptor sets as long as their layout matches
VkDescriptorSetLayout descriptorSetLayout ;
2016-06-03 12:19:53 +02:00
// The descriptor set stores the resources bound to the binding points in a shader
2016-08-09 23:52:06 +02:00
// It connects the binding points of the different shaders with the buffers and images used for those bindings
2016-02-16 15:07:25 +01:00
VkDescriptorSet descriptorSet ;
2016-06-03 12:19:53 +02:00
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Synchronization primitives
// Synchronization is an important concept of Vulkan that OpenGL mostly hid away. Getting this right is crucial to using Vulkan.
// Semaphores
// Used to coordinate operations within the graphics queue and ensure correct command ordering
VkSemaphore presentCompleteSemaphore ;
VkSemaphore renderCompleteSemaphore ;
// Fences
// Used to check the completion of queue operations (e.g. command buffer execution)
std : : vector < VkFence > waitFences ;
2016-03-06 12:16:04 +01:00
2016-02-16 15:07:25 +01:00
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
zoom = - 2.5f ;
title = " Vulkan Example - Basic indexed triangle " ;
// Values not set here are initialized in the base class constructor
}
~ VulkanExample ( )
{
// Clean up used Vulkan resources
2016-08-09 23:52:06 +02:00
// Note: Inherited destructor cleans up resources stored in base class
2016-06-03 12:19:53 +02:00
vkDestroyPipeline ( device , pipeline , nullptr ) ;
2016-02-16 15:07:25 +01:00
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
2016-08-09 23:52:06 +02:00
vkDestroyBuffer ( device , vertices . buffer , nullptr ) ;
vkFreeMemory ( device , vertices . memory , nullptr ) ;
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
vkDestroyBuffer ( device , indices . buffer , nullptr ) ;
vkFreeMemory ( device , indices . memory , nullptr ) ;
2016-03-06 12:16:04 +01:00
2016-12-24 12:43:37 +01:00
vkDestroyBuffer ( device , uniformBufferVS . buffer , nullptr ) ;
vkFreeMemory ( device , uniformBufferVS . memory , nullptr ) ;
2016-08-09 23:52:06 +02:00
vkDestroySemaphore ( device , presentCompleteSemaphore , nullptr ) ;
vkDestroySemaphore ( device , renderCompleteSemaphore , nullptr ) ;
for ( auto & fence : waitFences )
{
vkDestroyFence ( device , fence , nullptr ) ;
}
2016-02-16 15:07:25 +01:00
}
2016-07-18 20:32:25 +02:00
// This function is used to request a device memory type that supports all the property flags we request (e.g. device local, host visibile)
// Upon success it will return the index of the memory type that fits our requestes memory properties
// This is necessary as implementations can offer an arbitrary number of memory types with different
// memory properties.
// You can check http://vulkan.gpuinfo.org/ for details on different memory configurations
uint32_t getMemoryTypeIndex ( uint32_t typeBits , VkMemoryPropertyFlags properties )
{
// Iterate over all memory types available for the device used in this example
for ( uint32_t i = 0 ; i < deviceMemoryProperties . memoryTypeCount ; i + + )
{
if ( ( typeBits & 1 ) = = 1 )
{
if ( ( deviceMemoryProperties . memoryTypes [ i ] . propertyFlags & properties ) = = properties )
{
return i ;
}
}
typeBits > > = 1 ;
}
throw " Could not find a suitable memory type! " ;
}
2016-08-09 23:52:06 +02:00
// Create the Vulkan synchronization primitives used in this example
void prepareSynchronizationPrimitives ( )
{
// Semaphores (Used for correct command ordering)
VkSemaphoreCreateInfo semaphoreCreateInfo = { } ;
semaphoreCreateInfo . sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO ;
semaphoreCreateInfo . pNext = nullptr ;
// Semaphore used to ensures that image presentation is complete before starting to submit again
VK_CHECK_RESULT ( vkCreateSemaphore ( device , & semaphoreCreateInfo , nullptr , & presentCompleteSemaphore ) ) ;
// Semaphore used to ensures that all commands submitted have been finished before submitting the image to the queue
VK_CHECK_RESULT ( vkCreateSemaphore ( device , & semaphoreCreateInfo , nullptr , & renderCompleteSemaphore ) ) ;
// Fences (Used to check draw command buffer completion)
VkFenceCreateInfo fenceCreateInfo = { } ;
fenceCreateInfo . sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO ;
2016-08-14 10:18:43 +02:00
// Create in signaled state so we don't wait on first render of each command buffer
fenceCreateInfo . flags = VK_FENCE_CREATE_SIGNALED_BIT ;
2016-08-09 23:52:06 +02:00
waitFences . resize ( drawCmdBuffers . size ( ) ) ;
for ( auto & fence : waitFences )
{
VK_CHECK_RESULT ( vkCreateFence ( device , & fenceCreateInfo , nullptr , & fence ) ) ;
}
}
// Get a new command buffer from the command pool
// If begin is true, the command buffer is also started so we can start adding commands
2016-06-03 13:15:55 +02:00
VkCommandBuffer getCommandBuffer ( bool begin )
{
VkCommandBuffer cmdBuffer ;
VkCommandBufferAllocateInfo cmdBufAllocateInfo = { } ;
cmdBufAllocateInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO ;
cmdBufAllocateInfo . commandPool = cmdPool ;
cmdBufAllocateInfo . level = VK_COMMAND_BUFFER_LEVEL_PRIMARY ;
cmdBufAllocateInfo . commandBufferCount = 1 ;
VK_CHECK_RESULT ( vkAllocateCommandBuffers ( device , & cmdBufAllocateInfo , & cmdBuffer ) ) ;
// If requested, also start the new command buffer
if ( begin )
{
VkCommandBufferBeginInfo cmdBufInfo = vkTools : : initializers : : commandBufferBeginInfo ( ) ;
VK_CHECK_RESULT ( vkBeginCommandBuffer ( cmdBuffer , & cmdBufInfo ) ) ;
}
return cmdBuffer ;
}
2016-08-09 23:52:06 +02:00
// End the command buffer and submit it to the queue
// Uses a fence to ensure command buffer has finished executing before deleting it
2016-06-03 13:15:55 +02:00
void flushCommandBuffer ( VkCommandBuffer commandBuffer )
{
assert ( commandBuffer ! = VK_NULL_HANDLE ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( commandBuffer ) ) ;
VkSubmitInfo submitInfo = { } ;
submitInfo . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & commandBuffer ;
2016-08-09 23:52:06 +02:00
// Create fence to ensure that the command buffer has finished executing
VkFenceCreateInfo fenceCreateInfo = { } ;
fenceCreateInfo . sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO ;
fenceCreateInfo . flags = 0 ;
VkFence fence ;
VK_CHECK_RESULT ( vkCreateFence ( device , & fenceCreateInfo , nullptr , & fence ) ) ;
// Submit to the queue
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , fence ) ) ;
// Wait for the fence to signal that command buffer has finished executing
VK_CHECK_RESULT ( vkWaitForFences ( device , 1 , & fence , VK_TRUE , DEFAULT_FENCE_TIMEOUT ) ) ;
vkDestroyFence ( device , fence , nullptr ) ;
2016-06-06 09:41:58 +02:00
vkFreeCommandBuffers ( device , cmdPool , 1 , & commandBuffer ) ;
2016-06-03 13:15:55 +02:00
}
2016-02-16 15:07:25 +01:00
// Build separate command buffers for every framebuffer image
2016-08-09 23:52:06 +02:00
// Unlike in OpenGL all rendering commands are recorded once into command buffers that are then resubmitted to the queue
// This allows to generate work upfront and from multiple threads, one of the biggest advantages of Vulkan
2016-02-16 15:07:25 +01:00
void buildCommandBuffers ( )
{
VkCommandBufferBeginInfo cmdBufInfo = { } ;
cmdBufInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO ;
2016-08-09 23:52:06 +02:00
cmdBufInfo . pNext = nullptr ;
2016-02-16 15:07:25 +01:00
2016-06-02 20:58:25 +02:00
// Set clear values for all framebuffer attachments with loadOp set to clear
2016-08-09 23:52:06 +02:00
// We use two attachments (color and depth) that are cleared at the start of the subpass and as such we need to set clear values for both
2016-02-16 15:07:25 +01:00
VkClearValue clearValues [ 2 ] ;
2016-08-09 23:52:06 +02:00
clearValues [ 0 ] . color = { { 0.0f , 0.0f , 0.2f , 1.0f } } ;
2016-02-16 15:07:25 +01:00
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
VkRenderPassBeginInfo renderPassBeginInfo = { } ;
renderPassBeginInfo . sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO ;
2016-08-09 23:52:06 +02:00
renderPassBeginInfo . pNext = nullptr ;
2016-02-16 15:07:25 +01: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 ;
2016-05-11 20:39:22 +02:00
2016-02-16 15:07:25 +01:00
for ( int32_t i = 0 ; i < drawCmdBuffers . size ( ) ; + + i )
{
// Set target frame buffer
renderPassBeginInfo . framebuffer = frameBuffers [ i ] ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
2016-02-16 15:07:25 +01:00
2016-05-11 20:39:22 +02:00
// Start the first sub pass specified in our default render pass setup by the base class
// This will clear the color and depth attachment
2016-02-16 15:07:25 +01:00
vkCmdBeginRenderPass ( drawCmdBuffers [ i ] , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
// Update dynamic viewport state
VkViewport viewport = { } ;
viewport . height = ( float ) height ;
viewport . width = ( float ) width ;
viewport . minDepth = ( float ) 0.0f ;
viewport . maxDepth = ( float ) 1.0f ;
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
// Update dynamic scissor state
VkRect2D scissor = { } ;
scissor . extent . width = width ;
scissor . extent . height = height ;
scissor . offset . x = 0 ;
scissor . offset . y = 0 ;
vkCmdSetScissor ( drawCmdBuffers [ i ] , 0 , 1 , & scissor ) ;
// Bind descriptor sets describing shader binding points
2016-08-09 23:52:06 +02:00
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSet , 0 , nullptr ) ;
2016-02-16 15:07:25 +01:00
2016-06-03 12:19:53 +02:00
// Bind the rendering pipeline
2016-08-09 23:52:06 +02:00
// The pipeline (state object) contains all states of the rendering pipeline, binding it will set all the states specified at pipeline creation time
2016-06-03 12:19:53 +02:00
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline ) ;
2016-02-16 15:07:25 +01:00
2016-06-03 12:19:53 +02:00
// Bind triangle vertex buffer (contains position and colors)
2016-02-16 15:07:25 +01:00
VkDeviceSize offsets [ 1 ] = { 0 } ;
2017-01-21 22:05:21 +01:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , 0 , 1 , & vertices . buffer , offsets ) ;
2016-02-16 15:07:25 +01:00
2016-06-03 12:19:53 +02:00
// Bind triangle index buffer
2016-08-09 23:52:06 +02:00
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , indices . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
2016-02-16 15:07:25 +01:00
// Draw indexed triangle
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , indices . count , 1 , 0 , 0 , 1 ) ;
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
2016-08-09 23:52:06 +02:00
// Ending the render pass will add an implicit barrier transitioning the frame buffer color attachment to
// VK_IMAGE_LAYOUT_PRESENT_SRC_KHR for presenting it to the windowing system
2016-02-16 15:07:25 +01:00
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
2016-02-16 15:07:25 +01:00
}
}
void draw ( )
{
// Get next image in the swap chain (back/front buffer)
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( swapChain . acquireNextImage ( presentCompleteSemaphore , & currentBuffer ) ) ;
2016-02-16 15:07:25 +01:00
2016-08-14 10:18:43 +02:00
// Use a fence to wait until the command buffer has finished execution before using it again
VK_CHECK_RESULT ( vkWaitForFences ( device , 1 , & waitFences [ currentBuffer ] , VK_TRUE , UINT64_MAX ) ) ;
VK_CHECK_RESULT ( vkResetFences ( device , 1 , & waitFences [ currentBuffer ] ) ) ;
2016-08-11 19:29:40 +02:00
// Pipeline stage at which the queue submission will wait (via pWaitSemaphores)
VkPipelineStageFlags waitStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
2016-08-09 23:52:06 +02:00
// The submit info structure specifices a command buffer queue submission batch
VkSubmitInfo submitInfo = { } ;
2016-03-13 12:17:57 +01:00
submitInfo . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
2016-08-11 19:29:40 +02:00
submitInfo . pWaitDstStageMask = & waitStageMask ; // Pointer to the list of pipeline stages that the semaphore waits will occur at
2016-08-09 23:52:06 +02:00
submitInfo . pWaitSemaphores = & presentCompleteSemaphore ; // Semaphore(s) to wait upon before the submitted command buffer starts executing
submitInfo . waitSemaphoreCount = 1 ; // One wait semaphore
submitInfo . pSignalSemaphores = & renderCompleteSemaphore ; // Semaphore(s) to be signaled when command buffers have completed
submitInfo . signalSemaphoreCount = 1 ; // One signal semaphore
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ; // Command buffers(s) to execute in this batch (submission)
submitInfo . commandBufferCount = 1 ; // One command buffer
// Submit to the graphics queue passing a wait fence
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , waitFences [ currentBuffer ] ) ) ;
2016-03-13 12:17:57 +01:00
// Present the current buffer to the swap chain
2016-08-09 23:52:06 +02:00
// Pass the semaphore signaled by the command buffer submission from the submit info as the wait semaphore for swap chain presentation
// This ensures that the image is not presented to the windowing system until all commands have been submitted
VK_CHECK_RESULT ( swapChain . queuePresent ( queue , currentBuffer , renderCompleteSemaphore ) ) ;
2016-02-16 15:07:25 +01:00
}
2016-08-09 23:52:06 +02:00
// Prepare vertex and index buffers for an indexed triangle
// Also uploads them to device local memory using staging and initializes vertex input and attribute binding to match the vertex shader
2016-03-17 22:32:15 +01:00
void prepareVertices ( bool useStagingBuffers )
2016-02-16 15:07:25 +01:00
{
2016-08-09 23:52:06 +02:00
// A note on memory management in Vulkan in general:
// This is a very complex topic and while it's fine for an example application to to small individual memory allocations that is not
// what should be done a real-world application, where you should allocate large chunkgs of memory at once isntead.
2016-02-16 15:07:25 +01:00
// Setup vertices
2016-08-09 23:52:06 +02:00
std : : vector < Vertex > vertexBuffer =
{
2016-06-03 12:19:53 +02:00
{ { 1.0f , 1.0f , 0.0f } , { 1.0f , 0.0f , 0.0f } } ,
{ { - 1.0f , 1.0f , 0.0f } , { 0.0f , 1.0f , 0.0f } } ,
{ { 0.0f , - 1.0f , 0.0f } , { 0.0f , 0.0f , 1.0f } }
2016-02-16 15:07:25 +01:00
} ;
2016-06-03 12:19:53 +02:00
uint32_t vertexBufferSize = static_cast < uint32_t > ( vertexBuffer . size ( ) ) * sizeof ( Vertex ) ;
2016-02-16 15:07:25 +01:00
// Setup indices
std : : vector < uint32_t > indexBuffer = { 0 , 1 , 2 } ;
2016-06-03 12:19:53 +02:00
indices . count = static_cast < uint32_t > ( indexBuffer . size ( ) ) ;
uint32_t indexBufferSize = indices . count * sizeof ( uint32_t ) ;
2016-02-16 15:07:25 +01:00
VkMemoryAllocateInfo memAlloc = { } ;
memAlloc . sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO ;
VkMemoryRequirements memReqs ;
void * data ;
2016-03-17 22:32:15 +01:00
if ( useStagingBuffers )
{
// Static data like vertex and index buffer should be stored on the device memory
// for optimal (and fastest) access by the GPU
//
// To achieve this we use so-called "staging buffers" :
// - Create a buffer that's visible to the host (and can be mapped)
// - Copy the data to this buffer
// - Create another buffer that's local on the device (VRAM) with the same size
// - Copy the data from the host to the device using a command buffer
2016-05-11 20:39:22 +02:00
// - Delete the host visible (staging) buffer
// - Use the device local buffers for rendering
2016-03-17 22:32:15 +01:00
struct StagingBuffer {
VkDeviceMemory memory ;
VkBuffer buffer ;
} ;
struct {
StagingBuffer vertices ;
StagingBuffer indices ;
} stagingBuffers ;
// Vertex buffer
VkBufferCreateInfo vertexBufferInfo = { } ;
vertexBufferInfo . sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ;
vertexBufferInfo . size = vertexBufferSize ;
// Buffer is used as the copy source
vertexBufferInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
// Create a host-visible buffer to copy the vertex data to (staging buffer)
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & vertexBufferInfo , nullptr , & stagingBuffers . vertices . buffer ) ) ;
2016-03-17 22:32:15 +01:00
vkGetBufferMemoryRequirements ( device , stagingBuffers . vertices . buffer , & memReqs ) ;
memAlloc . allocationSize = memReqs . size ;
2016-07-18 20:32:25 +02:00
// Request a host visible memory type that can be used to copy our data do
// Also request it to be coherent, so that writes are visible to the GPU right after unmapping the buffer
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & stagingBuffers . vertices . memory ) ) ;
2016-03-17 22:32:15 +01:00
// Map and copy
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkMapMemory ( device , stagingBuffers . vertices . memory , 0 , memAlloc . allocationSize , 0 , & data ) ) ;
2016-03-17 22:32:15 +01:00
memcpy ( data , vertexBuffer . data ( ) , vertexBufferSize ) ;
vkUnmapMemory ( device , stagingBuffers . vertices . memory ) ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkBindBufferMemory ( device , stagingBuffers . vertices . buffer , stagingBuffers . vertices . memory , 0 ) ) ;
2016-03-17 22:32:15 +01:00
2016-08-09 23:52:06 +02:00
// Create a device local buffer to which the (host local) vertex data will be copied and which will be used for rendering
2016-03-17 22:32:15 +01:00
vertexBufferInfo . usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT ;
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & vertexBufferInfo , nullptr , & vertices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , vertices . buffer , & memReqs ) ;
2016-03-17 22:32:15 +01:00
memAlloc . allocationSize = memReqs . size ;
2016-07-18 20:32:25 +02:00
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & vertices . memory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , vertices . buffer , vertices . memory , 0 ) ) ;
2016-03-17 22:32:15 +01:00
// Index buffer
VkBufferCreateInfo indexbufferInfo = { } ;
indexbufferInfo . sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ;
indexbufferInfo . size = indexBufferSize ;
indexbufferInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
// Copy index data to a buffer visible to the host (staging buffer)
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & indexbufferInfo , nullptr , & stagingBuffers . indices . buffer ) ) ;
2016-03-17 22:32:15 +01:00
vkGetBufferMemoryRequirements ( device , stagingBuffers . indices . buffer , & memReqs ) ;
memAlloc . allocationSize = memReqs . size ;
2016-07-18 20:32:25 +02:00
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & stagingBuffers . indices . memory ) ) ;
VK_CHECK_RESULT ( vkMapMemory ( device , stagingBuffers . indices . memory , 0 , indexBufferSize , 0 , & data ) ) ;
2016-03-17 22:32:15 +01:00
memcpy ( data , indexBuffer . data ( ) , indexBufferSize ) ;
vkUnmapMemory ( device , stagingBuffers . indices . memory ) ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkBindBufferMemory ( device , stagingBuffers . indices . buffer , stagingBuffers . indices . memory , 0 ) ) ;
2016-03-17 22:32:15 +01:00
// Create destination buffer with device only visibility
indexbufferInfo . usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT ;
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & indexbufferInfo , nullptr , & indices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , indices . buffer , & memReqs ) ;
2016-03-17 22:32:15 +01:00
memAlloc . allocationSize = memReqs . size ;
2016-07-18 20:32:25 +02:00
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & indices . memory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , indices . buffer , indices . memory , 0 ) ) ;
2016-03-17 22:32:15 +01:00
VkCommandBufferBeginInfo cmdBufferBeginInfo = { } ;
cmdBufferBeginInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO ;
2016-08-09 23:52:06 +02:00
cmdBufferBeginInfo . pNext = nullptr ;
2016-03-17 22:32:15 +01:00
2016-06-03 13:15:55 +02:00
// Buffer copies have to be submitted to a queue, so we need a command buffer for them
2016-08-09 23:52:06 +02:00
// Note: Some devices offer a dedicated transfer queue (with only the transfer bit set) that may be faster when doing lots of copies
2016-06-03 13:15:55 +02:00
VkCommandBuffer copyCmd = getCommandBuffer ( true ) ;
2016-03-17 22:32:15 +01:00
// Put buffer region copies into command buffer
2016-06-03 13:15:55 +02:00
VkBufferCopy copyRegion = { } ;
2016-03-17 22:32:15 +01:00
// Vertex buffer
copyRegion . size = vertexBufferSize ;
2016-08-09 23:52:06 +02:00
vkCmdCopyBuffer ( copyCmd , stagingBuffers . vertices . buffer , vertices . buffer , 1 , & copyRegion ) ;
2016-03-17 22:32:15 +01:00
// Index buffer
copyRegion . size = indexBufferSize ;
2016-08-09 23:52:06 +02:00
vkCmdCopyBuffer ( copyCmd , stagingBuffers . indices . buffer , indices . buffer , 1 , & copyRegion ) ;
2016-03-17 22:32:15 +01:00
2016-08-09 23:52:06 +02:00
// Flushing the command buffer will also submit it to the queue and uses a fence to ensure that all commands have been executed before returning
2016-06-03 13:15:55 +02:00
flushCommandBuffer ( copyCmd ) ;
2016-03-17 22:32:15 +01:00
// Destroy staging buffers
2016-08-09 23:52:06 +02:00
// Note: Staging buffer must not be deleted before the copies have been submitted and executed
2016-03-17 22:32:15 +01:00
vkDestroyBuffer ( device , stagingBuffers . vertices . buffer , nullptr ) ;
vkFreeMemory ( device , stagingBuffers . vertices . memory , nullptr ) ;
vkDestroyBuffer ( device , stagingBuffers . indices . buffer , nullptr ) ;
vkFreeMemory ( device , stagingBuffers . indices . memory , nullptr ) ;
}
else
{
// Don't use staging
2016-08-09 23:52:06 +02:00
// Create host-visible buffers only and use these for rendering. This is not advised and will usually result in lower rendering performance
2016-03-17 22:32:15 +01:00
// Vertex buffer
VkBufferCreateInfo vertexBufferInfo = { } ;
vertexBufferInfo . sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ;
vertexBufferInfo . size = vertexBufferSize ;
vertexBufferInfo . usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT ;
// Copy vertex data to a buffer visible to the host
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & vertexBufferInfo , nullptr , & vertices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , vertices . buffer , & memReqs ) ;
2016-03-17 22:32:15 +01:00
memAlloc . allocationSize = memReqs . size ;
2016-07-18 20:32:25 +02:00
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ) ;
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & vertices . memory ) ) ;
VK_CHECK_RESULT ( vkMapMemory ( device , vertices . memory , 0 , memAlloc . allocationSize , 0 , & data ) ) ;
2016-03-17 22:32:15 +01:00
memcpy ( data , vertexBuffer . data ( ) , vertexBufferSize ) ;
2016-08-09 23:52:06 +02:00
vkUnmapMemory ( device , vertices . memory ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , vertices . buffer , vertices . memory , 0 ) ) ;
2016-03-17 22:32:15 +01:00
// Index buffer
VkBufferCreateInfo indexbufferInfo = { } ;
indexbufferInfo . sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ;
indexbufferInfo . size = indexBufferSize ;
indexbufferInfo . usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT ;
// Copy index data to a buffer visible to the host
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & indexbufferInfo , nullptr , & indices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , indices . buffer , & memReqs ) ;
2016-03-17 22:32:15 +01:00
memAlloc . allocationSize = memReqs . size ;
2016-07-18 20:32:25 +02:00
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ) ;
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & indices . memory ) ) ;
VK_CHECK_RESULT ( vkMapMemory ( device , indices . memory , 0 , indexBufferSize , 0 , & data ) ) ;
2016-03-17 22:32:15 +01:00
memcpy ( data , indexBuffer . data ( ) , indexBufferSize ) ;
2016-08-09 23:52:06 +02:00
vkUnmapMemory ( device , indices . memory ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , indices . buffer , indices . memory , 0 ) ) ;
2016-03-17 22:32:15 +01:00
}
2016-02-16 15:07:25 +01:00
}
void setupDescriptorPool ( )
{
// We need to tell the API the number of max. requested descriptors per type
VkDescriptorPoolSize typeCounts [ 1 ] ;
2016-08-09 23:52:06 +02:00
// This example only uses one descriptor type (uniform buffer) and only requests one descriptor of this type
2016-02-16 15:07:25 +01:00
typeCounts [ 0 ] . type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
typeCounts [ 0 ] . descriptorCount = 1 ;
// For additional types you need to add new entries in the type count list
// E.g. for two combined image samplers :
// typeCounts[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
// typeCounts[1].descriptorCount = 2;
// Create the global descriptor pool
// All descriptors used in this example are allocated from this pool
VkDescriptorPoolCreateInfo descriptorPoolInfo = { } ;
descriptorPoolInfo . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO ;
2016-08-09 23:52:06 +02:00
descriptorPoolInfo . pNext = nullptr ;
2016-02-16 15:07:25 +01:00
descriptorPoolInfo . poolSizeCount = 1 ;
descriptorPoolInfo . pPoolSizes = typeCounts ;
2016-08-09 23:52:06 +02:00
// Set the max. number of descriptor sets that can be requested from this pool (requesting beyond this limit will result in an error)
2016-02-16 15:07:25 +01:00
descriptorPoolInfo . maxSets = 1 ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSetLayout ( )
{
// Setup layout of descriptors used in this example
2016-08-09 23:52:06 +02:00
// Basically connects the different shader stages to descriptors for binding uniform buffers, image samplers, etc.
// So every shader binding should map to one descriptor set layout binding
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Binding 0: Uniform buffer (Vertex shader)
2016-02-16 15:07:25 +01:00
VkDescriptorSetLayoutBinding layoutBinding = { } ;
layoutBinding . descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
layoutBinding . descriptorCount = 1 ;
layoutBinding . stageFlags = VK_SHADER_STAGE_VERTEX_BIT ;
2016-08-09 23:52:06 +02:00
layoutBinding . pImmutableSamplers = nullptr ;
2016-02-16 15:07:25 +01:00
VkDescriptorSetLayoutCreateInfo descriptorLayout = { } ;
descriptorLayout . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO ;
2016-08-09 23:52:06 +02:00
descriptorLayout . pNext = nullptr ;
2016-02-16 15:07:25 +01:00
descriptorLayout . bindingCount = 1 ;
descriptorLayout . pBindings = & layoutBinding ;
2016-08-09 23:52:06 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Create the pipeline layout that is used to generate the rendering pipelines that are based on this descriptor set layout
// In a more complex scenario you would have different pipeline layouts for different descriptor set layouts that could be reused
2016-02-16 15:07:25 +01:00
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = { } ;
pPipelineLayoutCreateInfo . sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO ;
2016-08-09 23:52:06 +02:00
pPipelineLayoutCreateInfo . pNext = nullptr ;
2016-02-16 15:07:25 +01:00
pPipelineLayoutCreateInfo . setLayoutCount = 1 ;
pPipelineLayoutCreateInfo . pSetLayouts = & descriptorSetLayout ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSet ( )
{
2016-05-11 20:39:22 +02:00
// Allocate a new descriptor set from the global descriptor pool
2016-02-16 15:07:25 +01:00
VkDescriptorSetAllocateInfo allocInfo = { } ;
allocInfo . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO ;
allocInfo . descriptorPool = descriptorPool ;
allocInfo . descriptorSetCount = 1 ;
allocInfo . pSetLayouts = & descriptorSetLayout ;
2016-05-11 20:39:22 +02:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
// Update the descriptor set determining the shader binding points
// For every binding point used in a shader there needs to be one
// descriptor set matching that binding point
VkWriteDescriptorSet writeDescriptorSet = { } ;
2016-02-16 15:07:25 +01:00
// Binding 0 : Uniform buffer
writeDescriptorSet . sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ;
writeDescriptorSet . dstSet = descriptorSet ;
writeDescriptorSet . descriptorCount = 1 ;
writeDescriptorSet . descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
2016-12-24 12:43:37 +01:00
writeDescriptorSet . pBufferInfo = & uniformBufferVS . descriptor ;
2016-02-16 15:07:25 +01:00
// Binds this uniform buffer to binding point 0
writeDescriptorSet . dstBinding = 0 ;
2016-08-09 23:52:06 +02:00
vkUpdateDescriptorSets ( device , 1 , & writeDescriptorSet , 0 , nullptr ) ;
2016-02-16 15:07:25 +01:00
}
2016-06-03 13:15:55 +02:00
// Create the depth (and stencil) buffer attachments used by our framebuffers
2016-08-09 23:52:06 +02:00
// Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare
2016-06-03 13:15:55 +02:00
void setupDepthStencil ( )
{
// Create an optimal image used as the depth stencil attachment
VkImageCreateInfo image = { } ;
image . sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO ;
image . imageType = VK_IMAGE_TYPE_2D ;
image . format = depthFormat ;
// Use example's height and width
image . extent = { width , height , 1 } ;
image . mipLevels = 1 ;
image . arrayLayers = 1 ;
image . samples = VK_SAMPLE_COUNT_1_BIT ;
image . tiling = VK_IMAGE_TILING_OPTIMAL ;
image . usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT ;
image . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
VK_CHECK_RESULT ( vkCreateImage ( device , & image , nullptr , & depthStencil . image ) ) ;
// Allocate memory for the image (device local) and bind it to our image
VkMemoryAllocateInfo memAlloc = { } ;
memAlloc . sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO ;
VkMemoryRequirements memReqs ;
vkGetImageMemoryRequirements ( device , depthStencil . image , & memReqs ) ;
memAlloc . allocationSize = memReqs . size ;
2016-07-18 20:32:25 +02:00
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
2016-06-03 13:15:55 +02:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & depthStencil . mem ) ) ;
VK_CHECK_RESULT ( vkBindImageMemory ( device , depthStencil . image , depthStencil . mem , 0 ) ) ;
2016-08-09 23:52:06 +02:00
// Create a view for the depth stencil image
// Images aren't directly accessed in Vulkan, but rather through views described by a subresource range
// This allows for multiple views of one image with differing ranges (e.g. for different layers)
2016-06-03 13:15:55 +02:00
VkImageViewCreateInfo depthStencilView = { } ;
depthStencilView . sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO ;
depthStencilView . viewType = VK_IMAGE_VIEW_TYPE_2D ;
depthStencilView . format = depthFormat ;
depthStencilView . subresourceRange = { } ;
depthStencilView . subresourceRange . aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ;
depthStencilView . subresourceRange . baseMipLevel = 0 ;
depthStencilView . subresourceRange . levelCount = 1 ;
depthStencilView . subresourceRange . baseArrayLayer = 0 ;
depthStencilView . subresourceRange . layerCount = 1 ;
depthStencilView . image = depthStencil . image ;
VK_CHECK_RESULT ( vkCreateImageView ( device , & depthStencilView , nullptr , & depthStencil . view ) ) ;
}
2016-06-02 20:58:25 +02:00
// Create a frame buffer for each swap chain image
2016-08-09 23:52:06 +02:00
// Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare
2016-06-02 20:58:25 +02:00
void setupFrameBuffer ( )
{
2016-08-09 23:52:06 +02:00
// Create a frame buffer for every image in the swapchain
2016-06-02 20:58:25 +02:00
frameBuffers . resize ( swapChain . imageCount ) ;
for ( size_t i = 0 ; i < frameBuffers . size ( ) ; i + + )
{
2016-08-09 23:52:06 +02:00
std : : array < VkImageView , 2 > attachments ;
attachments [ 0 ] = swapChain . buffers [ i ] . view ; // Color attachment is the view of the swapchain image
attachments [ 1 ] = depthStencil . view ; // Depth/Stencil attachment is the same for all frame buffers
2016-06-02 20:58:25 +02:00
VkFramebufferCreateInfo frameBufferCreateInfo = { } ;
frameBufferCreateInfo . sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO ;
2016-08-09 23:52:06 +02:00
// All frame buffers use the same renderpass setup
2016-06-02 20:58:25 +02:00
frameBufferCreateInfo . renderPass = renderPass ;
frameBufferCreateInfo . attachmentCount = static_cast < uint32_t > ( attachments . size ( ) ) ;
frameBufferCreateInfo . pAttachments = attachments . data ( ) ;
frameBufferCreateInfo . width = width ;
frameBufferCreateInfo . height = height ;
frameBufferCreateInfo . layers = 1 ;
// Create the framebuffer
VK_CHECK_RESULT ( vkCreateFramebuffer ( device , & frameBufferCreateInfo , nullptr , & frameBuffers [ i ] ) ) ;
}
}
2016-08-09 23:52:06 +02:00
// Render pass setup
// Render passes are a new concept in Vulkan. They describe the attachments used during rendering and may contain multiple subpasses with attachment dependencies
// This allows the driver to know up-front what the rendering will look like and is a good opportunity to optimize especially on tile-based renderers (with multiple subpasses)
// Using sub pass dependencies also adds implicit layout transitions for the attachment used, so we don't need to add explicit image memory barriers to transform them
// Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare
2016-06-02 20:58:25 +02:00
void setupRenderPass ( )
{
2016-08-09 23:52:06 +02:00
// This example will use a single render pass with one subpass
// Descriptors for the attachments used by this renderpass
2016-06-03 12:19:53 +02:00
std : : array < VkAttachmentDescription , 2 > attachments = { } ;
2016-06-02 20:58:25 +02:00
// Color attachment
2017-01-25 18:54:09 +01:00
attachments [ 0 ] . format = swapChain . colorFormat ; // Use the color format selected by the swapchain
2016-08-09 23:52:06 +02:00
attachments [ 0 ] . samples = VK_SAMPLE_COUNT_1_BIT ; // We don't use multi sampling in this example
attachments [ 0 ] . loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR ; // Clear this attachment at the start of the render pass
attachments [ 0 ] . storeOp = VK_ATTACHMENT_STORE_OP_STORE ; // Keep it's contents after the render pass is finished (for displaying it)
attachments [ 0 ] . stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE ; // We don't use stencil, so don't care for load
attachments [ 0 ] . stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE ; // Same for store
attachments [ 0 ] . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ; // Layout at render pass start. Initial doesn't matter, so we use undefined
attachments [ 0 ] . finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ; // Layout to which the attachment is transitioned when the render pass is finished
// As we want to present the color buffer to the swapchain, we transition to PRESENT_KHR
2016-06-02 20:58:25 +02:00
// Depth attachment
2017-01-25 18:54:09 +01:00
attachments [ 1 ] . format = depthFormat ; // A proper depth format is selected in the example base
2016-08-09 23:52:06 +02:00
attachments [ 1 ] . samples = VK_SAMPLE_COUNT_1_BIT ;
attachments [ 1 ] . loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR ; // Clear depth at start of first subpass
attachments [ 1 ] . storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE ; // We don't need depth after render pass has finished (DONT_CARE may result in better performance)
attachments [ 1 ] . stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE ; // No stencil
attachments [ 1 ] . stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE ; // No Stencil
attachments [ 1 ] . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ; // Layout at render pass start. Initial doesn't matter, so we use undefined
attachments [ 1 ] . finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ; // Transition to depth/stencil attachment
// Setup attachment references
2016-06-02 20:58:25 +02:00
VkAttachmentReference colorReference = { } ;
2016-08-09 23:52:06 +02:00
colorReference . attachment = 0 ; // Attachment 0 is color
colorReference . layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ; // Attachment layout used as color during the subpass
2016-06-02 20:58:25 +02:00
VkAttachmentReference depthReference = { } ;
2016-08-09 23:52:06 +02:00
depthReference . attachment = 1 ; // Attachment 1 is color
depthReference . layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ; // Attachment used as depth/stemcil used during the subpass
// Setup a single subpass reference
VkSubpassDescription subpassDescription = { } ;
subpassDescription . pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS ;
subpassDescription . colorAttachmentCount = 1 ; // Subpass uses one color attachment
subpassDescription . pColorAttachments = & colorReference ; // Reference to the color attachment in slot 0
subpassDescription . pDepthStencilAttachment = & depthReference ; // Reference to the depth attachment in slot 1
subpassDescription . inputAttachmentCount = 0 ; // Input attachments can be used to sample from contents of a previous subpass
subpassDescription . pInputAttachments = nullptr ; // (Input attachments not used by this example)
subpassDescription . preserveAttachmentCount = 0 ; // Preserved attachments can be used to loop (and preserve) attachments through subpasses
subpassDescription . pPreserveAttachments = nullptr ; // (Preserve attachments not used by this example)
subpassDescription . pResolveAttachments = nullptr ; // Resolve attachments are resolved at the end of a sub pass and can be used for e.g. multi sampling
// Setup subpass dependencies
// These will add the implicit ttachment layout transitionss specified by the attachment descriptions
// The actual usage layout is preserved through the layout specified in the attachment reference
// Each subpass dependency will introduce a memory and execution dependency between the source and dest subpass described by
// srcStageMask, dstStageMask, srcAccessMask, dstAccessMask (and dependencyFlags is set)
// Note: VK_SUBPASS_EXTERNAL is a special constant that refers to all commands executed outside of the actual renderpass)
std : : array < VkSubpassDependency , 2 > dependencies ;
// First dependency at the start of the renderpass
// Does the transition from final to initial layout
dependencies [ 0 ] . srcSubpass = VK_SUBPASS_EXTERNAL ; // Producer of the dependency
dependencies [ 0 ] . dstSubpass = 0 ; // Consumer is our single subpass that will wait for the execution depdendency
dependencies [ 0 ] . srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT ;
dependencies [ 0 ] . dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
dependencies [ 0 ] . srcAccessMask = VK_ACCESS_MEMORY_READ_BIT ;
dependencies [ 0 ] . dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ;
dependencies [ 0 ] . dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT ;
// Second dependency at the end the renderpass
// Does the transition from the initial to the final layout
dependencies [ 1 ] . srcSubpass = 0 ; // Producer of the dependency is our single subpass
dependencies [ 1 ] . dstSubpass = VK_SUBPASS_EXTERNAL ; // Consumer are all commands outside of the renderpass
dependencies [ 1 ] . srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
dependencies [ 1 ] . dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT ;
dependencies [ 1 ] . srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ;
dependencies [ 1 ] . dstAccessMask = VK_ACCESS_MEMORY_READ_BIT ;
dependencies [ 1 ] . dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT ;
// Create the actual renderpass
2016-06-02 20:58:25 +02:00
VkRenderPassCreateInfo renderPassInfo = { } ;
renderPassInfo . sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO ;
2016-08-09 23:52:06 +02:00
renderPassInfo . attachmentCount = static_cast < uint32_t > ( attachments . size ( ) ) ; // Number of attachments used by this render pass
renderPassInfo . pAttachments = attachments . data ( ) ; // Descriptions of the attachments used by the render pass
renderPassInfo . subpassCount = 1 ; // We only use one subpass in this example
renderPassInfo . pSubpasses = & subpassDescription ; // Description of that subpass
renderPassInfo . dependencyCount = static_cast < uint32_t > ( dependencies . size ( ) ) ; // Number of subpass dependencies
renderPassInfo . pDependencies = dependencies . data ( ) ; // Subpass dependencies used by the render pass
2016-06-02 20:58:25 +02:00
VK_CHECK_RESULT ( vkCreateRenderPass ( device , & renderPassInfo , nullptr , & renderPass ) ) ;
}
2016-12-23 17:18:53 +01:00
// Vulkan loads it's shaders from an immediate binary representation called SPIR-V
// Shaders are compiled offline from e.g. GLSL using the reference glslang compiler
// This function loads such a shader from a binary file and returns a shader module structure
VkShaderModule loadSPIRVShader ( std : : string filename )
{
size_t shaderSize ;
char * shaderCode ;
# if defined(__ANDROID__)
// Load shader from compressed asset
2017-01-15 10:20:53 +01:00
AAsset * asset = AAssetManager_open ( androidApp - > activity - > assetManager , filename . c_str ( ) , AASSET_MODE_STREAMING ) ;
2016-12-23 17:18:53 +01:00
assert ( asset ) ;
2017-01-23 13:30:28 +01:00
shaderSize = AAsset_getLength ( asset ) ;
assert ( shaderSize > 0 ) ;
2016-12-23 17:18:53 +01:00
2017-01-23 13:30:28 +01:00
shaderCode = new char [ shaderSize ] ;
AAsset_read ( asset , shaderCode , shaderSize ) ;
2016-12-23 17:18:53 +01:00
AAsset_close ( asset ) ;
# else
std : : ifstream is ( filename , std : : ios : : binary | std : : ios : : in | std : : ios : : ate ) ;
if ( is . is_open ( ) )
{
shaderSize = is . tellg ( ) ;
is . seekg ( 0 , std : : ios : : beg ) ;
// Copy file contents into a buffer
shaderCode = new char [ shaderSize ] ;
is . read ( shaderCode , shaderSize ) ;
is . close ( ) ;
assert ( shaderSize > 0 ) ;
}
# endif
if ( shaderCode )
{
// Create a new shader module that will be used for pipeline creation
VkShaderModuleCreateInfo moduleCreateInfo { } ;
moduleCreateInfo . sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO ;
moduleCreateInfo . codeSize = shaderSize ;
moduleCreateInfo . pCode = ( uint32_t * ) shaderCode ;
VkShaderModule shaderModule ;
VK_CHECK_RESULT ( vkCreateShaderModule ( device , & moduleCreateInfo , NULL , & shaderModule ) ) ;
delete [ ] shaderCode ;
return shaderModule ;
}
else
{
std : : cerr < < " Error: Could not open shader file \" " < < filename < < " \" " < < std : : endl ;
return VK_NULL_HANDLE ;
}
}
2016-02-16 15:07:25 +01:00
void preparePipelines ( )
{
2016-08-09 23:52:06 +02:00
// Create the graphics pipeline used in this example
// Vulkan uses the concept of rendering pipelines to encapsulate fixed states, replacing OpenGL's complex state machine
// A pipeline is then stored and hashed on the GPU making pipeline changes very fast
// Note: There are still a few dynamic states that are not directly part of the pipeline (but the info that they are used is)
2016-02-16 15:07:25 +01:00
VkGraphicsPipelineCreateInfo pipelineCreateInfo = { } ;
pipelineCreateInfo . sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO ;
2016-08-09 23:52:06 +02:00
// The layout used for this pipeline (can be shared among multiple pipelines using the same layout)
2016-02-16 15:07:25 +01:00
pipelineCreateInfo . layout = pipelineLayout ;
// Renderpass this pipeline is attached to
pipelineCreateInfo . renderPass = renderPass ;
2016-08-09 23:52:06 +02:00
// Construct the differnent states making up the pipeline
2016-12-23 17:18:53 +01:00
2016-08-09 23:52:06 +02:00
// Input assembly state describes how primitives are assembled
// This pipeline will assemble vertex data as a triangle lists (though we only use one triangle)
2016-02-16 15:07:25 +01:00
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = { } ;
inputAssemblyState . sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO ;
inputAssemblyState . topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST ;
// Rasterization state
VkPipelineRasterizationStateCreateInfo rasterizationState = { } ;
2016-12-23 17:18:53 +01:00
rasterizationState . sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO ;
rasterizationState . polygonMode = VK_POLYGON_MODE_FILL ;
2016-02-16 15:07:25 +01:00
rasterizationState . cullMode = VK_CULL_MODE_NONE ;
rasterizationState . frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE ;
rasterizationState . depthClampEnable = VK_FALSE ;
rasterizationState . rasterizerDiscardEnable = VK_FALSE ;
rasterizationState . depthBiasEnable = VK_FALSE ;
2016-04-24 13:00:48 +02:00
rasterizationState . lineWidth = 1.0f ;
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Color blend state describes how blend factors are calculated (if used)
// We need one blend attachment state per color attachment (even if blending is not used
2016-02-16 15:07:25 +01:00
VkPipelineColorBlendAttachmentState blendAttachmentState [ 1 ] = { } ;
blendAttachmentState [ 0 ] . colorWriteMask = 0xf ;
blendAttachmentState [ 0 ] . blendEnable = VK_FALSE ;
2016-08-09 23:52:06 +02:00
VkPipelineColorBlendStateCreateInfo colorBlendState = { } ;
colorBlendState . sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO ;
2016-02-16 15:07:25 +01:00
colorBlendState . attachmentCount = 1 ;
colorBlendState . pAttachments = blendAttachmentState ;
2016-08-09 23:52:06 +02:00
// Viewport state sets the number of viewports and scissor used in this pipeline
// Note: This is actually overriden by the dynamic states (see below)
2016-02-16 15:07:25 +01:00
VkPipelineViewportStateCreateInfo viewportState = { } ;
viewportState . sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO ;
viewportState . viewportCount = 1 ;
viewportState . scissorCount = 1 ;
// Enable dynamic states
2016-08-09 23:52:06 +02:00
// Most states are baked into the pipeline, but there are still a few dynamic states that can be changed within a command buffer
// To be able to change these we need do specify which dynamic states will be changed using this pipeline. Their actual states are set later on in the command buffer.
// For this example we will set the viewport and scissor using dynamic states
2016-02-16 15:07:25 +01:00
std : : vector < VkDynamicState > dynamicStateEnables ;
dynamicStateEnables . push_back ( VK_DYNAMIC_STATE_VIEWPORT ) ;
dynamicStateEnables . push_back ( VK_DYNAMIC_STATE_SCISSOR ) ;
2016-08-09 23:52:06 +02:00
VkPipelineDynamicStateCreateInfo dynamicState = { } ;
2016-02-16 15:07:25 +01:00
dynamicState . sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO ;
dynamicState . pDynamicStates = dynamicStateEnables . data ( ) ;
2016-06-03 12:19:53 +02:00
dynamicState . dynamicStateCount = static_cast < uint32_t > ( dynamicStateEnables . size ( ) ) ;
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Depth and stencil state containing depth and stencil compare and test operations
// We only use depth tests and want depth tests and writes to be enabled and compare with less or equal
2016-02-16 15:07:25 +01:00
VkPipelineDepthStencilStateCreateInfo depthStencilState = { } ;
depthStencilState . sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO ;
depthStencilState . depthTestEnable = VK_TRUE ;
depthStencilState . depthWriteEnable = VK_TRUE ;
depthStencilState . depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL ;
depthStencilState . depthBoundsTestEnable = VK_FALSE ;
depthStencilState . back . failOp = VK_STENCIL_OP_KEEP ;
depthStencilState . back . passOp = VK_STENCIL_OP_KEEP ;
depthStencilState . back . compareOp = VK_COMPARE_OP_ALWAYS ;
depthStencilState . stencilTestEnable = VK_FALSE ;
depthStencilState . front = depthStencilState . back ;
// Multi sampling state
2016-08-09 23:52:06 +02:00
// This example does not make use fo multi sampling (for anti-aliasing), the state must still be set and passed to the pipeline
2016-02-16 15:07:25 +01:00
VkPipelineMultisampleStateCreateInfo multisampleState = { } ;
multisampleState . sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO ;
multisampleState . rasterizationSamples = VK_SAMPLE_COUNT_1_BIT ;
2016-08-09 23:52:06 +02:00
multisampleState . pSampleMask = nullptr ;
2016-02-16 15:07:25 +01:00
2017-01-21 22:05:21 +01:00
// Vertex input descriptions
// Specifies the vertex input parameters for a pipeline
// Vertex input binding
// This example uses a single vertex input binding at binding point 0 (see vkCmdBindVertexBuffers)
VkVertexInputBindingDescription vertexInputBinding = { } ;
vertexInputBinding . binding = 0 ;
vertexInputBinding . stride = sizeof ( Vertex ) ;
vertexInputBinding . inputRate = VK_VERTEX_INPUT_RATE_VERTEX ;
// Inpute attribute bindings describe shader attribute locations and memory layouts
std : : array < VkVertexInputAttributeDescription , 2 > vertexInputAttributs ;
// These match the following shader layout (see triangle.vert):
// layout (location = 0) in vec3 inPos;
// layout (location = 1) in vec3 inColor;
// Attribute location 0: Position
vertexInputAttributs [ 0 ] . binding = 0 ;
vertexInputAttributs [ 0 ] . location = 0 ;
// Position attribute is three 32 bit signed (SFLOAT) floats (R32 G32 B32)
vertexInputAttributs [ 0 ] . format = VK_FORMAT_R32G32B32_SFLOAT ;
vertexInputAttributs [ 0 ] . offset = offsetof ( Vertex , position ) ;
// Attribute location 1: Color
vertexInputAttributs [ 1 ] . binding = 0 ;
vertexInputAttributs [ 1 ] . location = 1 ;
// Color attribute is three 32 bit signed (SFLOAT) floats (R32 G32 B32)
vertexInputAttributs [ 1 ] . format = VK_FORMAT_R32G32B32_SFLOAT ;
vertexInputAttributs [ 1 ] . offset = offsetof ( Vertex , color ) ;
// Vertex input state used for pipeline creation
VkPipelineVertexInputStateCreateInfo vertexInputState = { } ;
vertexInputState . sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO ;
vertexInputState . vertexBindingDescriptionCount = 1 ;
vertexInputState . pVertexBindingDescriptions = & vertexInputBinding ;
vertexInputState . vertexAttributeDescriptionCount = 2 ;
vertexInputState . pVertexAttributeDescriptions = vertexInputAttributs . data ( ) ;
// Shaders
2016-12-23 17:18:53 +01:00
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages { } ;
// Vertex shader
shaderStages [ 0 ] . sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO ;
// Set pipeline stage for this shader
shaderStages [ 0 ] . stage = VK_SHADER_STAGE_VERTEX_BIT ;
// Load binary SPIR-V shader
shaderStages [ 0 ] . module = loadSPIRVShader ( getAssetPath ( ) + " shaders/triangle.vert.spv " ) ;
// Main entry point for the shader
shaderStages [ 0 ] . pName = " main " ;
assert ( shaderStages [ 0 ] . module ! = VK_NULL_HANDLE ) ;
// Fragment shader
shaderStages [ 1 ] . sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO ;
// Set pipeline stage for this shader
shaderStages [ 1 ] . stage = VK_SHADER_STAGE_FRAGMENT_BIT ;
// Load binary SPIR-V shader
shaderStages [ 1 ] . module = loadSPIRVShader ( getAssetPath ( ) + " shaders/triangle.frag.spv " ) ;
// Main entry point for the shader
shaderStages [ 1 ] . pName = " main " ;
assert ( shaderStages [ 1 ] . module ! = VK_NULL_HANDLE ) ;
// Set pipeline shader stage info
2016-06-03 12:19:53 +02:00
pipelineCreateInfo . stageCount = static_cast < uint32_t > ( shaderStages . size ( ) ) ;
2016-03-21 20:10:21 +01:00
pipelineCreateInfo . pStages = shaderStages . data ( ) ;
2016-12-23 17:18:53 +01:00
// Assign the pipeline states to the pipeline creation info structure
2017-01-21 22:05:21 +01:00
pipelineCreateInfo . pVertexInputState = & vertexInputState ;
2016-02-16 15:07:25 +01:00
pipelineCreateInfo . pInputAssemblyState = & inputAssemblyState ;
pipelineCreateInfo . pRasterizationState = & rasterizationState ;
pipelineCreateInfo . pColorBlendState = & colorBlendState ;
pipelineCreateInfo . pMultisampleState = & multisampleState ;
pipelineCreateInfo . pViewportState = & viewportState ;
pipelineCreateInfo . pDepthStencilState = & depthStencilState ;
pipelineCreateInfo . renderPass = renderPass ;
pipelineCreateInfo . pDynamicState = & dynamicState ;
2016-08-09 23:52:06 +02:00
// Create rendering pipeline using the specified states
2016-06-03 12:19:53 +02:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipeline ) ) ;
2016-02-16 15:07:25 +01:00
}
void prepareUniformBuffers ( )
{
2016-05-11 20:39:22 +02:00
// Prepare and initialize a uniform buffer block containing shader uniforms
2016-08-09 23:52:06 +02:00
// Single uniforms like in OpenGL are no longer present in Vulkan. All Shader uniforms are passed via uniform buffer blocks
2016-02-16 15:07:25 +01:00
VkMemoryRequirements memReqs ;
// Vertex shader uniform buffer block
VkBufferCreateInfo bufferInfo = { } ;
VkMemoryAllocateInfo allocInfo = { } ;
allocInfo . sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO ;
2016-08-09 23:52:06 +02:00
allocInfo . pNext = nullptr ;
2016-02-16 15:07:25 +01:00
allocInfo . allocationSize = 0 ;
allocInfo . memoryTypeIndex = 0 ;
bufferInfo . sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ;
bufferInfo . size = sizeof ( uboVS ) ;
2016-08-09 23:52:06 +02:00
// This buffer will be used as a uniform buffer
2016-02-16 15:07:25 +01:00
bufferInfo . usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ;
// Create a new buffer
2016-12-24 12:43:37 +01:00
VK_CHECK_RESULT ( vkCreateBuffer ( device , & bufferInfo , nullptr , & uniformBufferVS . buffer ) ) ;
2016-02-16 15:07:25 +01:00
// Get memory requirements including size, alignment and memory type
2016-12-24 12:43:37 +01:00
vkGetBufferMemoryRequirements ( device , uniformBufferVS . buffer , & memReqs ) ;
2016-02-16 15:07:25 +01:00
allocInfo . allocationSize = memReqs . size ;
2016-05-11 20:39:22 +02:00
// Get the memory type index that supports host visibile memory access
2016-08-09 23:52:06 +02:00
// Most implementations offer multiple memory types and selecting the correct one to allocate memory from is crucial
// We also want the buffer to be host coherent so we don't have to flush (or sync after every update.
// Note: This may affect performance so you might not want to do this in a real world application that updates buffers on a regular base
2016-07-18 20:32:25 +02:00
allocInfo . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
2016-02-16 15:07:25 +01:00
// Allocate memory for the uniform buffer
2016-12-24 12:43:37 +01:00
VK_CHECK_RESULT ( vkAllocateMemory ( device , & allocInfo , nullptr , & ( uniformBufferVS . memory ) ) ) ;
2016-02-16 15:07:25 +01:00
// Bind memory to buffer
2016-12-24 12:43:37 +01:00
VK_CHECK_RESULT ( vkBindBufferMemory ( device , uniformBufferVS . buffer , uniformBufferVS . memory , 0 ) ) ;
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Store information in the uniform's descriptor that is used by the descriptor set
2016-12-24 12:43:37 +01:00
uniformBufferVS . descriptor . buffer = uniformBufferVS . buffer ;
uniformBufferVS . descriptor . offset = 0 ;
uniformBufferVS . descriptor . range = sizeof ( uboVS ) ;
2016-02-16 15:07:25 +01:00
updateUniformBuffers ( ) ;
}
void updateUniformBuffers ( )
{
// Update matrices
2016-03-06 17:33:50 +01:00
uboVS . projectionMatrix = glm : : perspective ( glm : : radians ( 60.0f ) , ( float ) width / ( float ) height , 0.1f , 256.0f ) ;
2016-02-16 15:07:25 +01:00
uboVS . viewMatrix = glm : : translate ( glm : : mat4 ( ) , glm : : vec3 ( 0.0f , 0.0f , zoom ) ) ;
uboVS . modelMatrix = glm : : mat4 ( ) ;
2016-03-06 17:33:50 +01:00
uboVS . modelMatrix = glm : : rotate ( uboVS . modelMatrix , glm : : radians ( rotation . x ) , glm : : vec3 ( 1.0f , 0.0f , 0.0f ) ) ;
uboVS . modelMatrix = glm : : rotate ( uboVS . modelMatrix , glm : : radians ( rotation . y ) , glm : : vec3 ( 0.0f , 1.0f , 0.0f ) ) ;
uboVS . modelMatrix = glm : : rotate ( uboVS . modelMatrix , glm : : radians ( rotation . z ) , glm : : vec3 ( 0.0f , 0.0f , 1.0f ) ) ;
2016-02-16 15:07:25 +01:00
// Map uniform buffer and update it
uint8_t * pData ;
2016-12-24 12:43:37 +01:00
VK_CHECK_RESULT ( vkMapMemory ( device , uniformBufferVS . memory , 0 , sizeof ( uboVS ) , 0 , ( void * * ) & pData ) ) ;
2016-02-16 15:07:25 +01:00
memcpy ( pData , & uboVS , sizeof ( uboVS ) ) ;
2016-08-09 23:52:06 +02:00
// Unmap after data has been copied
// Note: Since we requested a host coherent memory type for the uniform buffer, the write is instantly visible to the GPU
2016-12-24 12:43:37 +01:00
vkUnmapMemory ( device , uniformBufferVS . memory ) ;
2016-02-16 15:07:25 +01:00
}
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
2016-08-09 23:52:06 +02:00
prepareSynchronizationPrimitives ( ) ;
2016-03-17 22:32:15 +01:00
prepareVertices ( USE_STAGING ) ;
2016-02-16 15:07:25 +01:00
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
}
virtual void viewChanged ( )
{
2016-08-09 23:52:06 +02:00
// This function is called by the base example class each time the view is changed by user input
2016-02-16 15:07:25 +01:00
updateUniformBuffers ( ) ;
}
} ;
2017-01-23 13:54:23 +01:00
// OS specific macros for the example main entry points
// Most of the code base is shared for the different supported operating systems, but stuff like message handling diffes
2016-02-16 15:07:25 +01:00
2016-03-21 22:41:27 +01:00
# if defined(_WIN32)
2017-01-23 13:54:23 +01:00
// Windows entry point
VulkanExample * vulkanExample ;
2016-02-16 15:07:25 +01:00
LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
2017-01-23 13:54:23 +01:00
if ( vulkanExample ! = NULL )
2016-02-16 15:07:25 +01:00
{
vulkanExample - > handleMessages ( hWnd , uMsg , wParam , lParam ) ;
}
return ( DefWindowProc ( hWnd , uMsg , wParam , lParam ) ) ;
}
2017-01-23 13:54:23 +01:00
int APIENTRY WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR pCmdLine , int nCmdShow )
2016-02-16 15:07:25 +01:00
{
2017-01-23 13:54:23 +01:00
for ( size_t i = 0 ; i < __argc ; i + + ) { VulkanExample : : args . push_back ( __argv [ i ] ) ; } ;
vulkanExample = new VulkanExample ( ) ;
vulkanExample - > initVulkan ( ) ;
vulkanExample - > setupWindow ( hInstance , WndProc ) ;
vulkanExample - > initSwapchain ( ) ;
vulkanExample - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
2016-02-16 15:07:25 +01:00
}
2016-03-20 20:04:59 +01:00
# elif defined(__ANDROID__)
// Android entry point
2017-01-23 13:54:23 +01:00
// A note on app_dummy(): This is required as the compiler may otherwise remove the main entry point of the application
VulkanExample * vulkanExample ;
2016-03-20 20:04:59 +01:00
void android_main ( android_app * state )
2016-02-16 15:07:25 +01:00
{
2016-03-20 20:04:59 +01:00
app_dummy ( ) ;
2016-02-16 15:07:25 +01:00
vulkanExample = new VulkanExample ( ) ;
2016-03-20 20:04:59 +01:00
state - > userData = vulkanExample ;
state - > onAppCmd = VulkanExample : : handleAppCommand ;
2016-03-20 21:46:49 +01:00
state - > onInputEvent = VulkanExample : : handleAppInput ;
2017-01-23 13:30:28 +01:00
androidApp = state ;
2017-01-23 13:54:23 +01:00
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
}
# elif defined(_DIRECT2DISPLAY)
// Linux entry point with direct to display wsi
// Direct to Displays (D2D) is used on embedded platforms
VulkanExample * vulkanExample ;
static void handleEvent ( )
{
}
int main ( const int argc , const char * argv [ ] )
{
for ( size_t i = 0 ; i < argc ; i + + ) { VulkanExample : : args . push_back ( argv [ i ] ) ; } ;
vulkanExample = new VulkanExample ( ) ;
2016-12-14 08:34:45 +01:00
vulkanExample - > initVulkan ( ) ;
2016-02-16 15:07:25 +01:00
vulkanExample - > initSwapchain ( ) ;
vulkanExample - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
2016-08-14 00:11:41 -07:00
}
2017-02-02 08:54:56 +00:00
# elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
VulkanExample * vulkanExample ;
int main ( const int argc , const char * argv [ ] )
{
for ( size_t i = 0 ; i < argc ; i + + ) { VulkanExample : : args . push_back ( argv [ i ] ) ; } ;
vulkanExample = new VulkanExample ( ) ;
vulkanExample - > initVulkan ( ) ;
vulkanExample - > setupWindow ( ) ;
vulkanExample - > initSwapchain ( ) ;
vulkanExample - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
}
2017-01-23 13:54:23 +01:00
# elif defined(__linux__)
// Linux entry point
VulkanExample * vulkanExample ;
static void handleEvent ( const xcb_generic_event_t * event )
{
if ( vulkanExample ! = NULL )
{
vulkanExample - > handleEvent ( event ) ;
}
}
int main ( const int argc , const char * argv [ ] )
{
for ( size_t i = 0 ; i < argc ; i + + ) { VulkanExample : : args . push_back ( argv [ i ] ) ; } ;
vulkanExample = new VulkanExample ( ) ;
vulkanExample - > initVulkan ( ) ;
vulkanExample - > setupWindow ( ) ;
vulkanExample - > initSwapchain ( ) ;
vulkanExample - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
}
2017-02-02 08:54:56 +00:00
# endif