Added tessellation evaluation shader frustum culling and pipeline stats for dynamic terrain tessellation example

This commit is contained in:
saschawillems 2016-06-23 22:01:48 +02:00
parent dbf80b217c
commit a9de176d12
7 changed files with 147 additions and 27 deletions

View file

@ -14,10 +14,10 @@ namespace vkTools
{ {
class Frustum class Frustum
{ {
private: public:
enum side { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3, BACK = 4, FRONT = 5 }; enum side { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3, BACK = 4, FRONT = 5 };
std::array<glm::vec4, 6> planes; std::array<glm::vec4, 6> planes;
public:
void update(glm::mat4 matrix) void update(glm::mat4 matrix)
{ {
planes[LEFT].x = matrix[0].w + matrix[0].x; planes[LEFT].x = matrix[0].w + matrix[0].x;

View file

@ -54,4 +54,6 @@ void main()
outFragColor = vec4((IAmbient + IDiffuse) * vec4(texture(terrainLayers, vec3(inUV, 0.0)).rgb, 1.0)); outFragColor = vec4((IAmbient + IDiffuse) * vec4(texture(terrainLayers, vec3(inUV, 0.0)).rgb, 1.0));
*/ */
outFragColor = sampleTerrainLayer(); outFragColor = sampleTerrainLayer();
}
//outFragColor.rgb = normalize(inNormal);
}

View file

@ -8,13 +8,14 @@ layout(set = 0, binding = 0) uniform UBO
mat4 projection; mat4 projection;
mat4 modelview; mat4 modelview;
vec4 lightPos; vec4 lightPos;
vec4 frustumPlanes[6];
float displacementFactor; float displacementFactor;
float tessellationFactor; float tessellationFactor;
vec2 viewportDim; vec2 viewportDim;
float tessellatedEdgeSize; float tessellatedEdgeSize;
} ubo; } ubo;
layout(set = 0, binding = 2) uniform sampler2D samplerHeight; layout(set = 0, binding = 1) uniform sampler2D samplerHeight;
layout (vertices = 4) out; layout (vertices = 4) out;
@ -31,7 +32,7 @@ float screenSpaceTessFactor(vec4 p0, vec4 p1)
// Calculate edge mid point // Calculate edge mid point
vec4 midPoint = 0.5 * (p0 + p1); vec4 midPoint = 0.5 * (p0 + p1);
// Sphere radius as distance between the control points // Sphere radius as distance between the control points
float radius = distance(p0, p1) / 2; float radius = distance(p0, p1) / 2.0;
// View space // View space
vec4 v0 = ubo.modelview * midPoint; vec4 v0 = ubo.modelview * midPoint;
@ -54,30 +55,62 @@ float screenSpaceTessFactor(vec4 p0, vec4 p1)
return clamp(distance(clip0, clip1) / ubo.tessellatedEdgeSize * ubo.tessellationFactor, 1.0, 64.0); return clamp(distance(clip0, clip1) / ubo.tessellatedEdgeSize * ubo.tessellationFactor, 1.0, 64.0);
} }
// Checks the current's patch visibility against the frustum using a sphere check
// Sphere radius is given by the patch size
bool frustumCheck()
{
// Fixed radius (increase if patch size is increased in example)
const float radius = 8.0f;
vec4 pos = gl_in[gl_InvocationID].gl_Position;
pos.y -= textureLod(samplerHeight, inUV[0], 0.0).r * ubo.displacementFactor;
// Check sphere against frustum planes
for (int i = 0; i < 6; i++) {
if (dot(pos, ubo.frustumPlanes[i]) + radius < 0.0)
{
return false;
}
}
return true;
}
void main() void main()
{ {
if (gl_InvocationID == 0) if (gl_InvocationID == 0)
{ {
if (ubo.tessellationFactor > 0.0) if (!frustumCheck())
{ {
gl_TessLevelOuter[0] = screenSpaceTessFactor(gl_in[3].gl_Position, gl_in[0].gl_Position); gl_TessLevelInner[0] = 0.0;
gl_TessLevelOuter[1] = screenSpaceTessFactor(gl_in[0].gl_Position, gl_in[1].gl_Position); gl_TessLevelInner[1] = 0.0;
gl_TessLevelOuter[2] = screenSpaceTessFactor(gl_in[1].gl_Position, gl_in[2].gl_Position); gl_TessLevelOuter[0] = 0.0;
gl_TessLevelOuter[3] = screenSpaceTessFactor(gl_in[2].gl_Position, gl_in[3].gl_Position); gl_TessLevelOuter[1] = 0.0;
gl_TessLevelInner[0] = mix(gl_TessLevelOuter[0], gl_TessLevelOuter[3], 0.5); gl_TessLevelOuter[2] = 0.0;
gl_TessLevelInner[1] = mix(gl_TessLevelOuter[2], gl_TessLevelOuter[1], 0.5); gl_TessLevelOuter[3] = 0.0;
} }
else else
{ {
// Tessellation factor can be set to zero by example if (ubo.tessellationFactor > 0.0)
// to demonstrate a simple passthrough {
gl_TessLevelInner[0] = 1.0; gl_TessLevelOuter[0] = screenSpaceTessFactor(gl_in[3].gl_Position, gl_in[0].gl_Position);
gl_TessLevelInner[1] = 1.0; gl_TessLevelOuter[1] = screenSpaceTessFactor(gl_in[0].gl_Position, gl_in[1].gl_Position);
gl_TessLevelOuter[0] = 1.0; gl_TessLevelOuter[2] = screenSpaceTessFactor(gl_in[1].gl_Position, gl_in[2].gl_Position);
gl_TessLevelOuter[1] = 1.0; gl_TessLevelOuter[3] = screenSpaceTessFactor(gl_in[2].gl_Position, gl_in[3].gl_Position);
gl_TessLevelOuter[2] = 1.0; gl_TessLevelInner[0] = mix(gl_TessLevelOuter[0], gl_TessLevelOuter[3], 0.5);
gl_TessLevelOuter[3] = 1.0; gl_TessLevelInner[1] = mix(gl_TessLevelOuter[2], gl_TessLevelOuter[1], 0.5);
}
else
{
// Tessellation factor can be set to zero by example
// to demonstrate a simple passthrough
gl_TessLevelInner[0] = 1.0;
gl_TessLevelInner[1] = 1.0;
gl_TessLevelOuter[0] = 1.0;
gl_TessLevelOuter[1] = 1.0;
gl_TessLevelOuter[2] = 1.0;
gl_TessLevelOuter[3] = 1.0;
}
} }
} }
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;

View file

@ -8,8 +8,11 @@ layout (set = 0, binding = 0) uniform UBO
mat4 projection; mat4 projection;
mat4 modelview; mat4 modelview;
vec4 lightPos; vec4 lightPos;
vec4 frustumPlanes[6];
float displacementFactor; float displacementFactor;
float tessellationFalloff; float tessellationFactor;
vec2 viewportDim;
float tessellatedEdgeSize;
} ubo; } ubo;
layout (set = 0, binding = 1) uniform sampler2D displacementMap; layout (set = 0, binding = 1) uniform sampler2D displacementMap;

