2016-02-16 15:07:25 +01:00
/*
* Vulkan Example - Basic indexed triangle rendering
*
2016-08-09 23:52:06 +02:00
* Note :
2021-12-30 22:58:53 -05:00
* This is a " pedal to the metal " example to show off how to get Vulkan up and displaying something
2016-02-16 15:07:25 +01:00
* 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 . )
*
2023-07-16 19:25:55 +02:00
* Copyright ( C ) 2016 - 2023 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"
2023-07-16 19:25:55 +02:00
// We want to keep GPU and CPU busy. To do that we may start building a new command buffer while the previous one is still being executed
// This number defines how many frames may be worked on simultaneously at once
2023-12-26 18:49:39 +01:00
// Increasing this number may improve performance but will also introduce additional latency
2023-07-16 19:25:55 +02:00
# define MAX_CONCURRENT_FRAMES 2
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 {
2023-12-26 18:49:39 +01:00
VkDeviceMemory memory { VK_NULL_HANDLE } ; // 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
2020-02-07 18:28:20 +01:00
struct {
2023-12-26 18:49:39 +01:00
VkDeviceMemory memory { VK_NULL_HANDLE } ;
2020-02-07 18:28:20 +01:00
VkBuffer buffer ;
2023-12-26 18:49:39 +01:00
uint32_t count { 0 } ;
2016-02-16 15:07:25 +01:00
} indices ;
2016-12-24 12:43:37 +01:00
// Uniform buffer block object
2023-07-16 19:25:55 +02:00
struct UniformBuffer {
2020-02-07 18:28:20 +01:00
VkDeviceMemory memory ;
VkBuffer buffer ;
2023-07-16 19:25:55 +02:00
// The descriptor set stores the resources bound to the binding points in a shader
// It connects the binding points of the different shaders with the buffers and images used for those bindings
VkDescriptorSet descriptorSet ;
// We keep a pointer to the mapped buffer, so we can easily update it's contents via a memcpy
uint8_t * mapped { nullptr } ;
} ;
// We use one UBO per frame, so we can have a frame overlap and make sure that uniforms aren't updated while still in use
std : : array < UniformBuffer , MAX_CONCURRENT_FRAMES > uniformBuffers ;
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)
2023-07-16 19:25:55 +02:00
struct ShaderData {
2016-02-16 15:07:25 +01:00
glm : : mat4 projectionMatrix ;
glm : : mat4 modelMatrix ;
glm : : mat4 viewMatrix ;
2023-07-16 19:25:55 +02:00
} ;
2016-02-16 15:07:25 +01:00
2020-08-08 18:13:29 +02:00
// The pipeline layout is used by a pipeline to access the descriptor sets
2016-08-09 23:52:06 +02:00
// 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
2023-12-26 18:49:39 +01:00
VkPipelineLayout pipelineLayout { VK_NULL_HANDLE } ;
2016-08-09 23:52:06 +02:00
// 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)
2021-12-30 22:58:53 -05:00
// Even though this adds a new dimension of planning ahead, it's a great opportunity for performance optimizations by the driver
2023-12-26 18:49:39 +01:00
VkPipeline pipeline { VK_NULL_HANDLE } ;
2016-06-03 12:19:53 +02:00
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
2023-12-26 18:49:39 +01:00
VkDescriptorSetLayout descriptorSetLayout { VK_NULL_HANDLE } ;
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.
2023-07-16 19:25:55 +02:00
// Semaphores are used to coordinate operations within the graphics queue and ensure correct command ordering
2023-12-26 18:49:39 +01:00
std : : array < VkSemaphore , MAX_CONCURRENT_FRAMES > presentCompleteSemaphores { } ;
std : : array < VkSemaphore , MAX_CONCURRENT_FRAMES > renderCompleteSemaphores { } ;
2023-07-16 19:25:55 +02:00
2023-12-26 18:49:39 +01:00
VkCommandPool commandPool { VK_NULL_HANDLE } ;
std : : array < VkCommandBuffer , MAX_CONCURRENT_FRAMES > commandBuffers { } ;
std : : array < VkFence , MAX_CONCURRENT_FRAMES > waitFences { } ;
2016-08-09 23:52:06 +02:00
2023-12-26 18:49:39 +01:00
// To select the correct sync objects, we need to keep track of the current frame
uint32_t currentFrame { 0 } ;
2016-03-06 12:16:04 +01:00
2023-12-30 13:15:37 +01:00
VulkanExample ( ) : VulkanExampleBase ( )
2016-02-16 15:07:25 +01:00
{
title = " Vulkan Example - Basic indexed triangle " ;
2023-07-16 19:25:55 +02:00
// To keep things simple, we don't use the UI overlay from the framework
2021-10-24 13:19:05 +02:00
settings . overlay = false ;
2020-04-22 20:58:24 +02:00
// Setup a default look-at camera
camera . type = Camera : : CameraType : : lookat ;
camera . setPosition ( glm : : vec3 ( 0.0f , 0.0f , - 2.5f ) ) ;
camera . setRotation ( glm : : vec3 ( 0.0f ) ) ;
camera . setPerspective ( 60.0f , ( float ) width / ( float ) height , 1.0f , 256.0f ) ;
2016-02-16 15:07:25 +01:00
// Values not set here are initialized in the base class constructor
}
~ VulkanExample ( )
{
2020-05-29 16:08:53 +01:00
// 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
2023-11-02 07:48:45 +01:00
vkDestroyCommandPool ( device , commandPool , nullptr ) ;
2023-12-26 18:49:39 +01:00
for ( uint32_t i = 0 ; i < MAX_CONCURRENT_FRAMES ; i + + ) {
2023-07-16 19:25:55 +02:00
vkDestroyFence ( device , waitFences [ i ] , nullptr ) ;
vkDestroySemaphore ( device , presentCompleteSemaphores [ i ] , nullptr ) ;
vkDestroySemaphore ( device , renderCompleteSemaphores [ i ] , nullptr ) ;
vkDestroyBuffer ( device , uniformBuffers [ i ] . buffer , nullptr ) ;
vkFreeMemory ( device , uniformBuffers [ i ] . memory , nullptr ) ;
2016-08-09 23:52:06 +02:00
}
2016-02-16 15:07:25 +01:00
}
2020-08-08 18:13:29 +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 visible)
// Upon success it will return the index of the memory type that fits our requested memory properties
2016-07-18 20:32:25 +02:00
// This is necessary as implementations can offer an arbitrary number of memory types with different
2020-05-29 16:08:53 +01:00
// memory properties.
2023-07-16 19:25:55 +02:00
// You can check https://vulkan.gpuinfo.org/ for details on different memory configurations
2016-07-18 20:32:25 +02:00
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 )
2020-02-07 18:28:20 +01:00
{
2016-07-18 20:32:25 +02:00
return i ;
}
}
typeBits > > = 1 ;
}
throw " Could not find a suitable memory type! " ;
}
2023-12-26 18:49:39 +01:00
// Create the per-frame (in flight) sVulkan synchronization primitives used in this example
2023-07-16 19:25:55 +02:00
void createSynchronizationPrimitives ( )
2016-08-09 23:52:06 +02:00
{
2023-12-26 18:49:39 +01:00
// Semaphores are used for correct command ordering within a queue
VkSemaphoreCreateInfo semaphoreCI { } ;
semaphoreCI . sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO ;
// Fences are used to check draw command buffer completion on the host
VkFenceCreateInfo fenceCI { } ;
fenceCI . sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO ;
// Create the fences in signaled state (so we don't wait on first render of each command buffer)
fenceCI . flags = VK_FENCE_CREATE_SIGNALED_BIT ;
for ( uint32_t i = 0 ; i < MAX_CONCURRENT_FRAMES ; i + + ) {
2023-07-16 19:25:55 +02:00
// Semaphore used to ensure that image presentation is complete before starting to submit again
VK_CHECK_RESULT ( vkCreateSemaphore ( device , & semaphoreCI , nullptr , & presentCompleteSemaphores [ i ] ) ) ;
// Semaphore used to ensure that all commands submitted have been finished before submitting the image to the queue
VK_CHECK_RESULT ( vkCreateSemaphore ( device , & semaphoreCI , nullptr , & renderCompleteSemaphores [ i ] ) ) ;
2023-12-26 18:49:39 +01:00
// Fence used to ensure that command buffer has completed exection before using it again
2023-07-16 19:25:55 +02:00
VK_CHECK_RESULT ( vkCreateFence ( device , & fenceCI , nullptr , & waitFences [ i ] ) ) ;
2016-06-03 13:15:55 +02:00
}
}
2023-07-16 19:25:55 +02:00
void createCommandBuffers ( )
2016-02-16 15:07:25 +01:00
{
2023-07-16 19:25:55 +02:00
// All command buffers are allocated from a command pool
VkCommandPoolCreateInfo commandPoolCI { } ;
commandPoolCI . sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO ;
commandPoolCI . queueFamilyIndex = swapChain . queueNodeIndex ;
commandPoolCI . flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT ;
VK_CHECK_RESULT ( vkCreateCommandPool ( device , & commandPoolCI , nullptr , & commandPool ) ) ;
// Allocate one command buffer per max. concurrent frame from above pool
VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks : : initializers : : commandBufferAllocateInfo ( commandPool , VK_COMMAND_BUFFER_LEVEL_PRIMARY , MAX_CONCURRENT_FRAMES ) ;
VK_CHECK_RESULT ( vkAllocateCommandBuffers ( device , & cmdBufAllocateInfo , commandBuffers . data ( ) ) ) ;
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
2023-07-16 19:25:55 +02:00
void createVertexBuffer ( )
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:
2020-08-09 13:16:35 +02:00
// This is a very complex topic and while it's fine for an example application to small individual memory allocations that is not
// what should be done a real-world application, where you should allocate large chunks of memory at once instead.
2016-08-09 23:52:06 +02:00
2016-02-16 15:07:25 +01:00
// Setup vertices
2023-07-17 20:32:27 +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
2023-07-17 20:32:27 +02:00
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
2023-07-17 20:32:27 +02:00
VkMemoryAllocateInfo memAlloc { } ;
2016-02-16 15:07:25 +01:00
memAlloc . sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO ;
VkMemoryRequirements memReqs ;
2023-07-16 19:25:55 +02:00
// 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
// - Delete the host visible (staging) buffer
// - Use the device local buffers for rendering
//
// Note: On unified memory architectures where host (CPU) and GPU share the same memory, staging is not necessary
// To keep this sample easy to follow, there is no check for that in place
struct StagingBuffer {
VkDeviceMemory memory ;
VkBuffer buffer ;
} ;
2016-02-16 15:07:25 +01:00
2023-07-16 19:25:55 +02:00
struct {
StagingBuffer vertices ;
StagingBuffer indices ;
} stagingBuffers ;
void * data ;
// Vertex buffer
VkBufferCreateInfo vertexBufferInfoCI { } ;
vertexBufferInfoCI . sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ;
vertexBufferInfoCI . size = vertexBufferSize ;
// Buffer is used as the copy source
vertexBufferInfoCI . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
// Create a host-visible buffer to copy the vertex data to (staging buffer)
VK_CHECK_RESULT ( vkCreateBuffer ( device , & vertexBufferInfoCI , nullptr , & stagingBuffers . vertices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , stagingBuffers . vertices . buffer , & memReqs ) ;
memAlloc . allocationSize = memReqs . size ;
// 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 ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & stagingBuffers . vertices . memory ) ) ;
// Map and copy
VK_CHECK_RESULT ( vkMapMemory ( device , stagingBuffers . vertices . memory , 0 , memAlloc . allocationSize , 0 , & data ) ) ;
memcpy ( data , vertexBuffer . data ( ) , vertexBufferSize ) ;
vkUnmapMemory ( device , stagingBuffers . vertices . memory ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , stagingBuffers . vertices . buffer , stagingBuffers . vertices . memory , 0 ) ) ;
// Create a device local buffer to which the (host local) vertex data will be copied and which will be used for rendering
vertexBufferInfoCI . usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT ;
VK_CHECK_RESULT ( vkCreateBuffer ( device , & vertexBufferInfoCI , nullptr , & vertices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , vertices . buffer , & memReqs ) ;
memAlloc . allocationSize = memReqs . size ;
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & vertices . memory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , vertices . buffer , vertices . memory , 0 ) ) ;
// Index buffer
VkBufferCreateInfo indexbufferCI { } ;
indexbufferCI . sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ;
indexbufferCI . size = indexBufferSize ;
indexbufferCI . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
// Copy index data to a buffer visible to the host (staging buffer)
VK_CHECK_RESULT ( vkCreateBuffer ( device , & indexbufferCI , nullptr , & stagingBuffers . indices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , stagingBuffers . indices . buffer , & memReqs ) ;
memAlloc . allocationSize = memReqs . size ;
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & stagingBuffers . indices . memory ) ) ;
VK_CHECK_RESULT ( vkMapMemory ( device , stagingBuffers . indices . memory , 0 , indexBufferSize , 0 , & data ) ) ;
memcpy ( data , indexBuffer . data ( ) , indexBufferSize ) ;
vkUnmapMemory ( device , stagingBuffers . indices . memory ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , stagingBuffers . indices . buffer , stagingBuffers . indices . memory , 0 ) ) ;
// Create destination buffer with device only visibility
indexbufferCI . usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT ;
VK_CHECK_RESULT ( vkCreateBuffer ( device , & indexbufferCI , nullptr , & indices . buffer ) ) ;
vkGetBufferMemoryRequirements ( device , indices . buffer , & memReqs ) ;
memAlloc . allocationSize = memReqs . size ;
memAlloc . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device , & memAlloc , nullptr , & indices . memory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device , indices . buffer , indices . memory , 0 ) ) ;
// Buffer copies have to be submitted to a queue, so we need a command buffer for them
// Note: Some devices offer a dedicated transfer queue (with only the transfer bit set) that may be faster when doing lots of copies
VkCommandBuffer copyCmd ;
2023-07-17 20:32:27 +02:00
VkCommandBufferAllocateInfo cmdBufAllocateInfo { } ;
2023-07-16 19:25:55 +02:00
cmdBufAllocateInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO ;
cmdBufAllocateInfo . commandPool = commandPool ;
cmdBufAllocateInfo . level = VK_COMMAND_BUFFER_LEVEL_PRIMARY ;
cmdBufAllocateInfo . commandBufferCount = 1 ;
VK_CHECK_RESULT ( vkAllocateCommandBuffers ( device , & cmdBufAllocateInfo , & copyCmd ) ) ;
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
VK_CHECK_RESULT ( vkBeginCommandBuffer ( copyCmd , & cmdBufInfo ) ) ;
// Put buffer region copies into command buffer
2023-07-17 20:32:27 +02:00
VkBufferCopy copyRegion { } ;
2023-07-16 19:25:55 +02:00
// Vertex buffer
copyRegion . size = vertexBufferSize ;
vkCmdCopyBuffer ( copyCmd , stagingBuffers . vertices . buffer , vertices . buffer , 1 , & copyRegion ) ;
// Index buffer
copyRegion . size = indexBufferSize ;
vkCmdCopyBuffer ( copyCmd , stagingBuffers . indices . buffer , indices . buffer , 1 , & copyRegion ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( copyCmd ) ) ;
// Submit the command buffer to the queue to finish the copy
2023-07-17 20:32:27 +02:00
VkSubmitInfo submitInfo { } ;
2023-07-16 19:25:55 +02:00
submitInfo . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & copyCmd ;
// Create fence to ensure that the command buffer has finished executing
VkFenceCreateInfo fenceCI { } ;
fenceCI . sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO ;
fenceCI . flags = 0 ;
VkFence fence ;
VK_CHECK_RESULT ( vkCreateFence ( device , & fenceCI , 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 ) ;
vkFreeCommandBuffers ( device , commandPool , 1 , & copyCmd ) ;
// Destroy staging buffers
// Note: Staging buffer must not be deleted before the copies have been submitted and executed
vkDestroyBuffer ( device , stagingBuffers . vertices . buffer , nullptr ) ;
vkFreeMemory ( device , stagingBuffers . vertices . memory , nullptr ) ;
vkDestroyBuffer ( device , stagingBuffers . indices . buffer , nullptr ) ;
vkFreeMemory ( device , stagingBuffers . indices . memory , nullptr ) ;
2016-02-16 15:07:25 +01:00
}
2023-07-16 19:25:55 +02:00
// Descriptors are allocated from a pool, that tells the implementation how many and what types of descriptors we are going to use (at maximum)
void createDescriptorPool ( )
2016-02-16 15:07:25 +01:00
{
// We need to tell the API the number of max. requested descriptors per type
2023-07-16 19:25:55 +02:00
VkDescriptorPoolSize descriptorTypeCounts [ 1 ] ;
// This example only one descriptor type (uniform buffer)
descriptorTypeCounts [ 0 ] . type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
// We have one buffer (and as such descriptor) per frame
descriptorTypeCounts [ 0 ] . descriptorCount = MAX_CONCURRENT_FRAMES ;
2016-02-16 15:07:25 +01:00
// 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
2023-07-16 19:25:55 +02:00
VkDescriptorPoolCreateInfo descriptorPoolCI { } ;
descriptorPoolCI . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO ;
descriptorPoolCI . pNext = nullptr ;
descriptorPoolCI . poolSizeCount = 1 ;
descriptorPoolCI . pPoolSizes = descriptorTypeCounts ;
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)
2023-07-16 19:25:55 +02:00
// Our sample will create one set per uniform buffer per frame
descriptorPoolCI . maxSets = MAX_CONCURRENT_FRAMES ;
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolCI , nullptr , & descriptorPool ) ) ;
2016-02-16 15:07:25 +01:00
}
2023-07-16 19:25:55 +02:00
// Descriptor set layouts define the interface between our application and the shader
// 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
void createDescriptorSetLayout ( )
2016-02-16 15:07:25 +01:00
{
2016-08-09 23:52:06 +02:00
// Binding 0: Uniform buffer (Vertex shader)
2023-07-16 19:25:55 +02:00
VkDescriptorSetLayoutBinding layoutBinding { } ;
2016-02-16 15:07:25 +01:00
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
2023-07-16 19:25:55 +02:00
VkDescriptorSetLayoutCreateInfo descriptorLayoutCI { } ;
descriptorLayoutCI . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO ;
descriptorLayoutCI . pNext = nullptr ;
descriptorLayoutCI . bindingCount = 1 ;
descriptorLayoutCI . pBindings = & layoutBinding ;
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayoutCI , 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
2023-07-16 19:25:55 +02:00
VkPipelineLayoutCreateInfo pipelineLayoutCI { } ;
pipelineLayoutCI . sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO ;
pipelineLayoutCI . pNext = nullptr ;
pipelineLayoutCI . setLayoutCount = 1 ;
pipelineLayoutCI . pSetLayouts = & descriptorSetLayout ;
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pipelineLayoutCI , nullptr , & pipelineLayout ) ) ;
2016-02-16 15:07:25 +01:00
}
2023-07-16 19:25:55 +02:00
// Shaders access data using descriptor sets that "point" at our uniform buffers
// The descriptor sets make use of the descriptor set layouts created above
void createDescriptorSets ( )
2016-02-16 15:07:25 +01:00
{
2023-07-16 19:25:55 +02:00
// Allocate one descriptor set per frame from the global descriptor pool
for ( uint32_t i = 0 ; i < MAX_CONCURRENT_FRAMES ; i + + ) {
2023-07-17 20:32:27 +02:00
VkDescriptorSetAllocateInfo allocInfo { } ;
2023-07-16 19:25:55 +02:00
allocInfo . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO ;
allocInfo . descriptorPool = descriptorPool ;
allocInfo . descriptorSetCount = 1 ;
allocInfo . pSetLayouts = & descriptorSetLayout ;
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & uniformBuffers [ i ] . 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
2023-07-17 20:32:27 +02:00
VkWriteDescriptorSet writeDescriptorSet { } ;
2023-07-16 19:25:55 +02:00
// The buffer's information is passed using a descriptor info structure
VkDescriptorBufferInfo bufferInfo { } ;
bufferInfo . buffer = uniformBuffers [ i ] . buffer ;
bufferInfo . range = sizeof ( ShaderData ) ;
// Binding 0 : Uniform buffer
writeDescriptorSet . sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ;
writeDescriptorSet . dstSet = uniformBuffers [ i ] . descriptorSet ;
writeDescriptorSet . descriptorCount = 1 ;
writeDescriptorSet . descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
writeDescriptorSet . pBufferInfo = & bufferInfo ;
writeDescriptorSet . dstBinding = 0 ;
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
2023-07-16 19:25:55 +02:00
VkImageCreateInfo imageCI { } ;
imageCI . sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO ;
imageCI . imageType = VK_IMAGE_TYPE_2D ;
imageCI . format = depthFormat ;
2016-06-03 13:15:55 +02:00
// Use example's height and width
2023-07-16 19:25:55 +02:00
imageCI . extent = { width , height , 1 } ;
imageCI . mipLevels = 1 ;
imageCI . arrayLayers = 1 ;
imageCI . samples = VK_SAMPLE_COUNT_1_BIT ;
imageCI . tiling = VK_IMAGE_TILING_OPTIMAL ;
imageCI . usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ;
imageCI . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
VK_CHECK_RESULT ( vkCreateImage ( device , & imageCI , nullptr , & depthStencil . image ) ) ;
2016-06-03 13:15:55 +02:00
// Allocate memory for the image (device local) and bind it to our image
2023-07-16 19:25:55 +02:00
VkMemoryAllocateInfo memAlloc { } ;
2016-06-03 13:15:55 +02:00
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)
2023-07-16 19:25:55 +02:00
VkImageViewCreateInfo depthStencilViewCI { } ;
depthStencilViewCI . sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO ;
depthStencilViewCI . viewType = VK_IMAGE_VIEW_TYPE_2D ;
depthStencilViewCI . format = depthFormat ;
depthStencilViewCI . subresourceRange = { } ;
depthStencilViewCI . subresourceRange . aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT ;
2022-03-06 18:36:11 +00:00
// Stencil aspect should only be set on depth + stencil formats (VK_FORMAT_D16_UNORM_S8_UINT..VK_FORMAT_D32_SFLOAT_S8_UINT)
2023-07-16 19:25:55 +02:00
if ( depthFormat > = VK_FORMAT_D16_UNORM_S8_UINT ) {
depthStencilViewCI . subresourceRange . aspectMask | = VK_IMAGE_ASPECT_STENCIL_BIT ;
}
depthStencilViewCI . subresourceRange . baseMipLevel = 0 ;
depthStencilViewCI . subresourceRange . levelCount = 1 ;
depthStencilViewCI . subresourceRange . baseArrayLayer = 0 ;
depthStencilViewCI . subresourceRange . layerCount = 1 ;
depthStencilViewCI . image = depthStencil . image ;
VK_CHECK_RESULT ( vkCreateImageView ( device , & depthStencilViewCI , nullptr , & depthStencil . view ) ) ;
2016-06-03 13:15:55 +02:00
}
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 + + )
{
2020-02-07 18:28:20 +01:00
std : : array < VkImageView , 2 > attachments ;
2023-07-16 19:25:55 +02:00
// Color attachment is the view of the swapchain image
attachments [ 0 ] = swapChain . buffers [ i ] . view ;
// Depth/Stencil attachment is the same for all frame buffers due to how depth works with current GPUs
attachments [ 1 ] = depthStencil . view ;
2016-06-02 20:58:25 +02:00
2023-07-16 19:25:55 +02:00
VkFramebufferCreateInfo frameBufferCI { } ;
frameBufferCI . sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO ;
2016-08-09 23:52:06 +02:00
// All frame buffers use the same renderpass setup
2023-07-16 19:25:55 +02:00
frameBufferCI . renderPass = renderPass ;
frameBufferCI . attachmentCount = static_cast < uint32_t > ( attachments . size ( ) ) ;
frameBufferCI . pAttachments = attachments . data ( ) ;
frameBufferCI . width = width ;
frameBufferCI . height = height ;
frameBufferCI . layers = 1 ;
2016-06-02 20:58:25 +02:00
// Create the framebuffer
2023-07-16 19:25:55 +02:00
VK_CHECK_RESULT ( vkCreateFramebuffer ( device , & frameBufferCI , nullptr , & frameBuffers [ i ] ) ) ;
2016-06-02 20:58:25 +02:00
}
}
2016-08-09 23:52:06 +02:00
// Render pass setup
2020-05-29 16:08:53 +01:00
// Render passes are a new concept in Vulkan. They describe the attachments used during rendering and may contain multiple subpasses with attachment dependencies
2016-08-09 23:52:06 +02:00
// 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
2023-07-17 20:32:27 +02:00
std : : array < VkAttachmentDescription , 2 > attachments { } ;
2016-06-02 20:58:25 +02:00
// Color attachment
2020-02-07 18:28:20 +01:00
attachments [ 0 ] . format = swapChain . colorFormat ; // Use the color format selected by the swapchain
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 its 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
2020-05-29 16:08:53 +01:00
// 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
2020-02-07 18:28:20 +01:00
attachments [ 1 ] . format = depthFormat ; // A proper depth format is selected in the example base
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
2016-08-09 23:52:06 +02:00
// Setup attachment references
2023-07-17 20:32:27 +02:00
VkAttachmentReference colorReference { } ;
2020-02-07 18:28:20 +01: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
2023-07-17 20:32:27 +02:00
VkAttachmentReference depthReference { } ;
2020-02-07 18:28:20 +01:00
depthReference . attachment = 1 ; // Attachment 1 is color
2020-08-09 13:16:35 +02:00
depthReference . layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ; // Attachment used as depth/stencil used during the subpass
2016-08-09 23:52:06 +02:00
// Setup a single subpass reference
2023-07-17 20:32:27 +02:00
VkSubpassDescription subpassDescription { } ;
2020-02-07 18:28:20 +01:00
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
2016-08-09 23:52:06 +02:00
// Setup subpass dependencies
2020-08-02 16:21:57 +08:00
// These will add the implicit attachment layout transitions specified by the attachment descriptions
2020-02-07 18:28:20 +01:00
// The actual usage layout is preserved through the layout specified in the attachment reference
2016-08-09 23:52:06 +02:00
// 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 ;
2023-01-01 09:14:25 +01:00
// Does the transition from final to initial layout for the depth an color attachments
// Depth attachment
dependencies [ 0 ] . srcSubpass = VK_SUBPASS_EXTERNAL ;
dependencies [ 0 ] . dstSubpass = 0 ;
dependencies [ 0 ] . srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ;
dependencies [ 0 ] . dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ;
dependencies [ 0 ] . srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ;
dependencies [ 0 ] . dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT ;
2023-01-05 13:28:40 +01:00
dependencies [ 0 ] . dependencyFlags = 0 ;
2023-01-01 09:14:25 +01:00
// Color attachment
dependencies [ 1 ] . srcSubpass = VK_SUBPASS_EXTERNAL ;
dependencies [ 1 ] . dstSubpass = 0 ;
dependencies [ 1 ] . srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
dependencies [ 1 ] . dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
dependencies [ 1 ] . srcAccessMask = 0 ;
dependencies [ 1 ] . dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT ;
2023-01-05 13:28:40 +01:00
dependencies [ 1 ] . dependencyFlags = 0 ;
2016-08-09 23:52:06 +02:00
// Create the actual renderpass
2023-07-16 19:25:55 +02:00
VkRenderPassCreateInfo renderPassCI { } ;
renderPassCI . sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO ;
renderPassCI . attachmentCount = static_cast < uint32_t > ( attachments . size ( ) ) ; // Number of attachments used by this render pass
renderPassCI . pAttachments = attachments . data ( ) ; // Descriptions of the attachments used by the render pass
renderPassCI . subpassCount = 1 ; // We only use one subpass in this example
renderPassCI . pSubpasses = & subpassDescription ; // Description of that subpass
renderPassCI . dependencyCount = static_cast < uint32_t > ( dependencies . size ( ) ) ; // Number of subpass dependencies
renderPassCI . pDependencies = dependencies . data ( ) ; // Subpass dependencies used by the render pass
VK_CHECK_RESULT ( vkCreateRenderPass ( device , & renderPassCI , nullptr , & renderPass ) ) ;
2016-06-02 20:58:25 +02:00
}
2020-01-12 12:54:41 +01:00
// Vulkan loads its shaders from an immediate binary representation called SPIR-V
2016-12-23 17:18:53 +01:00
// 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 ;
2023-07-16 19:25:55 +02:00
char * shaderCode { nullptr } ;
2016-12-23 17:18:53 +01:00
# 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
2023-07-16 19:25:55 +02:00
VkShaderModuleCreateInfo shaderModuleCI { } ;
shaderModuleCI . sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO ;
shaderModuleCI . codeSize = shaderSize ;
shaderModuleCI . pCode = ( uint32_t * ) shaderCode ;
2016-12-23 17:18:53 +01:00
VkShaderModule shaderModule ;
2023-07-16 19:25:55 +02:00
VK_CHECK_RESULT ( vkCreateShaderModule ( device , & shaderModuleCI , nullptr , & shaderModule ) ) ;
2016-12-23 17:18:53 +01:00
delete [ ] shaderCode ;
return shaderModule ;
}
else
{
std : : cerr < < " Error: Could not open shader file \" " < < filename < < " \" " < < std : : endl ;
return VK_NULL_HANDLE ;
}
}
2023-07-16 19:25:55 +02:00
void createPipelines ( )
2016-02-16 15:07:25 +01:00
{
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
2023-07-16 19:25:55 +02:00
VkGraphicsPipelineCreateInfo pipelineCI { } ;
pipelineCI . 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)
2023-07-16 19:25:55 +02:00
pipelineCI . layout = pipelineLayout ;
2016-02-16 15:07:25 +01:00
// Renderpass this pipeline is attached to
2023-07-16 19:25:55 +02:00
pipelineCI . renderPass = renderPass ;
2016-02-16 15:07:25 +01:00
2020-08-09 13:16:35 +02:00
// Construct the different 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)
2023-07-16 19:25:55 +02:00
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI { } ;
inputAssemblyStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO ;
inputAssemblyStateCI . topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST ;
2016-02-16 15:07:25 +01:00
// Rasterization state
2023-07-16 19:25:55 +02:00
VkPipelineRasterizationStateCreateInfo rasterizationStateCI { } ;
rasterizationStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO ;
rasterizationStateCI . polygonMode = VK_POLYGON_MODE_FILL ;
rasterizationStateCI . cullMode = VK_CULL_MODE_NONE ;
rasterizationStateCI . frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE ;
rasterizationStateCI . depthClampEnable = VK_FALSE ;
rasterizationStateCI . rasterizerDiscardEnable = VK_FALSE ;
rasterizationStateCI . depthBiasEnable = VK_FALSE ;
rasterizationStateCI . 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)
2020-02-07 18:28:20 +01:00
// We need one blend attachment state per color attachment (even if blending is not used)
2023-07-17 20:32:27 +02:00
VkPipelineColorBlendAttachmentState blendAttachmentState { } ;
blendAttachmentState . colorWriteMask = 0xf ;
blendAttachmentState . blendEnable = VK_FALSE ;
2023-07-16 19:25:55 +02:00
VkPipelineColorBlendStateCreateInfo colorBlendStateCI { } ;
colorBlendStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO ;
colorBlendStateCI . attachmentCount = 1 ;
2023-07-17 20:32:27 +02:00
colorBlendStateCI . pAttachments = & blendAttachmentState ;
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Viewport state sets the number of viewports and scissor used in this pipeline
2020-08-09 13:16:35 +02:00
// Note: This is actually overridden by the dynamic states (see below)
2023-07-16 19:25:55 +02:00
VkPipelineViewportStateCreateInfo viewportStateCI { } ;
viewportStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO ;
viewportStateCI . viewportCount = 1 ;
viewportStateCI . scissorCount = 1 ;
2016-02-16 15:07:25 +01:00
// 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 ) ;
2023-07-16 19:25:55 +02:00
VkPipelineDynamicStateCreateInfo dynamicStateCI { } ;
dynamicStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO ;
dynamicStateCI . pDynamicStates = dynamicStateEnables . data ( ) ;
dynamicStateCI . 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
2023-07-16 19:25:55 +02:00
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI { } ;
depthStencilStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO ;
depthStencilStateCI . depthTestEnable = VK_TRUE ;
depthStencilStateCI . depthWriteEnable = VK_TRUE ;
depthStencilStateCI . depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL ;
depthStencilStateCI . depthBoundsTestEnable = VK_FALSE ;
depthStencilStateCI . back . failOp = VK_STENCIL_OP_KEEP ;
depthStencilStateCI . back . passOp = VK_STENCIL_OP_KEEP ;
depthStencilStateCI . back . compareOp = VK_COMPARE_OP_ALWAYS ;
depthStencilStateCI . stencilTestEnable = VK_FALSE ;
depthStencilStateCI . front = depthStencilStateCI . back ;
2016-02-16 15:07:25 +01:00
// Multi sampling state
2020-08-09 13:16:35 +02:00
// This example does not make use of multi sampling (for anti-aliasing), the state must still be set and passed to the pipeline
2023-07-16 19:25:55 +02:00
VkPipelineMultisampleStateCreateInfo multisampleStateCI { } ;
multisampleStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO ;
multisampleStateCI . rasterizationSamples = VK_SAMPLE_COUNT_1_BIT ;
multisampleStateCI . pSampleMask = nullptr ;
2016-02-16 15:07:25 +01:00
2020-05-29 16:08:53 +01:00
// Vertex input descriptions
2017-01-21 22:05:21 +01:00
// 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)
2023-07-16 19:25:55 +02:00
VkVertexInputBindingDescription vertexInputBinding { } ;
2017-01-21 22:05:21 +01:00
vertexInputBinding . binding = 0 ;
vertexInputBinding . stride = sizeof ( Vertex ) ;
vertexInputBinding . inputRate = VK_VERTEX_INPUT_RATE_VERTEX ;
2020-08-09 13:16:35 +02:00
// Input attribute bindings describe shader attribute locations and memory layouts
2017-02-04 14:12:57 +01:00
std : : array < VkVertexInputAttributeDescription , 2 > vertexInputAttributs ;
2017-01-21 22:05:21 +01:00
// 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
2023-07-16 19:25:55 +02:00
VkPipelineVertexInputStateCreateInfo vertexInputStateCI { } ;
vertexInputStateCI . sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO ;
vertexInputStateCI . vertexBindingDescriptionCount = 1 ;
vertexInputStateCI . pVertexBindingDescriptions = & vertexInputBinding ;
vertexInputStateCI . vertexAttributeDescriptionCount = 2 ;
vertexInputStateCI . pVertexAttributeDescriptions = vertexInputAttributs . data ( ) ;
2017-01-21 22:05:21 +01:00
// 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
2020-05-29 16:08:53 +01:00
shaderStages [ 0 ] . module = loadSPIRVShader ( getShadersPath ( ) + " triangle/triangle.vert.spv " ) ;
2016-12-23 17:18:53 +01:00
// Main entry point for the shader
2017-02-04 14:12:57 +01:00
shaderStages [ 0 ] . pName = " main " ;
2016-12-23 17:18:53 +01:00
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
2020-05-29 16:08:53 +01:00
shaderStages [ 1 ] . module = loadSPIRVShader ( getShadersPath ( ) + " triangle/triangle.frag.spv " ) ;
2016-12-23 17:18:53 +01:00
// Main entry point for the shader
shaderStages [ 1 ] . pName = " main " ;
assert ( shaderStages [ 1 ] . module ! = VK_NULL_HANDLE ) ;
// Set pipeline shader stage info
2023-07-16 19:25:55 +02:00
pipelineCI . stageCount = static_cast < uint32_t > ( shaderStages . size ( ) ) ;
pipelineCI . pStages = shaderStages . data ( ) ;
2016-12-23 17:18:53 +01:00
// Assign the pipeline states to the pipeline creation info structure
2023-07-16 19:25:55 +02:00
pipelineCI . pVertexInputState = & vertexInputStateCI ;
pipelineCI . pInputAssemblyState = & inputAssemblyStateCI ;
pipelineCI . pRasterizationState = & rasterizationStateCI ;
pipelineCI . pColorBlendState = & colorBlendStateCI ;
pipelineCI . pMultisampleState = & multisampleStateCI ;
pipelineCI . pViewportState = & viewportStateCI ;
pipelineCI . pDepthStencilState = & depthStencilStateCI ;
pipelineCI . pDynamicState = & dynamicStateCI ;
2016-02-16 15:07:25 +01:00
2016-08-09 23:52:06 +02:00
// Create rendering pipeline using the specified states
2023-07-16 19:25:55 +02:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCI , nullptr , & pipeline ) ) ;
2017-02-04 14:12:57 +01:00
// Shader modules are no longer needed once the graphics pipeline has been created
vkDestroyShaderModule ( device , shaderStages [ 0 ] . module , nullptr ) ;
vkDestroyShaderModule ( device , shaderStages [ 1 ] . module , nullptr ) ;
2016-02-16 15:07:25 +01:00
}
2023-07-16 19:25:55 +02:00
void createUniformBuffers ( )
2016-02-16 15:07:25 +01:00
{
2023-07-16 19:25:55 +02:00
// Prepare and initialize the per-frame uniform buffer blocks 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
2023-07-17 20:32:27 +02:00
VkBufferCreateInfo bufferInfo { } ;
VkMemoryAllocateInfo allocInfo { } ;
2016-02-16 15:07:25 +01:00
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 ;
2023-07-16 19:25:55 +02:00
bufferInfo . size = sizeof ( ShaderData ) ;
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 ;
2023-07-16 19:25:55 +02:00
// Create the buffers
for ( uint32_t i = 0 ; i < MAX_CONCURRENT_FRAMES ; i + + ) {
VK_CHECK_RESULT ( vkCreateBuffer ( device , & bufferInfo , nullptr , & uniformBuffers [ i ] . buffer ) ) ;
// Get memory requirements including size, alignment and memory type
vkGetBufferMemoryRequirements ( device , uniformBuffers [ i ] . buffer , & memReqs ) ;
allocInfo . allocationSize = memReqs . size ;
// Get the memory type index that supports host visible memory access
// 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
allocInfo . memoryTypeIndex = getMemoryTypeIndex ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
// Allocate memory for the uniform buffer
VK_CHECK_RESULT ( vkAllocateMemory ( device , & allocInfo , nullptr , & ( uniformBuffers [ i ] . memory ) ) ) ;
// Bind memory to buffer
VK_CHECK_RESULT ( vkBindBufferMemory ( device , uniformBuffers [ i ] . buffer , uniformBuffers [ i ] . memory , 0 ) ) ;
// We map the buffer once, so we can update it without having to map it again
VK_CHECK_RESULT ( vkMapMemory ( device , uniformBuffers [ i ] . memory , 0 , sizeof ( ShaderData ) , 0 , ( void * * ) & uniformBuffers [ i ] . mapped ) ) ;
}
2016-02-16 15:07:25 +01:00
}
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
2023-07-16 19:25:55 +02:00
createSynchronizationPrimitives ( ) ;
createCommandBuffers ( ) ;
createVertexBuffer ( ) ;
createUniformBuffers ( ) ;
createDescriptorSetLayout ( ) ;
createDescriptorPool ( ) ;
createDescriptorSets ( ) ;
createPipelines ( ) ;
2016-02-16 15:07:25 +01:00
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
2023-07-16 19:25:55 +02:00
// Use a fence to wait until the command buffer has finished execution before using it again
vkWaitForFences ( device , 1 , & waitFences [ currentFrame ] , VK_TRUE , UINT64_MAX ) ;
2023-12-26 18:49:39 +01:00
VK_CHECK_RESULT ( vkResetFences ( device , 1 , & waitFences [ currentFrame ] ) ) ;
2023-07-16 19:25:55 +02:00
// Get the next swap chain image from the implementation
2023-12-26 18:49:39 +01:00
// Note that the implementation is free to return the images in any order, so we must use the acquire function and can't just cycle through the images/imageIndex on our own
2023-07-16 19:25:55 +02:00
uint32_t imageIndex ;
VkResult result = vkAcquireNextImageKHR ( device , swapChain . swapChain , UINT64_MAX , presentCompleteSemaphores [ currentFrame ] , VK_NULL_HANDLE , & imageIndex ) ;
if ( result = = VK_ERROR_OUT_OF_DATE_KHR ) {
windowResize ( ) ;
return ;
2023-12-26 18:49:39 +01:00
}
else if ( ( result ! = VK_SUCCESS ) & & ( result ! = VK_SUBOPTIMAL_KHR ) ) {
2023-07-16 19:25:55 +02:00
throw " Could not acquire the next swap chain image! " ;
}
// Update the uniform buffer for the next frame
ShaderData shaderData { } ;
shaderData . projectionMatrix = camera . matrices . perspective ;
shaderData . viewMatrix = camera . matrices . view ;
shaderData . modelMatrix = glm : : mat4 ( 1.0f ) ;
// Copy the current matrices to the current frame's uniform buffer
// Note: Since we requested a host coherent memory type for the uniform buffer, the write is instantly visible to the GPU
2023-12-26 18:49:39 +01:00
memcpy ( uniformBuffers [ currentFrame ] . mapped , & shaderData , sizeof ( ShaderData ) ) ;
2023-07-16 19:25:55 +02:00
// Build the command buffer
// Unlike in OpenGL all rendering commands are recorded into command buffers that are then submitted to the queue
// This allows to generate work upfront in a separate thread
// For basic command buffers (like in this sample), recording is so fast that there is no need to offload this
2023-12-26 18:49:39 +01:00
vkResetCommandBuffer ( commandBuffers [ currentFrame ] , 0 ) ;
2023-07-16 19:25:55 +02:00
2023-07-17 20:32:27 +02:00
VkCommandBufferBeginInfo cmdBufInfo { } ;
2023-07-16 19:25:55 +02:00
cmdBufInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO ;
// Set clear values for all framebuffer attachments with loadOp set to clear
// 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
VkClearValue clearValues [ 2 ] ;
clearValues [ 0 ] . color = { { 0.0f , 0.0f , 0.2f , 1.0f } } ;
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
2023-07-17 20:32:27 +02:00
VkRenderPassBeginInfo renderPassBeginInfo { } ;
2023-07-16 19:25:55 +02:00
renderPassBeginInfo . sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO ;
renderPassBeginInfo . pNext = nullptr ;
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 ;
renderPassBeginInfo . framebuffer = frameBuffers [ imageIndex ] ;
2023-12-26 18:49:39 +01:00
const VkCommandBuffer commandBuffer = commandBuffers [ currentFrame ] ;
VK_CHECK_RESULT ( vkBeginCommandBuffer ( commandBuffer , & cmdBufInfo ) ) ;
2023-07-16 19:25:55 +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
2023-12-26 18:49:39 +01:00
vkCmdBeginRenderPass ( commandBuffer , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
2023-07-16 19:25:55 +02:00
// Update dynamic viewport state
2023-07-17 20:32:27 +02:00
VkViewport viewport { } ;
2023-07-16 19:25:55 +02:00
viewport . height = ( float ) height ;
viewport . width = ( float ) width ;
viewport . minDepth = ( float ) 0.0f ;
viewport . maxDepth = ( float ) 1.0f ;
2023-12-26 18:49:39 +01:00
vkCmdSetViewport ( commandBuffer , 0 , 1 , & viewport ) ;
2023-07-16 19:25:55 +02:00
// Update dynamic scissor state
2023-07-17 20:32:27 +02:00
VkRect2D scissor { } ;
2023-07-16 19:25:55 +02:00
scissor . extent . width = width ;
scissor . extent . height = height ;
scissor . offset . x = 0 ;
scissor . offset . y = 0 ;
2023-12-26 18:49:39 +01:00
vkCmdSetScissor ( commandBuffer , 0 , 1 , & scissor ) ;
2023-07-16 19:25:55 +02:00
// Bind descriptor set for the currrent frame's uniform buffer, so the shader uses the data from that buffer for this draw
2023-12-26 18:49:39 +01:00
vkCmdBindDescriptorSets ( commandBuffer , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & uniformBuffers [ currentFrame ] . descriptorSet , 0 , nullptr ) ;
2023-07-16 19:25:55 +02:00
// Bind the rendering pipeline
// The pipeline (state object) contains all states of the rendering pipeline, binding it will set all the states specified at pipeline creation time
2023-12-26 18:49:39 +01:00
vkCmdBindPipeline ( commandBuffer , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline ) ;
2023-07-16 19:25:55 +02:00
// Bind triangle vertex buffer (contains position and colors)
2023-07-17 20:32:27 +02:00
VkDeviceSize offsets [ 1 ] { 0 } ;
2023-12-26 18:49:39 +01:00
vkCmdBindVertexBuffers ( commandBuffer , 0 , 1 , & vertices . buffer , offsets ) ;
2023-07-16 19:25:55 +02:00
// Bind triangle index buffer
2023-12-26 18:49:39 +01:00
vkCmdBindIndexBuffer ( commandBuffer , indices . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
2023-07-16 19:25:55 +02:00
// Draw indexed triangle
2023-12-26 18:49:39 +01:00
vkCmdDrawIndexed ( commandBuffer , indices . count , 1 , 0 , 0 , 1 ) ;
vkCmdEndRenderPass ( commandBuffer ) ;
2023-07-16 19:25:55 +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
2023-12-26 18:49:39 +01:00
VK_CHECK_RESULT ( vkEndCommandBuffer ( commandBuffer ) ) ;
2023-07-16 19:25:55 +02:00
// Submit the command buffer to the graphics queue
// Pipeline stage at which the queue submission will wait (via pWaitSemaphores)
VkPipelineStageFlags waitStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
// The submit info structure specifies a command buffer queue submission batch
2023-07-17 20:32:27 +02:00
VkSubmitInfo submitInfo { } ;
2023-07-16 19:25:55 +02:00
submitInfo . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
2023-12-26 18:49:39 +01:00
submitInfo . pWaitDstStageMask = & waitStageMask ; // Pointer to the list of pipeline stages that the semaphore waits will occur at
submitInfo . pCommandBuffers = & commandBuffer ; // Command buffers(s) to execute in this batch (submission)
submitInfo . commandBufferCount = 1 ; // We submit a single command buffer
2023-07-16 19:25:55 +02:00
// Semaphore to wait upon before the submitted command buffer starts executing
submitInfo . pWaitSemaphores = & presentCompleteSemaphores [ currentFrame ] ;
2023-12-26 18:49:39 +01:00
submitInfo . waitSemaphoreCount = 1 ;
2023-07-16 19:25:55 +02:00
// Semaphore to be signaled when command buffers have completed
submitInfo . pSignalSemaphores = & renderCompleteSemaphores [ currentFrame ] ;
2023-12-26 18:49:39 +01:00
submitInfo . signalSemaphoreCount = 1 ;
2023-07-16 19:25:55 +02:00
// Submit to the graphics queue passing a wait fence
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , waitFences [ currentFrame ] ) ) ;
// Present the current frame buffer to the swap chain
// 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
VkPresentInfoKHR presentInfo { } ;
presentInfo . sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR ;
presentInfo . waitSemaphoreCount = 1 ;
presentInfo . pWaitSemaphores = & renderCompleteSemaphores [ currentFrame ] ;
presentInfo . swapchainCount = 1 ;
presentInfo . pSwapchains = & swapChain . swapChain ;
presentInfo . pImageIndices = & imageIndex ;
result = vkQueuePresentKHR ( queue , & presentInfo ) ;
if ( ( result = = VK_ERROR_OUT_OF_DATE_KHR ) | | ( result = = VK_SUBOPTIMAL_KHR ) ) {
windowResize ( ) ;
}
else if ( result ! = VK_SUCCESS ) {
throw " Could not present the image to the swap chain! " ;
}
2023-12-26 18:49:39 +01:00
// Select the next frame to render to, based on the max. no. of concurrent frames
currentFrame = ( currentFrame + 1 ) % MAX_CONCURRENT_FRAMES ;
2016-02-16 15:07:25 +01:00
}
} ;
2023-07-17 20:32:27 +02:00
// OS specific main entry points
2020-08-09 13:16:35 +02:00
// Most of the code base is shared for the different supported operating systems, but stuff like message handling differs
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 - > 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
VulkanExample * vulkanExample ;
2016-03-20 20:04:59 +01:00
void android_main ( android_app * state )
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 - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
2016-08-14 00:11:41 -07:00
}
2020-09-13 10:12:33 +02:00
# elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
VulkanExample * vulkanExample ;
static void handleEvent ( const DFBWindowEvent * 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 - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
}
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 - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
}
2020-08-21 11:23:01 -03:00
# elif defined(__linux__) || defined(__FreeBSD__)
2017-01-23 13:54:23 +01:00
// Linux entry point
VulkanExample * vulkanExample ;
2021-04-27 13:52:27 +02:00
# if defined(VK_USE_PLATFORM_XCB_KHR)
2017-01-23 13:54:23 +01:00
static void handleEvent ( const xcb_generic_event_t * event )
{
if ( vulkanExample ! = NULL )
{
vulkanExample - > handleEvent ( event ) ;
}
}
2021-04-27 13:52:27 +02:00
# else
static void handleEvent ( )
{
}
# endif
2017-01-23 13:54:23 +01:00
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 - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
delete ( vulkanExample ) ;
return 0 ;
}
2020-09-12 03:19:28 +09:00
# elif (defined(VK_USE_PLATFORM_MACOS_MVK) && defined(VK_EXAMPLE_XCODE_GENERATED))
VulkanExample * vulkanExample ;
int main ( const int argc , const char * argv [ ] )
{
@ autoreleasepool
{
for ( size_t i = 0 ; i < argc ; i + + ) { VulkanExample : : args . push_back ( argv [ i ] ) ; } ;
vulkanExample = new VulkanExample ( ) ;
vulkanExample - > initVulkan ( ) ;
vulkanExample - > setupWindow ( nullptr ) ;
vulkanExample - > prepare ( ) ;
vulkanExample - > renderLoop ( ) ;
2022-08-01 16:52:06 -04:00
delete ( vulkanExample ) ;
2020-09-12 03:19:28 +09:00
}
return 0 ;
}
2023-09-01 11:12:08 -04:00
# elif defined(VK_USE_PLATFORM_SCREEN_QNX)
VULKAN_EXAMPLE_MAIN ( )
2017-02-02 08:54:56 +00:00
# endif