In the occlusion query example, uniform buffers containing visibility flags are only updated when the view changes, and visibility information is calculated from occlusion queries coming from the previous frame. This means a quick mouse movement can produce a visual glitch by which the number of visible fragments becomes zero (and this is correctly reflected in the UI overlay) but the color of the model remains unchanged, or vice versa, until the view is changed again with the mouse. To fix this, update uniform buffers before preparing every frame as part of the draw() method.
608 lines
20 KiB
C++
608 lines
20 KiB
C++
/*
|
|
* Vulkan Example - Using occlusion query for visbility testing
|
|
*
|
|
* 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 "VulkanBuffer.hpp"
|
|
#include "VulkanModel.hpp"
|
|
|
|
#define VERTEX_BUFFER_BIND_ID 0
|
|
#define ENABLE_VALIDATION false
|
|
|
|
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 {
|
|
vks::Model teapot;
|
|
vks::Model plane;
|
|
vks::Model sphere;
|
|
} models;
|
|
|
|
struct {
|
|
vks::Buffer occluder;
|
|
vks::Buffer teapot;
|
|
vks::Buffer sphere;
|
|
} uniformBuffers;
|
|
|
|
struct UBOVS {
|
|
glm::mat4 projection;
|
|
glm::mat4 model;
|
|
glm::vec4 lightPos = glm::vec4(10.0f, 10.0f, 10.0f, 1.0f);
|
|
float visible;
|
|
} uboVS;
|
|
|
|
struct {
|
|
VkPipelineVertexInputStateCreateInfo inputState;
|
|
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
|
|
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
|
|
} vertices;
|
|
|
|
struct {
|
|
VkPipeline solid;
|
|
VkPipeline occluder;
|
|
// Pipeline with basic shaders used for occlusion pass
|
|
VkPipeline simple;
|
|
} pipelines;
|
|
|
|
struct {
|
|
VkDescriptorSet teapot;
|
|
VkDescriptorSet sphere;
|
|
} descriptorSets;
|
|
|
|
VkPipelineLayout pipelineLayout;
|
|
VkDescriptorSet descriptorSet;
|
|
VkDescriptorSetLayout descriptorSetLayout;
|
|
|
|
// Pool that stores all occlusion queries
|
|
VkQueryPool queryPool;
|
|
|
|
// Passed query samples
|
|
uint64_t passedSamples[2] = { 1,1 };
|
|
|
|
VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
|
|
{
|
|
zoom = -35.0f;
|
|
zoomSpeed = 2.5f;
|
|
rotationSpeed = 0.5f;
|
|
rotation = { 0.0, -123.75, 0.0 };
|
|
title = "Occlusion queries";
|
|
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.occluder, nullptr);
|
|
vkDestroyPipeline(device, pipelines.simple, nullptr);
|
|
|
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
|
|
|
vkDestroyQueryPool(device, queryPool, nullptr);
|
|
|
|
uniformBuffers.occluder.destroy();
|
|
uniformBuffers.sphere.destroy();
|
|
uniformBuffers.teapot.destroy();
|
|
|
|
models.sphere.destroy();
|
|
models.plane.destroy();
|
|
models.teapot.destroy();
|
|
}
|
|
|
|
// Create a query pool for storing the occlusion query result
|
|
void setupQueryPool()
|
|
{
|
|
VkQueryPoolCreateInfo queryPoolInfo = {};
|
|
queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
|
queryPoolInfo.queryType = VK_QUERY_TYPE_OCCLUSION;
|
|
queryPoolInfo.queryCount = 2;
|
|
VK_CHECK_RESULT(vkCreateQueryPool(device, &queryPoolInfo, NULL, &queryPool));
|
|
}
|
|
|
|
// Retrieves the results of the occlusion queries submitted to the command buffer
|
|
void getQueryResults()
|
|
{
|
|
// We use vkGetQueryResults to copy the results into a host visible buffer
|
|
vkGetQueryPoolResults(
|
|
device,
|
|
queryPool,
|
|
0,
|
|
2,
|
|
sizeof(passedSamples),
|
|
passedSamples,
|
|
sizeof(uint64_t),
|
|
// Store results a 64 bit values and wait until the results have been finished
|
|
// If you don't want to wait, you can use VK_QUERY_RESULT_WITH_AVAILABILITY_BIT
|
|
// which also returns the state of the result (ready) in the result
|
|
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_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)
|
|
{
|
|
// Set target frame buffer
|
|
renderPassBeginInfo.framebuffer = frameBuffers[i];
|
|
|
|
VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
|
|
|
|
// Reset query pool
|
|
// Must be done outside of render pass
|
|
vkCmdResetQueryPool(drawCmdBuffers[i], queryPool, 0, 2);
|
|
|
|
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 };
|
|
|
|
glm::mat4 modelMatrix = glm::mat4(1.0f);
|
|
|
|
// Occlusion pass
|
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.simple);
|
|
|
|
// Occluder first
|
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.plane.vertices.buffer, offsets);
|
|
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.plane.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
vkCmdDrawIndexed(drawCmdBuffers[i], models.plane.indexCount, 1, 0, 0, 0);
|
|
|
|
// Teapot
|
|
vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 0, VK_FLAGS_NONE);
|
|
|
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.teapot, 0, NULL);
|
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.teapot.vertices.buffer, offsets);
|
|
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.teapot.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
vkCmdDrawIndexed(drawCmdBuffers[i], models.teapot.indexCount, 1, 0, 0, 0);
|
|
|
|
vkCmdEndQuery(drawCmdBuffers[i], queryPool, 0);
|
|
|
|
// Sphere
|
|
vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 1, VK_FLAGS_NONE);
|
|
|
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.sphere, 0, NULL);
|
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.sphere.vertices.buffer, offsets);
|
|
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.sphere.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
vkCmdDrawIndexed(drawCmdBuffers[i], models.sphere.indexCount, 1, 0, 0, 0);
|
|
|
|
vkCmdEndQuery(drawCmdBuffers[i], queryPool, 1);
|
|
|
|
// Visible pass
|
|
// Clear color and depth attachments
|
|
VkClearAttachment clearAttachments[2] = {};
|
|
|
|
clearAttachments[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
clearAttachments[0].clearValue.color = defaultClearColor;
|
|
clearAttachments[0].colorAttachment = 0;
|
|
|
|
clearAttachments[1].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
clearAttachments[1].clearValue.depthStencil = { 1.0f, 0 };
|
|
|
|
VkClearRect clearRect = {};
|
|
clearRect.layerCount = 1;
|
|
clearRect.rect.offset = { 0, 0 };
|
|
clearRect.rect.extent = { width, height };
|
|
|
|
vkCmdClearAttachments(
|
|
drawCmdBuffers[i],
|
|
2,
|
|
clearAttachments,
|
|
1,
|
|
&clearRect);
|
|
|
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);
|
|
|
|
// Teapot
|
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.teapot, 0, NULL);
|
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.teapot.vertices.buffer, offsets);
|
|
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.teapot.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
vkCmdDrawIndexed(drawCmdBuffers[i], models.teapot.indexCount, 1, 0, 0, 0);
|
|
|
|
// Sphere
|
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.sphere, 0, NULL);
|
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.sphere.vertices.buffer, offsets);
|
|
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.sphere.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
vkCmdDrawIndexed(drawCmdBuffers[i], models.sphere.indexCount, 1, 0, 0, 0);
|
|
|
|
// Occluder
|
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.occluder);
|
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.plane.vertices.buffer, offsets);
|
|
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.plane.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
vkCmdDrawIndexed(drawCmdBuffers[i], models.plane.indexCount, 1, 0, 0, 0);
|
|
|
|
drawUI(drawCmdBuffers[i]);
|
|
|
|
vkCmdEndRenderPass(drawCmdBuffers[i]);
|
|
|
|
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
|
|
}
|
|
}
|
|
|
|
void draw()
|
|
{
|
|
updateUniformBuffers();
|
|
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()
|
|
{
|
|
models.plane.loadFromFile(getAssetPath() + "models/plane_z.3ds", vertexLayout, 0.4f, vulkanDevice, queue);
|
|
models.teapot.loadFromFile(getAssetPath() + "models/teapot.3ds", vertexLayout, 0.3f, vulkanDevice, queue);
|
|
models.sphere.loadFromFile(getAssetPath() + "models/sphere.3ds", vertexLayout, 0.3f, vulkanDevice, queue);
|
|
}
|
|
|
|
void setupVertexDescriptions()
|
|
{
|
|
// Binding description
|
|
vertices.bindingDescriptions.resize(1);
|
|
vertices.bindingDescriptions[0] =
|
|
vks::initializers::vertexInputBindingDescription(
|
|
VERTEX_BUFFER_BIND_ID,
|
|
vertexLayout.stride(),
|
|
VK_VERTEX_INPUT_RATE_VERTEX);
|
|
|
|
// Attribute descriptions
|
|
// Describes memory layout and shader positions
|
|
vertices.attributeDescriptions.resize(3);
|
|
// Location 0 : Position
|
|
vertices.attributeDescriptions[0] =
|
|
vks::initializers::vertexInputAttributeDescription(
|
|
VERTEX_BUFFER_BIND_ID,
|
|
0,
|
|
VK_FORMAT_R32G32B32_SFLOAT,
|
|
0);
|
|
// Location 1 : Normal
|
|
vertices.attributeDescriptions[1] =
|
|
vks::initializers::vertexInputAttributeDescription(
|
|
VERTEX_BUFFER_BIND_ID,
|
|
1,
|
|
VK_FORMAT_R32G32B32_SFLOAT,
|
|
sizeof(float) * 3);
|
|
// Location 3 : Color
|
|
vertices.attributeDescriptions[2] =
|
|
vks::initializers::vertexInputAttributeDescription(
|
|
VERTEX_BUFFER_BIND_ID,
|
|
2,
|
|
VK_FORMAT_R32G32B32_SFLOAT,
|
|
sizeof(float) * 6);
|
|
|
|
vertices.inputState = vks::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 =
|
|
{
|
|
// One uniform buffer block for each mesh
|
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3)
|
|
};
|
|
|
|
VkDescriptorPoolCreateInfo descriptorPoolInfo =
|
|
vks::initializers::descriptorPoolCreateInfo(
|
|
poolSizes.size(),
|
|
poolSizes.data(),
|
|
3);
|
|
|
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
|
}
|
|
|
|
void setupDescriptorSetLayout()
|
|
{
|
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
|
|
{
|
|
// Binding 0 : Vertex shader uniform buffer
|
|
vks::initializers::descriptorSetLayoutBinding(
|
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
VK_SHADER_STAGE_VERTEX_BIT,
|
|
0)
|
|
};
|
|
|
|
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 setupDescriptorSets()
|
|
{
|
|
VkDescriptorSetAllocateInfo allocInfo =
|
|
vks::initializers::descriptorSetAllocateInfo(
|
|
descriptorPool,
|
|
&descriptorSetLayout,
|
|
1);
|
|
|
|
// Occluder (plane)
|
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
|
|
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
|
|
{
|
|
// Binding 0 : Vertex shader uniform buffer
|
|
vks::initializers::writeDescriptorSet(
|
|
descriptorSet,
|
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
0,
|
|
&uniformBuffers.occluder.descriptor)
|
|
};
|
|
|
|
vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
|
|
|
|
// Teapot
|
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.teapot));
|
|
writeDescriptorSets[0].dstSet = descriptorSets.teapot;
|
|
writeDescriptorSets[0].pBufferInfo = &uniformBuffers.teapot.descriptor;
|
|
vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
|
|
|
|
// Sphere
|
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.sphere));
|
|
writeDescriptorSets[0].dstSet = descriptorSets.sphere;
|
|
writeDescriptorSets[0].pBufferInfo = &uniformBuffers.sphere.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_BACK_BIT,
|
|
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
|
|
};
|
|
VkPipelineDynamicStateCreateInfo dynamicState =
|
|
vks::initializers::pipelineDynamicStateCreateInfo(
|
|
dynamicStateEnables.data(),
|
|
dynamicStateEnables.size(),
|
|
0);
|
|
|
|
// Solid rendering pipeline
|
|
// Load shaders
|
|
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
|
|
|
shaderStages[0] = loadShader(getAssetPath() + "shaders/occlusionquery/mesh.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
|
shaderStages[1] = loadShader(getAssetPath() + "shaders/occlusionquery/mesh.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
|
|
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
|
|
vks::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();
|
|
|
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid));
|
|
|
|
// Basic pipeline for coloring occluded objects
|
|
shaderStages[0] = loadShader(getAssetPath() + "shaders/occlusionquery/simple.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
|
shaderStages[1] = loadShader(getAssetPath() + "shaders/occlusionquery/simple.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
rasterizationState.cullMode = VK_CULL_MODE_NONE;
|
|
|
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.simple));
|
|
|
|
// Visual pipeline for the occluder
|
|
shaderStages[0] = loadShader(getAssetPath() + "shaders/occlusionquery/occluder.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
|
shaderStages[1] = loadShader(getAssetPath() + "shaders/occlusionquery/occluder.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
|
|
// Enable blending
|
|
blendAttachmentState.blendEnable = VK_TRUE;
|
|
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
|
|
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_COLOR;
|
|
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
|
|
|
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.occluder));
|
|
}
|
|
|
|
// 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.occluder,
|
|
sizeof(uboVS)));
|
|
|
|
// Teapot
|
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
|
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
&uniformBuffers.teapot,
|
|
sizeof(uboVS)));
|
|
|
|
// Sphere
|
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
|
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
&uniformBuffers.sphere,
|
|
sizeof(uboVS)));
|
|
|
|
// Map persistent
|
|
VK_CHECK_RESULT(uniformBuffers.occluder.map());
|
|
VK_CHECK_RESULT(uniformBuffers.teapot.map());
|
|
VK_CHECK_RESULT(uniformBuffers.sphere.map());
|
|
|
|
updateUniformBuffers();
|
|
}
|
|
|
|
void updateUniformBuffers()
|
|
{
|
|
// Vertex shader
|
|
uboVS.projection = glm::perspective(glm::radians(60.0f), (float)width / (float)height, 0.1f, 256.0f);
|
|
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, zoom));
|
|
|
|
glm::mat4 rotMatrix = glm::mat4(1.0f);
|
|
rotMatrix = glm::rotate(rotMatrix, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
|
|
rotMatrix = glm::rotate(rotMatrix, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
|
|
rotMatrix = glm::rotate(rotMatrix, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
|
|
|
|
uboVS.model = viewMatrix * rotMatrix;
|
|
|
|
uint8_t *pData;
|
|
|
|
// Occluder
|
|
uboVS.visible = 1.0f;
|
|
memcpy(uniformBuffers.occluder.mapped, &uboVS, sizeof(uboVS));
|
|
|
|
// Teapot
|
|
// Toggle color depending on visibility
|
|
uboVS.visible = (passedSamples[0] > 0) ? 1.0f : 0.0f;
|
|
uboVS.model = viewMatrix * rotMatrix * glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -10.0f));
|
|
memcpy(uniformBuffers.teapot.mapped, &uboVS, sizeof(uboVS));
|
|
|
|
// Sphere
|
|
// Toggle color depending on visibility
|
|
uboVS.visible = (passedSamples[1] > 0) ? 1.0f : 0.0f;
|
|
uboVS.model = viewMatrix * rotMatrix * glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 10.0f));
|
|
memcpy(uniformBuffers.sphere.mapped, &uboVS, sizeof(uboVS));
|
|
}
|
|
|
|
void prepare()
|
|
{
|
|
VulkanExampleBase::prepare();
|
|
loadAssets();
|
|
setupQueryPool();
|
|
setupVertexDescriptions();
|
|
prepareUniformBuffers();
|
|
setupDescriptorSetLayout();
|
|
preparePipelines();
|
|
setupDescriptorPool();
|
|
setupDescriptorSets();
|
|
buildCommandBuffers();
|
|
prepared = true;
|
|
}
|
|
|
|
virtual void render()
|
|
{
|
|
if (!prepared)
|
|
return;
|
|
draw();
|
|
}
|
|
|
|
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
|
{
|
|
if (overlay->header("Occlusion query results")) {
|
|
overlay->text("Teapot: %d samples passed", passedSamples[0]);
|
|
overlay->text("Sphere: %d samples passed", passedSamples[1]);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
VULKAN_EXAMPLE_MAIN()
|