text: /*
  * Vulkan Example - Geometry shader (vertex normal debugging)
  *
  * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
  *
  * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
  */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <assert.h>
  #include <vector>
  
  #define GLM_FORCE_RADIANS
  #define GLM_FORCE_DEPTH_ZERO_TO_ONE
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  
  #include <vulkan/vulkan.h>
  #include "vulkanexamplebase.h"
  #include "VulkanglTFModel.h"
  
  #define VERTEX_BUFFER_BIND_ID 0
  #define ENABLE_VALIDATION false
  
  class VulkanExample : public VulkanExampleBase
  {
  public:
  	bool displayNormals = true;
  
  	vkglTF::Model scene;
  
  	struct {
  		glm::mat4 projection;
  		glm::mat4 modelView;
  	} uboVS;
  
  	struct {
  		glm::mat4 projection;
  		glm::mat4 modelView;
  		glm::vec2 viewportDim;
  	} uboGS;
  
  	struct {
  		vks::Buffer VS;
  		vks::Buffer GS;
  	} uniformBuffers;
  
  	struct {
  		VkPipeline solid;
  		VkPipeline normals;
  	} pipelines;
  
  	VkPipelineLayout pipelineLayout;
  	VkDescriptorSet descriptorSet;
  	VkDescriptorSetLayout descriptorSetLayout;
  
  	VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
  	{
  		title = "Geometry shader normal debugging";
  		camera.type = Camera::CameraType::lookat;
  		camera.setPosition(glm::vec3(0.0f, 0.0f, -1.0f));
  		camera.setRotation(glm::vec3(0.0f, -25.0f, 0.0f));
  		camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 128.0f);
  		settings.overlay = true;
  	}
  
  	~VulkanExample()
  	{
  		// Clean up used Vulkan resources
  		// Note : Inherited destructor cleans up resources stored in base class
  		vkDestroyPipeline(device, pipelines.solid, nullptr);
  		vkDestroyPipeline(device, pipelines.normals, nullptr);
  
  		vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
  		vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
  
  		uniformBuffers.GS.destroy();
  		uniformBuffers.VS.destroy();
  	}
  
  	// Enable physical device features required for this example
  	virtual void getEnabledFeatures()
  	{
  		// Geometry shader support is required for this example
  		if (deviceFeatures.geometryShader) {
  			enabledFeatures.geometryShader = VK_TRUE;
  		}
  		else {
  			vks::tools::exitFatal("Selected GPU does not support geometry shaders!", VK_ERROR_FEATURE_NOT_PRESENT);
  		}
  	}
  
  	void buildCommandBuffers()
  	{
  		VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
  
  		VkClearValue clearValues[2];
  		clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 0.0f } };
  		clearValues[1].depthStencil = { 1.0f, 0 };
  
  		VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
  		renderPassBeginInfo.renderPass = renderPass;
  		renderPassBeginInfo.renderArea.offset.x = 0;
  		renderPassBeginInfo.renderArea.offset.y = 0;
  		renderPassBeginInfo.renderArea.extent.width = width;
  		renderPassBeginInfo.renderArea.extent.height = height;
  		renderPassBeginInfo.clearValueCount = 2;
  		renderPassBeginInfo.pClearValues = clearValues;
  
  		for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
  		{
  			// Set target frame buffer
  			renderPassBeginInfo.framebuffer = frameBuffers[i];
  
  			VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
  
  			vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  
  			VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f
  			);
  			vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
  
  			VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
  			vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
  
  			vkCmdSetLineWidth(drawCmdBuffers[i], 1.0f);
  
  			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
  
  			VkDeviceSize offsets[1] = { 0 };
  
  			// Solid shading
  			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);
  			scene.draw(drawCmdBuffers[i]);
  
  			// Normal debugging
  			if (displayNormals)
  			{
  				vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.normals);
  				scene.draw(drawCmdBuffers[i]);
  			}
  
  			drawUI(drawCmdBuffers[i]);
  
  			vkCmdEndRenderPass(drawCmdBuffers[i]);
  
  			VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
  		}
  	}
  
  	void loadAssets()
  	{
  		scene.loadFromFile(getAssetPath() + "models/suzanne.gltf", vulkanDevice, queue, vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY);
  	}
  
  	void setupDescriptorPool()
  	{
  		// Example uses two ubos
  		std::vector<VkDescriptorPoolSize> poolSizes =
  		{
  			vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2),
  		};
  
  		VkDescriptorPoolCreateInfo descriptorPoolInfo =
  			vks::initializers::descriptorPoolCreateInfo(
  				poolSizes.size(),
  				poolSizes.data(),
  				2);
  
  		VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
  	}
  
  	void setupDescriptorSetLayout()
  	{
  		std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
  		{
  			// Binding 0 : Vertex shader ubo
  			vks::initializers::descriptorSetLayoutBinding(
  				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
  				VK_SHADER_STAGE_VERTEX_BIT,
  				0),
  			// Binding 1 : Geometry shader ubo
  			vks::initializers::descriptorSetLayoutBinding(
  				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
  				VK_SHADER_STAGE_GEOMETRY_BIT,
  				1)
  		};
  
  		VkDescriptorSetLayoutCreateInfo descriptorLayout =
  			vks::initializers::descriptorSetLayoutCreateInfo(
  				setLayoutBindings.data(),
  				setLayoutBindings.size());
  
  		VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
  
  		VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
  			vks::initializers::pipelineLayoutCreateInfo(
  				&descriptorSetLayout,
  				1);
  
  		VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
  	}
  
  	void setupDescriptorSet()
  	{
  		VkDescriptorSetAllocateInfo allocInfo =
  			vks::initializers::descriptorSetAllocateInfo(
  				descriptorPool,
  				&descriptorSetLayout,
  				1);
  
  		VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
  
  		std::vector<VkWriteDescriptorSet> writeDescriptorSets =
  		{
  			// Binding 0 : Vertex shader shader ubo
  			vks::initializers::writeDescriptorSet(
  				descriptorSet,
  				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
  				0,
  				&uniformBuffers.VS.descriptor),
  			// Binding 1 : Geometry shader ubo
  			vks::initializers::writeDescriptorSet(
  				descriptorSet,
  				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
  				1,
  				&uniformBuffers.GS.descriptor)
  		};
  
  		vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
  	}
  
  	void preparePipelines()
  	{
  		VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
  		VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0);
  		VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
  		VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
  		VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
  		VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
  		VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
  		std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_LINE_WIDTH };
  		VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables, 0);
  
  		// Tessellation pipeline
  		std::array<VkPipelineShaderStageCreateInfo, 3> shaderStages;
  		shaderStages[0] = loadShader(getShadersPath() + "geometryshader/base.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
  		shaderStages[1] = loadShader(getShadersPath() + "geometryshader/base.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
  		shaderStages[2] = loadShader(getShadersPath() + "geometryshader/normaldebug.geom.spv", VK_SHADER_STAGE_GEOMETRY_BIT);
  
  		VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
  		pipelineCI.pInputAssemblyState = &inputAssemblyState;
  		pipelineCI.pRasterizationState = &rasterizationState;
  		pipelineCI.pColorBlendState = &colorBlendState;
  		pipelineCI.pMultisampleState = &multisampleState;
  		pipelineCI.pViewportState = &viewportState;
  		pipelineCI.pDepthStencilState = &depthStencilState;
  		pipelineCI.pDynamicState = &dynamicState;
  		pipelineCI.stageCount = shaderStages.size();
  		pipelineCI.pStages = shaderStages.data();
  		pipelineCI.renderPass = renderPass;
  		pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::Color });
  
  		// Normal debugging pipeline
  		VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.normals));
  
  		// Solid rendering pipeline
  		shaderStages[0] = loadShader(getShadersPath() + "geometryshader/mesh.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
  		shaderStages[1] = loadShader(getShadersPath() + "geometryshader/mesh.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
  		pipelineCI.stageCount = 2;
  		VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.solid));
  	}
  
  	// Prepare and initialize uniform buffer containing shader uniforms
  	void prepareUniformBuffers()
  	{
  		// Vertex shader uniform buffer block
  		VK_CHECK_RESULT(vulkanDevice->createBuffer(
  			VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
  			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
  			&uniformBuffers.VS,
  			sizeof(uboVS)));
  
  		// Geometry shader uniform buffer block
  		VK_CHECK_RESULT(vulkanDevice->createBuffer(
  			VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
  			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
  			&uniformBuffers.GS,
  			sizeof(uboGS)));
  
  		// Map persistent
  		VK_CHECK_RESULT(uniformBuffers.VS.map());
  		VK_CHECK_RESULT(uniformBuffers.GS.map());
  
  		updateUniformBuffers();
  	}
  
  	void updateUniformBuffers()
  	{
  		// Vertex shader
  		uboVS.projection = camera.matrices.perspective;
  		uboVS.modelView = camera.matrices.view;
  		memcpy(uniformBuffers.VS.mapped, &uboVS, sizeof(uboVS));
  		// Geometry shader
  		uboGS.projection = camera.matrices.perspective;
  		uboGS.modelView = camera.matrices.view;
  		uboGS.viewportDim = glm::vec2(width, height);
  		memcpy(uniformBuffers.GS.mapped, &uboGS, sizeof(uboGS));
  	}
  
  	void draw()
  	{
  		VulkanExampleBase::prepareFrame();
  
  		// Command buffer to be sumitted to the queue
  		submitInfo.commandBufferCount = 1;
  		submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
  
  		// Submit to queue
  		VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
  
  		VulkanExampleBase::submitFrame();
  	}
  
  	void prepare()
  	{
  		VulkanExampleBase::prepare();
  		loadAssets();
  		prepareUniformBuffers();
  		setupDescriptorSetLayout();
  		preparePipelines();
  		setupDescriptorPool();
  		setupDescriptorSet();
  		buildCommandBuffers();
  		prepared = true;
  	}
  
  	virtual void render()
  	{
  		if (!prepared)
  			return;
  		draw();
  	}
  
  	virtual void viewChanged()
  	{
  		updateUniformBuffers();
  	}
  
  	virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
  	{
  		if (overlay->header("Settings")) {
  			if (overlay->checkBox("Display normals", &displayNormals)) {
  				buildCommandBuffers();
  			}
  		}
  	}
  
  };
  
  VULKAN_EXAMPLE_MAIN()