diff --git a/CMakeLists.txt b/CMakeLists.txt
index 31165e34..a93ae182 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -177,6 +177,7 @@ set(EXAMPLES
pbribl
pbrtexture
pipelines
+ pipelinestatistics
pushconstants
radialblur
raytracing
diff --git a/android/pipelinestatistics/.gitignore b/android/pipelinestatistics/.gitignore
new file mode 100644
index 00000000..7a5d249c
--- /dev/null
+++ b/android/pipelinestatistics/.gitignore
@@ -0,0 +1,10 @@
+/assets/
+/res/
+/bin/
+/libs/
+/obj/
+/build.xml
+/local.properties
+/project.properties
+/proguard-project.txt
+*.apk
\ No newline at end of file
diff --git a/android/pipelinestatistics/AndroidManifest.xml b/android/pipelinestatistics/AndroidManifest.xml
new file mode 100644
index 00000000..7e73dc1a
--- /dev/null
+++ b/android/pipelinestatistics/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/pipelinestatistics/example.json b/android/pipelinestatistics/example.json
new file mode 100644
index 00000000..ee6eebfd
--- /dev/null
+++ b/android/pipelinestatistics/example.json
@@ -0,0 +1,15 @@
+{
+ "apkname": "vulkanPipelinestatistics",
+ "directories": {
+ "shaders": "pipelinestatistics"
+ },
+ "assets": {
+ "models": [
+ "cube.obj",
+ "geosphere.obj",
+ "teapot.dae",
+ "torusknot.obj",
+ "venus.fbx"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/data/shaders/pipelinestatistics/scene.frag b/data/shaders/pipelinestatistics/scene.frag
new file mode 100644
index 00000000..43f18f5f
--- /dev/null
+++ b/data/shaders/pipelinestatistics/scene.frag
@@ -0,0 +1,19 @@
+#version 450
+
+layout (location = 0) in vec3 inNormal;
+layout (location = 1) in vec3 inColor;
+layout (location = 2) in vec3 inViewVec;
+layout (location = 3) in vec3 inLightVec;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main()
+{
+ vec3 N = normalize(inNormal);
+ vec3 L = normalize(inLightVec);
+ vec3 V = normalize(inViewVec);
+ vec3 R = reflect(-L, N);
+ vec3 diffuse = max(dot(N, L), 0.0) * inColor;
+ vec3 specular = pow(max(dot(R, V), 0.0), 8.0) * vec3(0.75);
+ outFragColor = vec4(diffuse + specular, 0.5);
+}
\ No newline at end of file
diff --git a/data/shaders/pipelinestatistics/scene.frag.spv b/data/shaders/pipelinestatistics/scene.frag.spv
new file mode 100644
index 00000000..17d51ad6
Binary files /dev/null and b/data/shaders/pipelinestatistics/scene.frag.spv differ
diff --git a/data/shaders/pipelinestatistics/scene.tesc b/data/shaders/pipelinestatistics/scene.tesc
new file mode 100644
index 00000000..60693281
--- /dev/null
+++ b/data/shaders/pipelinestatistics/scene.tesc
@@ -0,0 +1,30 @@
+#version 450
+
+layout (vertices = 3) out;
+
+layout (location = 0) in vec3 inNormal[];
+layout (location = 1) in vec3 inColor[];
+layout (location = 2) in vec3 inViewVec[];
+layout (location = 3) in vec3 inLightVec[];
+
+layout (location = 0) out vec3 outNormal[3];
+layout (location = 1) out vec3 outColor[3];
+layout (location = 2) out vec3 outViewVec[3];
+layout (location = 3) out vec3 outLightVec[3];
+
+void main(void)
+{
+ if (gl_InvocationID == 0)
+ {
+ gl_TessLevelInner[0] = 2.0;
+ gl_TessLevelOuter[0] = 1.0;
+ gl_TessLevelOuter[1] = 1.0;
+ gl_TessLevelOuter[2] = 1.0;
+ }
+
+ gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
+ outNormal[gl_InvocationID] = inNormal[gl_InvocationID];
+ outColor[gl_InvocationID] = inColor[gl_InvocationID];
+ outViewVec[gl_InvocationID] = inViewVec[gl_InvocationID];
+ outLightVec[gl_InvocationID] = inLightVec[gl_InvocationID];
+}
\ No newline at end of file
diff --git a/data/shaders/pipelinestatistics/scene.tesc.spv b/data/shaders/pipelinestatistics/scene.tesc.spv
new file mode 100644
index 00000000..d68bc835
Binary files /dev/null and b/data/shaders/pipelinestatistics/scene.tesc.spv differ
diff --git a/data/shaders/pipelinestatistics/scene.tese b/data/shaders/pipelinestatistics/scene.tese
new file mode 100644
index 00000000..39f020d2
--- /dev/null
+++ b/data/shaders/pipelinestatistics/scene.tese
@@ -0,0 +1,24 @@
+#version 450
+
+layout (triangles) in;
+
+layout (location = 0) in vec3 inNormal[];
+layout (location = 1) in vec3 inColor[];
+layout (location = 2) in vec3 inViewVec[];
+layout (location = 3) in vec3 inLightVec[];
+
+layout (location = 0) out vec3 outNormal;
+layout (location = 1) out vec3 outColor;
+layout (location = 2) out vec3 outViewVec;
+layout (location = 3) out vec3 outLightVec;
+
+void main(void)
+{
+ gl_Position = (gl_TessCoord.x * gl_in[2].gl_Position) +
+ (gl_TessCoord.y * gl_in[1].gl_Position) +
+ (gl_TessCoord.z * gl_in[0].gl_Position);
+ outNormal = gl_TessCoord.x*inNormal[2] + gl_TessCoord.y*inNormal[1] + gl_TessCoord.z*inNormal[0];
+ outViewVec = gl_TessCoord.x*inViewVec[2] + gl_TessCoord.y*inViewVec[1] + gl_TessCoord.z*inViewVec[0];
+ outLightVec = gl_TessCoord.x*inLightVec[2] + gl_TessCoord.y*inLightVec[1] + gl_TessCoord.z*inLightVec[0];
+ outColor = inColor[0];
+}
\ No newline at end of file
diff --git a/data/shaders/pipelinestatistics/scene.tese.spv b/data/shaders/pipelinestatistics/scene.tese.spv
new file mode 100644
index 00000000..1a6233c4
Binary files /dev/null and b/data/shaders/pipelinestatistics/scene.tese.spv differ
diff --git a/data/shaders/pipelinestatistics/scene.vert b/data/shaders/pipelinestatistics/scene.vert
new file mode 100644
index 00000000..0399c8c3
--- /dev/null
+++ b/data/shaders/pipelinestatistics/scene.vert
@@ -0,0 +1,41 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec3 inColor;
+
+layout (binding = 0) uniform UBO
+{
+ mat4 projection;
+ mat4 modelview;
+ vec4 lightPos;
+} ubo;
+
+layout (location = 0) out vec3 outNormal;
+layout (location = 1) out vec3 outColor;
+layout (location = 2) out vec3 outViewVec;
+layout (location = 3) out vec3 outLightVec;
+
+layout(push_constant) uniform PushConsts {
+ vec3 objPos;
+} pushConsts;
+
+out gl_PerVertex
+{
+ vec4 gl_Position;
+};
+
+void main()
+{
+ outNormal = inNormal;
+ outColor = inColor;
+
+ vec3 locPos = vec3(ubo.modelview * vec4(inPos, 1.0));
+ vec3 worldPos = vec3(ubo.modelview * vec4(inPos + pushConsts.objPos, 1.0));
+ gl_Position = ubo.projection /* ubo.modelview */ * vec4(worldPos, 1.0);
+
+ vec4 pos = ubo.modelview * vec4(worldPos, 1.0);
+ outNormal = mat3(ubo.modelview) * inNormal;
+ outLightVec = ubo.lightPos.xyz - pos.xyz;
+ outViewVec = -pos.xyz;
+}
\ No newline at end of file
diff --git a/data/shaders/pipelinestatistics/scene.vert.spv b/data/shaders/pipelinestatistics/scene.vert.spv
new file mode 100644
index 00000000..c9a8eeb6
Binary files /dev/null and b/data/shaders/pipelinestatistics/scene.vert.spv differ
diff --git a/pipelinestatistics/pipelinestatistics.cpp b/pipelinestatistics/pipelinestatistics.cpp
new file mode 100644
index 00000000..6d4d06e6
--- /dev/null
+++ b/pipelinestatistics/pipelinestatistics.cpp
@@ -0,0 +1,492 @@
+/*
+* Vulkan Example - Retrieving pipeline statistics
+*
+* Copyright (C) 2017 by Sascha Willems - www.saschawillems.de
+*
+* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#define GLM_FORCE_RADIANS
+#define GLM_FORCE_DEPTH_ZERO_TO_ONE
+#include
+#include
+
+#include
+#include "vulkanexamplebase.h"
+#include "VulkanBuffer.hpp"
+#include "VulkanModel.hpp"
+
+#define ENABLE_VALIDATION false
+#define OBJ_DIM 0.05f
+
+class VulkanExample : public VulkanExampleBase
+{
+public:
+ // Vertex layout for the models
+ vks::VertexLayout vertexLayout = vks::VertexLayout({
+ vks::VERTEX_COMPONENT_POSITION,
+ vks::VERTEX_COMPONENT_NORMAL,
+ vks::VERTEX_COMPONENT_COLOR,
+ });
+
+ struct Models {
+ std::vector objects;
+ int32_t objectIndex = 3;
+ std::vector names;
+ } models;
+
+ struct UniformBuffers {
+ vks::Buffer VS;
+ } uniformBuffers;
+
+ struct UBOVS {
+ glm::mat4 projection;
+ glm::mat4 modelview;
+ glm::vec4 lightPos = glm::vec4(-10.0f, -10.0f, 10.0f, 1.0f);
+ } uboVS;
+
+ VkPipeline pipeline = VK_NULL_HANDLE;
+
+ int32_t cullMode = VK_CULL_MODE_BACK_BIT;
+ bool blending = false;
+ bool discard = false;
+ bool wireframe = false;
+ bool tessellation = false;
+
+ VkPipelineLayout pipelineLayout;
+ VkDescriptorSet descriptorSet;
+ VkDescriptorSetLayout descriptorSetLayout;
+
+ VkQueryPool queryPool;
+
+ // Vector for storing pipeline statistics results
+ std::vector pipelineStats;
+ std::vector pipelineStatNames;
+
+ int32_t gridSize = 3;
+
+ VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
+ {
+ title = "Pipeline statistics";
+ camera.type = Camera::CameraType::firstperson;
+ camera.setPosition(glm::vec3(-4.0f, 3.0f, -3.75f));
+ camera.setRotation(glm::vec3(-15.25f, -46.5f, 0.0f));
+ camera.movementSpeed = 4.0f;
+ camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
+ camera.rotationSpeed = 0.25f;
+ settings.overlay = true;
+ }
+
+ ~VulkanExample()
+ {
+ vkDestroyPipeline(device, pipeline, nullptr);
+ vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
+ vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
+ vkDestroyQueryPool(device, queryPool, nullptr);
+ uniformBuffers.VS.destroy();
+ for (auto& model : models.objects) {
+ model.destroy();
+ }
+ }
+
+ virtual void getEnabledFeatures()
+ {
+ // Support for pipeline statistics is optional
+ if (deviceFeatures.pipelineStatisticsQuery) {
+ enabledFeatures.pipelineStatisticsQuery = VK_TRUE;
+ }
+ else {
+ vks::tools::exitFatal("Selected GPU does not support pipeline statistics!", "Feature not supported");
+ }
+ if (deviceFeatures.fillModeNonSolid) {
+ enabledFeatures.fillModeNonSolid = VK_TRUE;
+ }
+ if (deviceFeatures.tessellationShader) {
+ enabledFeatures.tessellationShader = VK_TRUE;
+ }
+ }
+
+ // Setup a query pool for storing pipeline statistics
+ void setupQueryPool()
+ {
+ pipelineStatNames = {
+ "Input assembly vertex count ",
+ "Input assembly primitives count ",
+ "Vertex shader invocations ",
+ "Clipping stage primitives processed",
+ "Clipping stage primtives output ",
+ "Fragment shader invocations "
+ };
+ if (deviceFeatures.tessellationShader) {
+ pipelineStatNames.push_back("Tess. control shader patches ");
+ pipelineStatNames.push_back("Tess. eval. shader invocations ");
+ }
+ pipelineStats.resize(pipelineStatNames.size());
+
+ VkQueryPoolCreateInfo queryPoolInfo = {};
+ queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
+ // This query pool will store pipeline statistics
+ queryPoolInfo.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS;
+ // Pipeline counters to be returned for this pool
+ queryPoolInfo.pipelineStatistics =
+ VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT;
+ if (deviceFeatures.tessellationShader) {
+ queryPoolInfo.pipelineStatistics |=
+ VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT;
+ }
+ queryPoolInfo.queryCount = deviceFeatures.tessellationShader ? 8 : 6;
+ VK_CHECK_RESULT(vkCreateQueryPool(device, &queryPoolInfo, NULL, &queryPool));
+ }
+
+ // Retrieves the results of the pipeline statistics query submitted to the command buffer
+ void getQueryResults()
+ {
+ uint32_t count = static_cast(pipelineStats.size());
+ vkGetQueryPoolResults(
+ device,
+ queryPool,
+ 0,
+ 1,
+ count * sizeof(uint64_t),
+ pipelineStats.data(),
+ sizeof(uint64_t),
+ VK_QUERY_RESULT_64_BIT);
+ }
+
+ void buildCommandBuffers()
+ {
+ VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
+
+ VkClearValue clearValues[2];
+ clearValues[0].color = defaultClearColor;
+ 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) {
+ renderPassBeginInfo.framebuffer = frameBuffers[i];
+
+ VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
+
+ // Reset timestamp query pool
+ vkCmdResetQueryPool(drawCmdBuffers[i], queryPool, 0, static_cast(pipelineStats.size()));
+
+ 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);
+
+ VkDeviceSize offsets[1] = { 0 };
+
+ // Start capture of pipeline statistics
+ vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 0, VK_QUERY_CONTROL_PRECISE_BIT);
+
+ vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
+ vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.objects[models.objectIndex].vertices.buffer, offsets);
+ vkCmdBindIndexBuffer(drawCmdBuffers[i], models.objects[models.objectIndex].indices.buffer, 0, VK_INDEX_TYPE_UINT32);
+
+ for (int32_t y = 0; y < gridSize; y++) {
+ for (int32_t x = 0; x < gridSize; x++) {
+ glm::vec3 pos = glm::vec3(float(x - (gridSize / 2.0f)) * 2.5f, 0.0f, float(y - (gridSize / 2.0f)) * 2.5f);
+ vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::vec3), &pos);
+ vkCmdDrawIndexed(drawCmdBuffers[i], models.objects[models.objectIndex].indexCount, 1, 0, 0, 0);
+ }
+ }
+
+ // End capture of pipeline statistics
+ vkCmdEndQuery(drawCmdBuffers[i], queryPool, 0);
+
+ vkCmdEndRenderPass(drawCmdBuffers[i]);
+
+ VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
+ }
+ }
+
+ void draw()
+ {
+ VulkanExampleBase::prepareFrame();
+
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
+ VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
+
+ // Read query results for displaying in next frame
+ getQueryResults();
+
+ VulkanExampleBase::submitFrame();
+ }
+
+ void loadAssets()
+ {
+ // Objects
+ std::vector filenames = { "geosphere.obj", "teapot.dae", "torusknot.obj", "venus.fbx" };
+ for (auto file : filenames) {
+ vks::Model model;
+ model.loadFromFile(getAssetPath() + "models/" + file, vertexLayout, OBJ_DIM * (file == "venus.fbx" ? 3.0f : 1.0f), vulkanDevice, queue);
+ models.objects.push_back(model);
+ }
+ models.names = { "Sphere", "Teapot", "Torusknot", "Venus" };
+ }
+
+ void setupDescriptorPool()
+ {
+ std::vector poolSizes = {
+ vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3)
+ };
+ VkDescriptorPoolCreateInfo descriptorPoolInfo =
+ vks::initializers::descriptorPoolCreateInfo(poolSizes, 3);
+ VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
+ }
+
+ void setupDescriptorSetLayout()
+ {
+ std::vector setLayoutBindings = {
+ vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0)
+ };
+ VkDescriptorSetLayoutCreateInfo descriptorLayout =
+ vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
+ VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
+
+ VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
+ vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
+ VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(glm::vec3), 0);
+ pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
+ pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
+ VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
+ }
+
+ void setupDescriptorSets()
+ {
+ VkDescriptorSetAllocateInfo allocInfo =
+ vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
+ VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
+ std::vector writeDescriptorSets = {
+ vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.VS.descriptor)
+ };
+ vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
+ }
+
+ void preparePipelines()
+ {
+ if (pipeline != VK_NULL_HANDLE) {
+ vkDestroyPipeline(device, pipeline, nullptr);
+ }
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
+ vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
+
+ VkPipelineRasterizationStateCreateInfo rasterizationState =
+ vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, cullMode, 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 dynamicStateEnables = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR
+ };
+ VkPipelineDynamicStateCreateInfo dynamicState =
+ vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast(dynamicStateEnables.size()), 0);
+
+ VkGraphicsPipelineCreateInfo pipelineCreateInfo =
+ vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
+
+ VkPipelineTessellationStateCreateInfo tessellationState =
+ vks::initializers::pipelineTessellationStateCreateInfo(3);
+
+ pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
+ pipelineCreateInfo.pRasterizationState = &rasterizationState;
+ pipelineCreateInfo.pColorBlendState = &colorBlendState;
+ pipelineCreateInfo.pMultisampleState = &multisampleState;
+ pipelineCreateInfo.pViewportState = &viewportState;
+ pipelineCreateInfo.pDepthStencilState = &depthStencilState;
+ pipelineCreateInfo.pDynamicState = &dynamicState;
+
+ // Vertex bindings and attributes
+ std::vector vertexInputBindings = {
+ vks::initializers::vertexInputBindingDescription(0, vertexLayout.stride(), VK_VERTEX_INPUT_RATE_VERTEX)
+ };
+
+ std::vector vertexInputAttributes = {
+ vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Location 0 : Position
+ vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3), // Location 1 : Normal
+ vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 6) // Location 3 : Color
+ };
+
+ VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
+ vertexInputState.vertexBindingDescriptionCount = static_cast(vertexInputBindings.size());
+ vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
+ vertexInputState.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size());
+ vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
+
+ pipelineCreateInfo.pVertexInputState = &vertexInputState;
+
+ if (blending) {
+ blendAttachmentState.blendEnable = VK_TRUE;
+ blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
+ blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
+ depthStencilState.depthWriteEnable = VK_FALSE;
+ }
+
+ if (discard) {
+ rasterizationState.rasterizerDiscardEnable = VK_TRUE;
+ }
+
+ if (wireframe) {
+ rasterizationState.polygonMode = VK_POLYGON_MODE_LINE;
+ }
+
+ std::vector shaderStages;
+ shaderStages.resize(tessellation ? 4 : 2);
+ shaderStages[0] = loadShader(getAssetPath() + "shaders/pipelinestatistics/scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
+ shaderStages[1] = loadShader(getAssetPath() + "shaders/pipelinestatistics/scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
+
+ if (tessellation) {
+ inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
+ pipelineCreateInfo.pTessellationState = &tessellationState;
+ shaderStages[2] = loadShader(getAssetPath() + "shaders/pipelinestatistics/scene.tesc.spv", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
+ shaderStages[3] = loadShader(getAssetPath() + "shaders/pipelinestatistics/scene.tese.spv", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
+ }
+
+ pipelineCreateInfo.stageCount = static_cast(shaderStages.size());
+ pipelineCreateInfo.pStages = shaderStages.data();
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
+ }
+
+ // Prepare and initialize uniform buffer containing shader uniforms
+ void prepareUniformBuffers()
+ {
+ 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)));
+
+ // Map persistent
+ VK_CHECK_RESULT(uniformBuffers.VS.map());
+
+ updateUniformBuffers();
+ }
+
+ void updateUniformBuffers()
+ {
+ uboVS.projection = camera.matrices.perspective;
+ uboVS.modelview = camera.matrices.view;
+ memcpy(uniformBuffers.VS.mapped, &uboVS, sizeof(uboVS));
+ }
+
+ void prepare()
+ {
+ VulkanExampleBase::prepare();
+ loadAssets();
+ setupQueryPool();
+ prepareUniformBuffers();
+ setupDescriptorSetLayout();
+ preparePipelines();
+ setupDescriptorPool();
+ setupDescriptorSets();
+ 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->comboBox("Object type", &models.objectIndex, models.names)) {
+ updateUniformBuffers();
+ buildCommandBuffers();
+ }
+ if (overlay->sliderInt("Grid size", &gridSize, 1, 10)) {
+ buildCommandBuffers();
+ }
+ std::vector cullModeNames = { "None", "Front", "Back", "Back and front" };
+ if (overlay->comboBox("Cull mode", &cullMode, cullModeNames)) {
+ preparePipelines();
+ buildCommandBuffers();
+ }
+ if (overlay->checkBox("Blending", &blending)) {
+ preparePipelines();
+ buildCommandBuffers();
+ }
+ if (deviceFeatures.fillModeNonSolid) {
+ if (overlay->checkBox("Wireframe", &wireframe)) {
+ preparePipelines();
+ buildCommandBuffers();
+ }
+ }
+ if (deviceFeatures.tessellationShader) {
+ if (overlay->checkBox("Tessellation", &tessellation)) {
+ preparePipelines();
+ buildCommandBuffers();
+ }
+ }
+ if (overlay->checkBox("Discard", &discard)) {
+ preparePipelines();
+ buildCommandBuffers();
+ }
+ }
+ if (!pipelineStats.empty()) {
+ if (overlay->header("Pipeline statistics")) {
+ for (auto i = 0; i < pipelineStats.size(); i++) {
+ std::string caption = pipelineStatNames[i] + ": %d";
+ overlay->text(caption.c_str(), pipelineStats[i]);
+ }
+ }
+ }
+ }
+
+};
+
+VULKAN_EXAMPLE_MAIN()
\ No newline at end of file