text: /*
  * Vulkan Example - Attraction based compute shader particle system
  *
  * Updated compute shader by Lukas Bergdoll (https://github.com/Voultapher)
  *
  * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
  *
  * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
  */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <assert.h>
  #include <vector>
  #include <random>
  
  #define GLM_FORCE_RADIANS
  #define GLM_FORCE_DEPTH_ZERO_TO_ONE
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  
  #include <vulkan/vulkan.h>
  #include "vulkanexamplebase.h"
  
  #define VERTEX_BUFFER_BIND_ID 0
  #define ENABLE_VALIDATION false
  #if defined(__ANDROID__)
  // Lower particle count on Android for performance reasons
  #define PARTICLE_COUNT 64 * 1024
  #else
  #define PARTICLE_COUNT 256 * 1024
  #endif
  
  class VulkanExample : public VulkanExampleBase
  {
  public:
  	float timer = 0.0f;
  	float animStart = 20.0f;
  	bool animate = true;
  
  	struct {
  		vkTools::VulkanTexture particle;
  		vkTools::VulkanTexture gradient;
  	} textures;
  
  	struct {
  		VkPipelineVertexInputStateCreateInfo inputState;
  		std::vector<VkVertexInputBindingDescription> bindingDescriptions;
  		std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
  	} vertices;
  
  	struct {
  		VkPipeline postCompute;
  		// Compute pipelines are separated from 
  		// graphics pipelines in Vulkan
  		VkPipeline compute;
  	} pipelines;
  
  	VkQueue computeQueue;
  	//VkCommandBuffer computeCmdBuffer;
  	VkPipelineLayout computePipelineLayout;
  	VkDescriptorSet computeDescriptorSet;
  	VkDescriptorSetLayout computeDescriptorSetLayout;
  
  	vkTools::UniformData computeStorageBuffer;
  
  	struct {
  		float deltaT;
  		float destX;
  		float destY;
  		int32_t particleCount = PARTICLE_COUNT;
  	} computeUbo;
  
  	struct {
  		struct {
  			vkTools::UniformData ubo;
  		} computeShader;
  	} uniformData;
  
  	struct Particle {
  		glm::vec2 pos;
  		glm::vec2 vel;
  		glm::vec4 gradientPos;
  	};
  
  	VkPipelineLayout pipelineLayout;
  	VkDescriptorSet descriptorSetPostCompute;
  	VkDescriptorSetLayout descriptorSetLayout;
  
  	VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
  	{
  		enableTextOverlay = true;
  		title = "Vulkan Example - Compute shader particle system";
  	}
  
  	~VulkanExample()
  	{
  		// Clean up used Vulkan resources 
  		// Note : Inherited destructor cleans up resources stored in base class
  
  		vkDestroyPipeline(device, pipelines.postCompute, nullptr);
  
  		vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
  		vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
  		vkDestroyBuffer(device, computeStorageBuffer.buffer, nullptr);
  		vkFreeMemory(device, computeStorageBuffer.memory, nullptr);
  
  		vkTools::destroyUniformData(device, &uniformData.computeShader.ubo);
  
  		vkDestroyPipelineLayout(device, computePipelineLayout, nullptr);
  		vkDestroyDescriptorSetLayout(device, computeDescriptorSetLayout, nullptr);
  		vkDestroyPipeline(device, pipelines.compute, nullptr);
  
  		textureLoader->destroyTexture(textures.particle);
  		textureLoader->destroyTexture(textures.gradient);
  	}
  
  	void loadTextures()
  	{
  		textureLoader->loadTexture(getAssetPath() + "textures/particle01_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, &textures.particle, false);
  		textureLoader->loadTexture(getAssetPath() + "textures/particle_gradient_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, &textures.gradient, false);
  	}
  
  	void buildCommandBuffers()
  	{
  		// Destroy command buffers if already present
  		if (!checkCommandBuffers())
  		{
  			destroyCommandBuffers();
  			createCommandBuffers();
  		}
  
  		VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
  
  		VkClearValue clearValues[2];
  		clearValues[0].color = defaultClearColor;
  		clearValues[1].depthStencil = { 1.0f, 0 };
  
  		VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo();
  		renderPassBeginInfo.renderPass = renderPass;
  		renderPassBeginInfo.renderArea.offset.x = 0;
  		renderPassBeginInfo.renderArea.offset.y = 0;
  		renderPassBeginInfo.renderArea.extent.width = width;
  		renderPassBeginInfo.renderArea.extent.height = height;
  		renderPassBeginInfo.clearValueCount = 2;
  		renderPassBeginInfo.pClearValues = clearValues;
  
  		for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
  		{
  			// Set target frame buffer
  			renderPassBeginInfo.framebuffer = frameBuffers[i];
  
  			VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
  
  			// Compute particle movement
  
  			// Add memory barrier to ensure that the (rendering) vertex shader operations have finished
  			// Required as the compute shader will overwrite the vertex buffer data
  			VkBufferMemoryBarrier bufferBarrier = vkTools::initializers::bufferMemoryBarrier();
  			// Vertex shader invocations have finished reading from the buffer
  			bufferBarrier.srcAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
  			// Compute shader buffer read and write
  			bufferBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
  			bufferBarrier.buffer = computeStorageBuffer.buffer;
  			bufferBarrier.size = computeStorageBuffer.descriptor.range;
  			bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  			bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  
  			vkCmdPipelineBarrier(
  				drawCmdBuffers[i],
  				VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
  				VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
  				VK_FLAGS_NONE,
  				0, nullptr,
  				1, &bufferBarrier,
  				0, nullptr);
  
  			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, pipelines.compute);
  			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &computeDescriptorSet, 0, 0);
  
  			// Dispatch the compute job
  			vkCmdDispatch(drawCmdBuffers[i], PARTICLE_COUNT / 16, 1, 1);
  
  			// Add memory barrier to ensure that compute shader has finished writing to the buffer
  			// Without this the (rendering) vertex shader may display incomplete results (partial data from last frame)
  			// Compute shader has finished writes to the buffer
  			bufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
  			// Vertex shader access (attribute binding)
  			bufferBarrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
  			bufferBarrier.buffer = computeStorageBuffer.buffer;
  			bufferBarrier.size = computeStorageBuffer.descriptor.range;
  			bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  			bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  
  			vkCmdPipelineBarrier(
  				drawCmdBuffers[i],
  				VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
  				VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
  				VK_FLAGS_NONE, 
  				0, nullptr,
  				1, &bufferBarrier,
  				0, nullptr);
  
  			// Draw the particle system using the update vertex buffer
  
  			vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  
  			VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
  			vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
  
  			VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0);
  			vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
  
  			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.postCompute);
  			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSetPostCompute, 0, NULL);
  
  			VkDeviceSize offsets[1] = { 0 };
  			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &computeStorageBuffer.buffer, offsets);
  			vkCmdDraw(drawCmdBuffers[i], PARTICLE_COUNT, 1, 0, 0);
  
  			vkCmdEndRenderPass(drawCmdBuffers[i]);
  
  			VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
  		}
  
  	}
  
  	// Setup and fill the compute shader storage buffers for
  	// vertex positions and velocities
  	void prepareStorageBuffers()
  	{
  
  		std::mt19937 rGenerator;
  		std::uniform_real_distribution<float> rDistribution(-1.0f, 1.0f);
  
  		// Initial particle positions
  		std::vector<Particle> particleBuffer(PARTICLE_COUNT);
  		for (auto& particle : particleBuffer)
  		{
  			particle.pos = glm::vec2(rDistribution(rGenerator), rDistribution(rGenerator));
  			particle.vel = glm::vec2(0.0f);
  			particle.gradientPos.x = particle.pos.x / 2.0f;
  		}
  
  		uint32_t storageBufferSize = particleBuffer.size() * sizeof(Particle);
  
  		// Staging
  		// SSBO is static, copy to device local memory 
  		// This results in better performance
  
  		struct {
  			VkDeviceMemory memory;
  			VkBuffer buffer;
  		} stagingBuffer;
  
  		VulkanExampleBase::createBuffer(
  			VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
  			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
  			storageBufferSize,
  			particleBuffer.data(),
  			&stagingBuffer.buffer,
  			&stagingBuffer.memory);
  
  		VulkanExampleBase::createBuffer(
  			VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
  			VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
  			storageBufferSize,
  			nullptr,
  			&computeStorageBuffer.buffer,
  			&computeStorageBuffer.memory);
  
  		// Copy to staging buffer
  		VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
  
  		VkBufferCopy copyRegion = {};
  		copyRegion.size = storageBufferSize;
  		vkCmdCopyBuffer(
  			copyCmd,
  			stagingBuffer.buffer,
  			computeStorageBuffer.buffer,
  			1,
  			&copyRegion);
  
  		VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true);
  
  		vkFreeMemory(device, stagingBuffer.memory, nullptr);
  		vkDestroyBuffer(device, stagingBuffer.buffer, nullptr);
  
  		computeStorageBuffer.descriptor.range = storageBufferSize;
  		computeStorageBuffer.descriptor.buffer = computeStorageBuffer.buffer;
  		computeStorageBuffer.descriptor.offset = 0;
  
  		// Binding description
  		vertices.bindingDescriptions.resize(1);
  		vertices.bindingDescriptions[0] =
  			vkTools::initializers::vertexInputBindingDescription(
  				VERTEX_BUFFER_BIND_ID,
  				sizeof(Particle),
  				VK_VERTEX_INPUT_RATE_VERTEX);
  
  		// Attribute descriptions
  		// Describes memory layout and shader positions
  		vertices.attributeDescriptions.resize(2);
  		// Location 0 : Position
  		vertices.attributeDescriptions[0] =
  			vkTools::initializers::vertexInputAttributeDescription(
  				VERTEX_BUFFER_BIND_ID,
  				0,
  				VK_FORMAT_R32G32_SFLOAT,
  				0);
  		// Location 1 : Gradient position
  		vertices.attributeDescriptions[1] =
  			vkTools::initializers::vertexInputAttributeDescription(
  				VERTEX_BUFFER_BIND_ID,
  				1,
  				VK_FORMAT_R32G32B32A32_SFLOAT,
  				4 * sizeof(float));
  
  		// Assign to vertex buffer
  		vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo();
  		vertices.inputState.vertexBindingDescriptionCount = vertices.bindingDescriptions.size();
  		vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data();
  		vertices.inputState.vertexAttributeDescriptionCount = vertices.attributeDescriptions.size();
  		vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data();
  	}
  
  	void setupDescriptorPool()
  	{
  		std::vector<VkDescriptorPoolSize> poolSizes =
  		{
  			vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
  			vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1),
  			vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2)
  		};
  
  		VkDescriptorPoolCreateInfo descriptorPoolInfo =
  			vkTools::initializers::descriptorPoolCreateInfo(
  				poolSizes.size(),
  				poolSizes.data(),
  				2);
  
  		VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
  	}
  
  	void setupDescriptorSetLayout()
  	{
  		std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings;
  		// Binding 0 : Particle color map
  		setLayoutBindings.push_back(vkTools::initializers::descriptorSetLayoutBinding(
  			VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
  			VK_SHADER_STAGE_FRAGMENT_BIT,
  			0));
  		// Binding 1 : Particle gradient ramp
  		setLayoutBindings.push_back(vkTools::initializers::descriptorSetLayoutBinding(
  			VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
  			VK_SHADER_STAGE_FRAGMENT_BIT,
  			1));
  
  		VkDescriptorSetLayoutCreateInfo descriptorLayout =
  			vkTools::initializers::descriptorSetLayoutCreateInfo(
  				setLayoutBindings.data(),
  				setLayoutBindings.size());
  
  		VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
  
  		VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
  			vkTools::initializers::pipelineLayoutCreateInfo(
  				&descriptorSetLayout,
  				1);
  
  		VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
  	}
  
  	void setupDescriptorSet()
  	{
  		VkDescriptorSetAllocateInfo allocInfo =
  			vkTools::initializers::descriptorSetAllocateInfo(
  				descriptorPool,
  				&descriptorSetLayout,
  				1);
  
  		VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSetPostCompute));
  
  		// Image descriptor for the color map texture
  		std::vector<VkDescriptorImageInfo> texDescriptors;
  		texDescriptors.push_back(vkTools::initializers::descriptorImageInfo(
  			textures.particle.sampler,
  			textures.particle.view,
  			VK_IMAGE_LAYOUT_GENERAL));
  		texDescriptors.push_back(vkTools::initializers::descriptorImageInfo(
  			textures.gradient.sampler,
  			textures.gradient.view,
  			VK_IMAGE_LAYOUT_GENERAL));
  
  		std::vector<VkWriteDescriptorSet> writeDescriptorSets;
  		// Binding 0 : Particle color map
  		writeDescriptorSets.push_back(vkTools::initializers::writeDescriptorSet(
  			descriptorSetPostCompute,
  			VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
  			0,
  			&texDescriptors[0]));
  		// Binding 1 : Particle gradient ramp
  		writeDescriptorSets.push_back(vkTools::initializers::writeDescriptorSet(
  			descriptorSetPostCompute,
  			VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
  			1,
  			&texDescriptors[1]));
  
  		vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
  	}
  
  	void preparePipelines()
  	{
  		VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
  			vkTools::initializers::pipelineInputAssemblyStateCreateInfo(
  				VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
  				0,
  				VK_FALSE);
  
  		VkPipelineRasterizationStateCreateInfo rasterizationState =
  			vkTools::initializers::pipelineRasterizationStateCreateInfo(
  				VK_POLYGON_MODE_FILL,
  				VK_CULL_MODE_NONE,
  				VK_FRONT_FACE_COUNTER_CLOCKWISE,
  				0);
  
  		VkPipelineColorBlendAttachmentState blendAttachmentState =
  			vkTools::initializers::pipelineColorBlendAttachmentState(
  				0xf,
  				VK_FALSE);
  
  		VkPipelineColorBlendStateCreateInfo colorBlendState =
  			vkTools::initializers::pipelineColorBlendStateCreateInfo(
  				1,
  				&blendAttachmentState);
  
  		VkPipelineDepthStencilStateCreateInfo depthStencilState =
  			vkTools::initializers::pipelineDepthStencilStateCreateInfo(
  				VK_FALSE,
  				VK_FALSE,
  				VK_COMPARE_OP_ALWAYS);
  
  		VkPipelineViewportStateCreateInfo viewportState =
  			vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
  
  		VkPipelineMultisampleStateCreateInfo multisampleState =
  			vkTools::initializers::pipelineMultisampleStateCreateInfo(
  				VK_SAMPLE_COUNT_1_BIT,
  				0);
  
  		std::vector<VkDynamicState> dynamicStateEnables = {
  			VK_DYNAMIC_STATE_VIEWPORT,
  			VK_DYNAMIC_STATE_SCISSOR
  		};
  		VkPipelineDynamicStateCreateInfo dynamicState =
  			vkTools::initializers::pipelineDynamicStateCreateInfo(
  				dynamicStateEnables.data(),
  				dynamicStateEnables.size(),
  				0);
  
  		// Rendering pipeline
  		// Load shaders
  		std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
  
  		shaderStages[0] = loadShader(getAssetPath() + "shaders/computeparticles/particle.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
  		shaderStages[1] = loadShader(getAssetPath() + "shaders/computeparticles/particle.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
  
  		VkGraphicsPipelineCreateInfo pipelineCreateInfo =
  			vkTools::initializers::pipelineCreateInfo(
  				pipelineLayout,
  				renderPass,
  				0);
  
  		pipelineCreateInfo.pVertexInputState = &vertices.inputState;
  		pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
  		pipelineCreateInfo.pRasterizationState = &rasterizationState;
  		pipelineCreateInfo.pColorBlendState = &colorBlendState;
  		pipelineCreateInfo.pMultisampleState = &multisampleState;
  		pipelineCreateInfo.pViewportState = &viewportState;
  		pipelineCreateInfo.pDepthStencilState = &depthStencilState;
  		pipelineCreateInfo.pDynamicState = &dynamicState;
  		pipelineCreateInfo.stageCount = shaderStages.size();
  		pipelineCreateInfo.pStages = shaderStages.data();
  		pipelineCreateInfo.renderPass = renderPass;
  
  		// Additive blending
  		blendAttachmentState.colorWriteMask = 0xF;
  		blendAttachmentState.blendEnable = VK_TRUE;
  		blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
  		blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
  		blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
  		blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
  		blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
  		blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;
  
  		VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.postCompute));
  	}
  
  	void prepareCompute()
  	{
  		// Create compute pipeline
  		// Compute pipelines are created separate from graphics pipelines
  		// even if they use the same queue
  
  		std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
  			// Binding 0 : Particle position storage buffer
  			vkTools::initializers::descriptorSetLayoutBinding(
  				VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
  				VK_SHADER_STAGE_COMPUTE_BIT,
  				0),
  			// Binding 1 : Uniform buffer
  			vkTools::initializers::descriptorSetLayoutBinding(
  				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
  				VK_SHADER_STAGE_COMPUTE_BIT,
  				1),
  		};
  
  		VkDescriptorSetLayoutCreateInfo descriptorLayout =
  			vkTools::initializers::descriptorSetLayoutCreateInfo(
  				setLayoutBindings.data(),
  				setLayoutBindings.size());
  
  		VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device,	&descriptorLayout, nullptr,	&computeDescriptorSetLayout));
  
  
  		VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
  			vkTools::initializers::pipelineLayoutCreateInfo(
  				&computeDescriptorSetLayout,
  				1);
  
  		VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr,	&computePipelineLayout));
  
  		VkDescriptorSetAllocateInfo allocInfo =
  			vkTools::initializers::descriptorSetAllocateInfo(
  				descriptorPool,
  				&computeDescriptorSetLayout,
  				1);
  
  		VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &computeDescriptorSet));
  
  		std::vector<VkWriteDescriptorSet> computeWriteDescriptorSets =
  		{
  			// Binding 0 : Particle position storage buffer
  			vkTools::initializers::writeDescriptorSet(
  				computeDescriptorSet,
  				VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
  				0,
  				&computeStorageBuffer.descriptor),
  			// Binding 1 : Uniform buffer
  			vkTools::initializers::writeDescriptorSet(
  				computeDescriptorSet,
  				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
  				1,
  				&uniformData.computeShader.ubo.descriptor)
  		};
  
  		vkUpdateDescriptorSets(device, computeWriteDescriptorSets.size(), computeWriteDescriptorSets.data(), 0, NULL);
  
  		// Create pipeline		
  		VkComputePipelineCreateInfo computePipelineCreateInfo =
  			vkTools::initializers::computePipelineCreateInfo(
  				computePipelineLayout,
  				0);
  		computePipelineCreateInfo.stage = loadShader(getAssetPath() + "shaders/computeparticles/particle.comp.spv", VK_SHADER_STAGE_COMPUTE_BIT);
  		VK_CHECK_RESULT(vkCreateComputePipelines(device, pipelineCache, 1, &computePipelineCreateInfo, nullptr, &pipelines.compute));
  	}
  
  	// Prepare and initialize uniform buffer containing shader uniforms
  	void prepareUniformBuffers()
  	{
  		// Compute shader uniform buffer block
  		createBuffer(
  			VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
  			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
  			sizeof(computeUbo),
  			nullptr,
  			&uniformData.computeShader.ubo.buffer,
  			&uniformData.computeShader.ubo.memory,
  			&uniformData.computeShader.ubo.descriptor);
  
  		// Map for host access
  		VK_CHECK_RESULT(vkMapMemory(device, uniformData.computeShader.ubo.memory, 0, sizeof(computeUbo), 0, (void **)&uniformData.computeShader.ubo.mapped));
  
  		updateUniformBuffers();
  	}
  
  	void updateUniformBuffers()
  	{
  		computeUbo.deltaT = frameTimer * 2.5f;
  		if (animate) 
  		{
  			computeUbo.destX = sin(glm::radians(timer*360.0)) * 0.75f;
  			computeUbo.destY = 0.f;
  		}
  		else
  		{
  			float normalizedMx = (mousePos.x - static_cast<float>(width / 2)) / static_cast<float>(width / 2);
  			float normalizedMy = (mousePos.y - static_cast<float>(height / 2)) / static_cast<float>(height / 2);
  			computeUbo.destX = normalizedMx;
  			computeUbo.destY = normalizedMy;
  		}
  
  		memcpy(uniformData.computeShader.ubo.mapped, &computeUbo, sizeof(computeUbo));
  	}
  
  	// Find and create a compute capable device queue
  	void getComputeQueue()
  	{
  		uint32_t queueIndex = 0;
  		uint32_t queueCount;
  		vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
  		assert(queueCount >= 1);
  
  		std::vector<VkQueueFamilyProperties> queueProps;
  		queueProps.resize(queueCount);
  		vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
  		
  		for (queueIndex = 0; queueIndex < queueCount; queueIndex++)
  		{
  			if (queueProps[queueIndex].queueFlags & VK_QUEUE_COMPUTE_BIT)
  				break;
  		}
  		assert(queueIndex < queueCount);
  
  		VkDeviceQueueCreateInfo queueCreateInfo = {};
  		queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
  		queueCreateInfo.pNext = NULL;
  		queueCreateInfo.queueFamilyIndex = queueIndex;
  		queueCreateInfo.queueCount = 1;
  		vkGetDeviceQueue(device, queueIndex, 0, &computeQueue);
  	}
  
  	void draw()
  	{
  		VulkanExampleBase::prepareFrame();
  
  		submitInfo.commandBufferCount = 1;
  		submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
  		VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
  
  		VulkanExampleBase::submitFrame();
  	}
  
  	void prepare()
  	{
  		VulkanExampleBase::prepare();
  		loadTextures();
  		getComputeQueue();
  		prepareStorageBuffers();
  		prepareUniformBuffers();
  		setupDescriptorSetLayout();
  		preparePipelines();
  		setupDescriptorPool();
  		setupDescriptorSet();
  		prepareCompute();
  		buildCommandBuffers();
  		prepared = true;
  	}
  
  	virtual void render()
  	{
  		if (!prepared)
  			return;
  		draw();
  
  		if (animate)
  		{
  			if (animStart > 0.0f)
  			{
  				animStart -= frameTimer * 5.0f;
  			}
  			else if (animStart <= 0.0f)
  			{
  				timer += frameTimer * 0.04f;
  				if (timer > 1.f)
  					timer = 0.f;
  			}
  		}
  		
  		updateUniformBuffers();
  	}
  
  	void toggleAnimation()
  	{
  		animate = !animate;
  	}
  };
  
  VulkanExample *vulkanExample;
  
  #if defined(_WIN32)
  LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  {
  	if (vulkanExample != NULL)
  	{
  		vulkanExample->handleMessages(hWnd, uMsg, wParam, lParam);
  		if (uMsg == WM_KEYDOWN)
  		{
  			switch (wParam)
  			{
  			case 0x41:
  				vulkanExample->toggleAnimation();
  				break;
  			}
  		}
  	}
  	return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  }
  #elif defined(__linux__) && !defined(__ANDROID__)
  static void handleEvent(const xcb_generic_event_t *event)
  {
  	if (vulkanExample != NULL)
  	{
  		vulkanExample->handleEvent(event);
  	}
  }
  #endif
  
  // Main entry point
  #if defined(_WIN32)
  // Windows entry point
  int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
  #elif defined(__ANDROID__)
  // Android entry point
  void android_main(android_app* state)
  #elif defined(__linux__)
  // Linux entry point
  int main(const int argc, const char *argv[])
  #endif
  {
  #if defined(__ANDROID__)
  	// Removing this may cause the compiler to omit the main entry point 
  	// which would make the application crash at start
  	app_dummy();
  #endif
  	vulkanExample = new VulkanExample();
  #if defined(_WIN32)
  	vulkanExample->setupWindow(hInstance, WndProc);
  #elif defined(__ANDROID__)
  	// Attach vulkan example to global android application state
  	state->userData = vulkanExample;
  	state->onAppCmd = VulkanExample::handleAppCommand;
  	state->onInputEvent = VulkanExample::handleAppInput;
  	vulkanExample->androidApp = state;
  #elif defined(__linux__)
  	vulkanExample->setupWindow();
  #endif
  #if !defined(__ANDROID__)
  	vulkanExample->initSwapchain();
  	vulkanExample->prepare();
  #endif
  	vulkanExample->renderLoop();
  	delete(vulkanExample);
  #if !defined(__ANDROID__)
  	return 0;
  #endif
  }