View file

@ -20,7 +20,7 @@
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include "vulkanexamplebase.h" #include "vulkanexamplebase.h"
#include "vulkanMeshLoader.hpp" #include "frustum.hpp"
#define VERTEX_BUFFER_BIND_ID 0 #define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false #define ENABLE_VALIDATION false
@ -66,8 +66,9 @@ public:
glm::mat4 projection; glm::mat4 projection;
glm::mat4 modelview; glm::mat4 modelview;
glm::vec4 lightPos = glm::vec4(0.0f, -2.0f, 0.0f, 0.0f); glm::vec4 lightPos = glm::vec4(0.0f, -2.0f, 0.0f, 0.0f);
float displacementFactor = 16.0f * 2.0f; glm::vec4 frustumPlanes[6];
float tessellationFactor = 0.75f; float displacementFactor = 32.0f;
float tessellationFactor = 1.0f;
glm::vec2 viewportDim; glm::vec2 viewportDim;
// Desired size of tessellated quad patch edge // Desired size of tessellated quad patch edge
float tessellatedEdgeSize = 20.0f; float tessellatedEdgeSize = 20.0f;
@ -99,6 +100,17 @@ public:
VkDescriptorSet skysphere; VkDescriptorSet skysphere;
} descriptorSets; } descriptorSets;
// Pipeline statistics
struct {
VkBuffer buffer;
VkDeviceMemory memory;
} queryResult;
VkQueryPool queryPool;
uint64_t pipelineStats[2] = { 0 };
// View frustum passed to tessellation control shader for culling
vkTools::Frustum frustum;
VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{ {
enableTextOverlay = true; enableTextOverlay = true;
@ -140,6 +152,58 @@ public:
textureLoader->destroyTexture(textures.heightMap); textureLoader->destroyTexture(textures.heightMap);
textureLoader->destroyTexture(textures.skySphere); textureLoader->destroyTexture(textures.skySphere);
textureLoader->destroyTexture(textures.terrainArray); textureLoader->destroyTexture(textures.terrainArray);
vkDestroyQueryPool(device, queryPool, nullptr);
vkDestroyBuffer(device, queryResult.buffer, nullptr);
vkFreeMemory(device, queryResult.memory, nullptr);
}
// Setup pool and buffer for storing pipeline statistics results
void setupQueryResultBuffer()
{
uint32_t bufSize = 2 * sizeof(uint64_t);
VkMemoryRequirements memReqs;
VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo();
VkBufferCreateInfo bufferCreateInfo =
vkTools::initializers::bufferCreateInfo(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
bufSize);
// Results are saved in a host visible buffer for easy access by the application
VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &queryResult.buffer));
vkGetBufferMemoryRequirements(device, queryResult.buffer, &memReqs);
memAlloc.allocationSize = memReqs.size;
memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &queryResult.memory));
VK_CHECK_RESULT(vkBindBufferMemory(device, queryResult.buffer, queryResult.memory, 0));
// Create query pool
VkQueryPoolCreateInfo queryPoolInfo = {};
queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
queryPoolInfo.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS;
queryPoolInfo.pipelineStatistics =
VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT;
queryPoolInfo.queryCount = 2;
VK_CHECK_RESULT(vkCreateQueryPool(device, &queryPoolInfo, NULL, &queryPool));
}
// Retrieves the results of the pipeline statistics query submitted to the command buffer
void getQueryResults()
{
// We use vkGetQueryResults to copy the results into a host visible buffer
vkGetQueryPoolResults(
device,
queryPool,
0,
1,
sizeof(pipelineStats),
pipelineStats,
sizeof(uint64_t),
VK_QUERY_RESULT_64_BIT);
} }
void loadTextures() void loadTextures()
@ -223,6 +287,8 @@ public:
VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
vkCmdResetQueryPool(drawCmdBuffers[i], queryPool, 0, 2);
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
@ -243,11 +309,16 @@ public:
vkCmdDrawIndexed(drawCmdBuffers[i], meshes.skysphere.indexCount, 1, 0, 0, 0); vkCmdDrawIndexed(drawCmdBuffers[i], meshes.skysphere.indexCount, 1, 0, 0, 0);
// Terrrain // Terrrain
// Begin pipeline statistics query
vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 0, VK_QUERY_CONTROL_PRECISE_BIT);
// Render
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.terrain); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.terrain);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.terrain, 0, 1, &descriptorSets.terrain, 0, NULL); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.terrain, 0, 1, &descriptorSets.terrain, 0, NULL);
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.object.vertices.buf, offsets); vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.object.vertices.buf, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.object.indices.buf, 0, VK_INDEX_TYPE_UINT32); vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.object.indices.buf, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], meshes.object.indexCount, 1, 0, 0, 0); vkCmdDrawIndexed(drawCmdBuffers[i], meshes.object.indexCount, 1, 0, 0, 0);
// End pipeline statistics query
vkCmdEndQuery(drawCmdBuffers[i], queryPool, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]); vkCmdEndRenderPass(drawCmdBuffers[i]);
@ -285,7 +356,7 @@ public:
vertices[index].pos[0] = x * wx + wx / 2.0f - (float)PATCH_SIZE * wx / 2.0f; vertices[index].pos[0] = x * wx + wx / 2.0f - (float)PATCH_SIZE * wx / 2.0f;
vertices[index].pos[1] = 0.0f; vertices[index].pos[1] = 0.0f;
vertices[index].pos[2] = y * wy + wy / 2.0f - (float)PATCH_SIZE * wy / 2.0f; vertices[index].pos[2] = y * wy + wy / 2.0f - (float)PATCH_SIZE * wy / 2.0f;
vertices[index].normal = glm::vec3(0.0f, -1.0f, 0.0f); vertices[index].normal = glm::vec3(0.0f, 1.0f, 0.0f);
vertices[index].uv = glm::vec2((float)x / PATCH_SIZE, (float)y / PATCH_SIZE) * UV_SCALE; vertices[index].uv = glm::vec2((float)x / PATCH_SIZE, (float)y / PATCH_SIZE) * UV_SCALE;
} }
} }
@ -459,7 +530,7 @@ public:
// Binding 1 : Height map // Binding 1 : Height map
vkTools::initializers::descriptorSetLayoutBinding( vkTools::initializers::descriptorSetLayoutBinding(
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
1), 1),
// Binding 3 : Terrain texture array layers // Binding 3 : Terrain texture array layers
vkTools::initializers::descriptorSetLayoutBinding( vkTools::initializers::descriptorSetLayoutBinding(
@ -685,6 +756,9 @@ public:
uboTess.lightPos.y = -0.5f - uboTess.displacementFactor; // todo: Not uesed yet uboTess.lightPos.y = -0.5f - uboTess.displacementFactor; // todo: Not uesed yet
uboTess.viewportDim = glm::vec2((float)width, (float)height); uboTess.viewportDim = glm::vec2((float)width, (float)height);
frustum.update(uboTess.projection * uboTess.modelview);
memcpy(uboTess.frustumPlanes, frustum.planes.data(), sizeof(glm::vec4) * 6);
float savedFactor = uboTess.tessellationFactor; float savedFactor = uboTess.tessellationFactor;
if (!tessellation) if (!tessellation)
{ {
@ -721,6 +795,9 @@ public:
// Submit to queue // Submit to queue
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
// Read query results for displaying in next frame
getQueryResults();
VulkanExampleBase::submitFrame(); VulkanExampleBase::submitFrame();
} }
@ -728,8 +805,9 @@ public:
{ {
VulkanExampleBase::prepare(); VulkanExampleBase::prepare();
loadMeshes(); loadMeshes();
generateTerrain();
loadTextures(); loadTextures();
generateTerrain();
setupQueryResultBuffer();
setupVertexDescriptions(); setupVertexDescriptions();
prepareUniformBuffers(); prepareUniformBuffers();
setupDescriptorSetLayouts(); setupDescriptorSetLayouts();
@ -810,6 +888,10 @@ public:
textOverlay->addText("Press \"f\" to toggle wireframe", 5.0f, 100.0f, VulkanTextOverlay::alignLeft); textOverlay->addText("Press \"f\" to toggle wireframe", 5.0f, 100.0f, VulkanTextOverlay::alignLeft);
textOverlay->addText("Press \"t\" to toggle tessellation", 5.0f, 115.0f, VulkanTextOverlay::alignLeft); textOverlay->addText("Press \"t\" to toggle tessellation", 5.0f, 115.0f, VulkanTextOverlay::alignLeft);
#endif #endif
textOverlay->addText("pipeline stats:", width - 5.0f, 5.0f, VulkanTextOverlay::alignRight);
textOverlay->addText("VS:" + std::to_string(pipelineStats[0]), width - 5.0f, 20.0f, VulkanTextOverlay::alignRight);
textOverlay->addText("TE:" + std::to_string(pipelineStats[1]), width - 5.0f, 35.0f, VulkanTextOverlay::alignRight);
} }
}; };