2016-07-22 23:16:08 +02:00
/*
2020-05-29 16:08:53 +01:00
* Vulkan Example - Indirect drawing
2016-07-22 23:16:08 +02:00
*
* Copyright ( C ) 2016 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*
* Summary :
2016-08-01 21:43:47 +02:00
* Use a device local buffer that stores draw commands for instanced rendering of different meshes stored
* in the same buffer .
2016-07-22 23:16:08 +02:00
*
2020-05-29 16:08:53 +01:00
* Indirect drawing offloads draw command generation and offers the ability to update them on the GPU
2016-07-22 23:16:08 +02:00
* without the CPU having to touch the buffer again , also reducing the number of drawcalls .
*
* The example shows how to setup and fill such a buffer on the CPU side , stages it to the device and
2016-08-01 21:43:47 +02:00
* shows how to render it using only one draw command .
*
* See readme . md for details
2016-07-22 23:16:08 +02:00
*
*/
# include "vulkanexamplebase.h"
2020-07-28 20:20:38 +02:00
# include "VulkanglTFModel.h"
2016-07-22 23:16:08 +02:00
# define VERTEX_BUFFER_BIND_ID 0
# define INSTANCE_BUFFER_BIND_ID 1
// Number of instances per object
2016-08-01 22:01:36 +02:00
# if defined(__ANDROID__)
# define OBJECT_INSTANCE_COUNT 1024
// Circular range of plant distribution
# define PLANT_RADIUS 20.0f
# else
2016-07-22 23:16:08 +02:00
# define OBJECT_INSTANCE_COUNT 2048
2016-08-01 21:43:47 +02:00
// Circular range of plant distribution
# define PLANT_RADIUS 25.0f
2016-08-01 22:01:36 +02:00
# endif
2016-07-22 23:16:08 +02:00
class VulkanExample : public VulkanExampleBase
{
public :
struct {
2017-02-11 14:18:24 +01:00
vks : : Texture2DArray plants ;
vks : : Texture2D ground ;
} textures ;
2016-07-22 23:16:08 +02:00
struct {
2020-07-28 20:20:38 +02:00
vkglTF : : Model plants ;
vkglTF : : Model ground ;
vkglTF : : Model skysphere ;
2017-02-11 14:18:24 +01:00
} models ;
2016-07-22 23:16:08 +02:00
// Per-instance data block
struct InstanceData {
glm : : vec3 pos ;
glm : : vec3 rot ;
float scale ;
uint32_t texIndex ;
} ;
// Contains the instanced data
2017-02-12 10:44:51 +01:00
vks : : Buffer instanceBuffer ;
2016-07-22 23:16:08 +02:00
// Contains the indirect drawing commands
2017-02-12 10:44:51 +01:00
vks : : Buffer indirectCommandsBuffer ;
2016-07-22 23:16:08 +02:00
uint32_t indirectDrawCount ;
struct {
glm : : mat4 projection ;
glm : : mat4 view ;
} uboVS ;
struct {
2017-02-12 10:44:51 +01:00
vks : : Buffer scene ;
2016-07-22 23:16:08 +02:00
} uniformData ;
struct {
2016-08-01 21:43:47 +02:00
VkPipeline plants ;
VkPipeline ground ;
VkPipeline skysphere ;
2016-07-22 23:16:08 +02:00
} pipelines ;
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
2016-08-01 21:43:47 +02:00
VkSampler samplerRepeat ;
2016-07-22 23:16:08 +02:00
uint32_t objectCount = 0 ;
// Store the indirect draw commands containing index offsets and instance count per object
std : : vector < VkDrawIndexedIndirectCommand > indirectCommands ;
2023-12-30 13:15:37 +01:00
VulkanExample ( ) : VulkanExampleBase ( )
2016-07-22 23:16:08 +02:00
{
2017-11-01 14:22:10 +01:00
title = " Indirect rendering " ;
2016-08-01 21:43:47 +02:00
camera . type = Camera : : CameraType : : firstperson ;
camera . setPerspective ( 60.0f , ( float ) width / ( float ) height , 0.1f , 512.0f ) ;
camera . setRotation ( glm : : vec3 ( - 12.0f , 159.0f , 0.0f ) ) ;
camera . setTranslation ( glm : : vec3 ( 0.4f , 1.25f , 0.0f ) ) ;
camera . movementSpeed = 5.0f ;
2016-07-22 23:16:08 +02:00
}
~ VulkanExample ( )
{
2016-08-01 21:43:47 +02:00
vkDestroyPipeline ( device , pipelines . plants , nullptr ) ;
vkDestroyPipeline ( device , pipelines . ground , nullptr ) ;
2016-08-04 18:57:32 +02:00
vkDestroyPipeline ( device , pipelines . skysphere , nullptr ) ;
2016-07-22 23:16:08 +02:00
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
2017-02-09 21:55:35 +01:00
textures . plants . destroy ( ) ;
textures . ground . destroy ( ) ;
2016-07-22 23:16:08 +02:00
instanceBuffer . destroy ( ) ;
indirectCommandsBuffer . destroy ( ) ;
uniformData . scene . destroy ( ) ;
}
2020-05-29 16:08:53 +01:00
// Enable physical device features required for this example
2017-03-31 12:37:53 +02:00
virtual void getEnabledFeatures ( )
{
2017-06-07 22:44:29 +02:00
// Example uses multi draw indirect if available
2017-03-31 12:37:53 +02:00
if ( deviceFeatures . multiDrawIndirect ) {
enabledFeatures . multiDrawIndirect = VK_TRUE ;
}
2017-06-07 22:44:29 +02:00
// Enable anisotropic filtering if supported
if ( deviceFeatures . samplerAnisotropy ) {
enabledFeatures . samplerAnisotropy = VK_TRUE ;
2017-03-31 12:37:53 +02:00
}
2017-06-07 22:44:29 +02:00
} ;
2017-03-31 12:37:53 +02:00
2016-07-22 23:16:08 +02:00
void buildCommandBuffers ( )
{
2017-02-12 11:12:42 +01:00
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
2016-07-22 23:16:08 +02:00
VkClearValue clearValues [ 2 ] ;
2016-08-01 21:43:47 +02:00
clearValues [ 0 ] . color = { { 0.18f , 0.27f , 0.5f , 0.0f } } ;
2016-07-22 23:16:08 +02:00
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
2017-02-12 11:12:42 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks : : initializers : : renderPassBeginInfo ( ) ;
2016-07-22 23:16:08 +02:00
renderPassBeginInfo . renderPass = renderPass ;
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 ) ;
2017-02-12 11:12:42 +01:00
VkViewport viewport = vks : : initializers : : viewport ( ( float ) width , ( float ) height , 0.0f , 1.0f ) ;
2016-07-22 23:16:08 +02:00
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
2017-02-12 11:12:42 +01:00
VkRect2D scissor = vks : : initializers : : rect2D ( width , height , 0 , 0 ) ;
2016-07-22 23:16:08 +02:00
vkCmdSetScissor ( drawCmdBuffers [ i ] , 0 , 1 , & scissor ) ;
2016-08-01 21:43:47 +02:00
VkDeviceSize offsets [ 1 ] = { 0 } ;
2016-07-22 23:16:08 +02:00
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSet , 0 , NULL ) ;
2020-07-28 20:20:38 +02:00
// Skysphere
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelines . skysphere ) ;
models . skysphere . draw ( drawCmdBuffers [ i ] ) ;
// Ground
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelines . ground ) ;
models . ground . draw ( drawCmdBuffers [ i ] ) ;
// [POI] Instanced multi draw rendering of the plants
2016-08-01 21:43:47 +02:00
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelines . plants ) ;
2016-07-22 23:16:08 +02:00
// Binding point 0 : Mesh vertex buffer
2017-02-11 14:18:24 +01:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & models . plants . vertices . buffer , offsets ) ;
2016-07-22 23:16:08 +02:00
// Binding point 1 : Instance data buffer
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , INSTANCE_BUFFER_BIND_ID , 1 , & instanceBuffer . buffer , offsets ) ;
2020-05-29 16:08:53 +01:00
2017-02-11 14:18:24 +01:00
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , models . plants . indices . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
2016-07-22 23:16:08 +02:00
2016-08-04 18:57:32 +02:00
// If the multi draw feature is supported:
2020-08-09 14:39:32 +02:00
// One draw call for an arbitrary number of objects
2016-07-22 23:16:08 +02:00
// Index offsets and instance count are taken from the indirect buffer
2016-08-04 18:57:32 +02:00
if ( vulkanDevice - > features . multiDrawIndirect )
{
vkCmdDrawIndexedIndirect ( drawCmdBuffers [ i ] , indirectCommandsBuffer . buffer , 0 , indirectDrawCount , sizeof ( VkDrawIndexedIndirectCommand ) ) ;
}
else
{
// If multi draw is not available, we must issue separate draw commands
for ( auto j = 0 ; j < indirectCommands . size ( ) ; j + + )
{
vkCmdDrawIndexedIndirect ( drawCmdBuffers [ i ] , indirectCommandsBuffer . buffer , j * sizeof ( VkDrawIndexedIndirectCommand ) , 1 , sizeof ( VkDrawIndexedIndirectCommand ) ) ;
}
}
2016-07-22 23:16:08 +02:00
2018-08-30 21:08:02 +02:00
drawUI ( drawCmdBuffers [ i ] ) ;
2016-07-22 23:16:08 +02:00
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
}
}
2016-08-01 21:43:47 +02:00
void loadAssets ( )
2016-07-22 23:16:08 +02:00
{
2020-07-28 20:20:38 +02:00
const uint32_t glTFLoadingFlags = vkglTF : : FileLoadingFlags : : PreTransformVertices | vkglTF : : FileLoadingFlags : : PreMultiplyVertexColors | vkglTF : : FileLoadingFlags : : FlipY ;
models . plants . loadFromFile ( getAssetPath ( ) + " models/plants.gltf " , vulkanDevice , queue , glTFLoadingFlags ) ;
models . ground . loadFromFile ( getAssetPath ( ) + " models/plane_circle.gltf " , vulkanDevice , queue , glTFLoadingFlags ) ;
models . skysphere . loadFromFile ( getAssetPath ( ) + " models/sphere.gltf " , vulkanDevice , queue , glTFLoadingFlags ) ;
textures . plants . loadFromFile ( getAssetPath ( ) + " textures/texturearray_plants_rgba.ktx " , VK_FORMAT_R8G8B8A8_UNORM , vulkanDevice , queue ) ;
textures . ground . loadFromFile ( getAssetPath ( ) + " textures/ground_dry_rgba.ktx " , VK_FORMAT_R8G8B8A8_UNORM , vulkanDevice , queue ) ;
2016-07-22 23:16:08 +02:00
}
void setupDescriptorPool ( )
{
2020-07-28 20:20:38 +02:00
std : : vector < VkDescriptorPoolSize > poolSizes = {
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 ) ,
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 2 ) ,
2016-07-22 23:16:08 +02:00
} ;
2020-07-28 20:20:38 +02:00
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks : : initializers : : descriptorPoolCreateInfo ( poolSizes , 2 ) ;
2016-07-22 23:16:08 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
}
void setupDescriptorSetLayout ( )
{
2020-07-28 20:20:38 +02:00
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings = {
2016-08-01 21:43:47 +02:00
// Binding 0: Vertex shader uniform buffer
2020-07-28 20:20:38 +02:00
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , VK_SHADER_STAGE_VERTEX_BIT , 0 ) ,
2016-08-01 21:43:47 +02:00
// Binding 1: Fragment shader combined sampler (plants texture array)
2020-07-28 20:20:38 +02:00
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , VK_SHADER_STAGE_FRAGMENT_BIT , 1 ) ,
2016-08-01 21:43:47 +02:00
// Binding 1: Fragment shader combined sampler (ground texture)
2020-07-28 20:20:38 +02:00
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , VK_SHADER_STAGE_FRAGMENT_BIT , 2 ) ,
2016-07-22 23:16:08 +02:00
} ;
2020-07-28 20:20:38 +02:00
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks : : initializers : : descriptorSetLayoutCreateInfo ( setLayoutBindings ) ;
2016-07-22 23:16:08 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
2020-07-28 20:20:38 +02:00
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks : : initializers : : pipelineLayoutCreateInfo ( & descriptorSetLayout , 1 ) ;
2016-07-22 23:16:08 +02:00
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
}
void setupDescriptorSet ( )
{
2020-07-28 20:20:38 +02:00
VkDescriptorSetAllocateInfo allocInfo = vks : : initializers : : descriptorSetAllocateInfo ( descriptorPool , & descriptorSetLayout , 1 ) ;
2016-07-22 23:16:08 +02:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
2020-07-28 20:20:38 +02:00
std : : vector < VkWriteDescriptorSet > writeDescriptorSets = {
2016-08-01 21:43:47 +02:00
// Binding 0: Vertex shader uniform buffer
2020-07-28 20:20:38 +02:00
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 0 , & uniformData . scene . descriptor ) ,
2020-05-29 16:08:53 +01:00
// Binding 1: Plants texture array combined
2020-07-28 20:20:38 +02:00
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 1 , & textures . plants . descriptor ) ,
2020-05-29 16:08:53 +01:00
// Binding 2: Ground texture combined
2020-07-28 20:20:38 +02:00
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 2 , & textures . ground . descriptor )
2016-07-22 23:16:08 +02:00
} ;
2020-07-28 20:20:38 +02:00
vkUpdateDescriptorSets ( device , static_cast < uint32_t > ( writeDescriptorSets . size ( ) ) , writeDescriptorSets . data ( ) , 0 , nullptr ) ;
2016-07-22 23:16:08 +02:00
}
void preparePipelines ( )
{
2020-07-28 20:20:38 +02:00
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_COUNTER_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 ) ;
2016-08-01 21:43:47 +02:00
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
2020-07-28 20:20:38 +02:00
VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks : : initializers : : pipelineCreateInfo ( pipelineLayout , renderPass ) ;
2016-07-22 23:16:08 +02:00
pipelineCreateInfo . pInputAssemblyState = & inputAssemblyState ;
pipelineCreateInfo . pRasterizationState = & rasterizationState ;
pipelineCreateInfo . pColorBlendState = & colorBlendState ;
pipelineCreateInfo . pMultisampleState = & multisampleState ;
pipelineCreateInfo . pViewportState = & viewportState ;
pipelineCreateInfo . pDepthStencilState = & depthStencilState ;
pipelineCreateInfo . pDynamicState = & dynamicState ;
2016-08-08 19:52:16 +02:00
pipelineCreateInfo . stageCount = static_cast < uint32_t > ( shaderStages . size ( ) ) ;
2016-07-22 23:16:08 +02:00
pipelineCreateInfo . pStages = shaderStages . data ( ) ;
2020-07-28 20:20:38 +02:00
// This example uses two different input states, one for the instanced part and one for non-instanced rendering
VkPipelineVertexInputStateCreateInfo inputState = vks : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
std : : vector < VkVertexInputBindingDescription > bindingDescriptions ;
std : : vector < VkVertexInputAttributeDescription > attributeDescriptions ;
// Vertex input bindings
// The instancing pipeline uses a vertex input state with two bindings
bindingDescriptions = {
// Binding point 0: Mesh vertex layout description at per-vertex rate
vks : : initializers : : vertexInputBindingDescription ( VERTEX_BUFFER_BIND_ID , sizeof ( vkglTF : : Vertex ) , VK_VERTEX_INPUT_RATE_VERTEX ) ,
// Binding point 1: Instanced data at per-instance rate
vks : : initializers : : vertexInputBindingDescription ( INSTANCE_BUFFER_BIND_ID , sizeof ( InstanceData ) , VK_VERTEX_INPUT_RATE_INSTANCE )
} ;
// Vertex attribute bindings
// Note that the shader declaration for per-vertex and per-instance attributes is the same, the different input rates are only stored in the bindings:
// instanced.vert:
// layout (location = 0) in vec3 inPos; Per-Vertex
// ...
// layout (location = 4) in vec3 instancePos; Per-Instance
attributeDescriptions = {
2020-08-09 14:39:32 +02:00
// Per-vertex attributes
2020-07-28 20:20:38 +02:00
// These are advanced for each vertex fetched by the vertex shader
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 0 , VK_FORMAT_R32G32B32_SFLOAT , 0 ) , // Location 0: Position
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 1 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 3 ) , // Location 1: Normal
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 2 , VK_FORMAT_R32G32_SFLOAT , sizeof ( float ) * 6 ) , // Location 2: Texture coordinates
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 3 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 8 ) , // Location 3: Color
// Per-Instance attributes
// These are fetched for each instance rendered
vks : : initializers : : vertexInputAttributeDescription ( INSTANCE_BUFFER_BIND_ID , 4 , VK_FORMAT_R32G32B32_SFLOAT , offsetof ( InstanceData , pos ) ) , // Location 4: Position
vks : : initializers : : vertexInputAttributeDescription ( INSTANCE_BUFFER_BIND_ID , 5 , VK_FORMAT_R32G32B32_SFLOAT , offsetof ( InstanceData , rot ) ) , // Location 5: Rotation
vks : : initializers : : vertexInputAttributeDescription ( INSTANCE_BUFFER_BIND_ID , 6 , VK_FORMAT_R32_SFLOAT , offsetof ( InstanceData , scale ) ) , // Location 6: Scale
vks : : initializers : : vertexInputAttributeDescription ( INSTANCE_BUFFER_BIND_ID , 7 , VK_FORMAT_R32_SINT , offsetof ( InstanceData , texIndex ) ) , // Location 7: Texture array layer index
} ;
inputState . pVertexBindingDescriptions = bindingDescriptions . data ( ) ;
inputState . pVertexAttributeDescriptions = attributeDescriptions . data ( ) ;
inputState . vertexBindingDescriptionCount = static_cast < uint32_t > ( bindingDescriptions . size ( ) ) ;
inputState . vertexAttributeDescriptionCount = static_cast < uint32_t > ( attributeDescriptions . size ( ) ) ;
pipelineCreateInfo . pVertexInputState = & inputState ;
2016-08-01 21:43:47 +02:00
// Indirect (and instanced) pipeline for the plants
2020-05-29 16:08:53 +01:00
shaderStages [ 0 ] = loadShader ( getShadersPath ( ) + " indirectdraw/indirectdraw.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getShadersPath ( ) + " indirectdraw/indirectdraw.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2016-08-01 21:43:47 +02:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipelines . plants ) ) ;
2020-07-28 20:20:38 +02:00
// Only use non-instanced vertex attributes for models rendered without instancing
inputState . vertexBindingDescriptionCount = 1 ;
inputState . vertexAttributeDescriptionCount = 4 ;
2016-08-01 21:43:47 +02:00
// Ground
2020-05-29 16:08:53 +01:00
shaderStages [ 0 ] = loadShader ( getShadersPath ( ) + " indirectdraw/ground.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getShadersPath ( ) + " indirectdraw/ground.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2020-07-28 20:20:38 +02:00
rasterizationState . cullMode = VK_CULL_MODE_BACK_BIT ;
2016-08-01 21:43:47 +02:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipelines . ground ) ) ;
// Skysphere
2020-05-29 16:08:53 +01:00
shaderStages [ 0 ] = loadShader ( getShadersPath ( ) + " indirectdraw/skysphere.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getShadersPath ( ) + " indirectdraw/skysphere.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2020-07-28 20:20:38 +02:00
depthStencilState . depthWriteEnable = VK_FALSE ;
rasterizationState . cullMode = VK_CULL_MODE_FRONT_BIT ;
2016-08-01 21:43:47 +02:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipelines . skysphere ) ) ;
2016-07-22 23:16:08 +02:00
}
// Prepare (and stage) a buffer containing the indirect draw commands
void prepareIndirectData ( )
{
2016-08-01 21:43:47 +02:00
indirectCommands . clear ( ) ;
2016-07-22 23:16:08 +02:00
2020-07-28 20:20:38 +02:00
// Create on indirect command for node in the scene with a mesh attached to it
2016-08-01 21:43:47 +02:00
uint32_t m = 0 ;
2020-07-28 20:20:38 +02:00
for ( auto & node : models . plants . nodes )
2016-08-01 21:43:47 +02:00
{
2020-07-28 20:20:38 +02:00
if ( node - > mesh )
{
VkDrawIndexedIndirectCommand indirectCmd { } ;
indirectCmd . instanceCount = OBJECT_INSTANCE_COUNT ;
indirectCmd . firstInstance = m * OBJECT_INSTANCE_COUNT ;
// @todo: Multiple primitives
// A glTF node may consist of multiple primitives, so we may have to do multiple commands per mesh
indirectCmd . firstIndex = node - > mesh - > primitives [ 0 ] - > firstIndex ;
indirectCmd . indexCount = node - > mesh - > primitives [ 0 ] - > indexCount ;
2020-05-29 16:08:53 +01:00
2020-07-28 20:20:38 +02:00
indirectCommands . push_back ( indirectCmd ) ;
2016-08-01 21:43:47 +02:00
2020-07-28 20:20:38 +02:00
m + + ;
}
2016-08-01 21:43:47 +02:00
}
2016-07-22 23:16:08 +02:00
indirectDrawCount = static_cast < uint32_t > ( indirectCommands . size ( ) ) ;
objectCount = 0 ;
for ( auto indirectCmd : indirectCommands )
{
objectCount + = indirectCmd . instanceCount ;
}
2017-02-12 10:44:51 +01:00
vks : : Buffer stagingBuffer ;
2016-07-22 23:16:08 +02:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_TRANSFER_SRC_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& stagingBuffer ,
indirectCommands . size ( ) * sizeof ( VkDrawIndexedIndirectCommand ) ,
indirectCommands . data ( ) ) ) ;
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT ,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ,
& indirectCommandsBuffer ,
stagingBuffer . size ) ) ;
2016-08-01 21:43:47 +02:00
vulkanDevice - > copyBuffer ( & stagingBuffer , & indirectCommandsBuffer , queue ) ;
2016-07-22 23:16:08 +02:00
stagingBuffer . destroy ( ) ;
}
// Prepare (and stage) a buffer containing instanced data for the mesh draws
void prepareInstanceData ( )
{
std : : vector < InstanceData > instanceData ;
instanceData . resize ( objectCount ) ;
2018-01-18 21:31:19 +01:00
std : : default_random_engine rndEngine ( benchmark . active ? 0 : ( unsigned ) time ( nullptr ) ) ;
2016-08-08 19:52:16 +02:00
std : : uniform_real_distribution < float > uniformDist ( 0.0f , 1.0f ) ;
2016-07-22 23:16:08 +02:00
2018-01-18 21:31:19 +01:00
for ( uint32_t i = 0 ; i < objectCount ; i + + ) {
float theta = 2 * float ( M_PI ) * uniformDist ( rndEngine ) ;
float phi = acos ( 1 - 2 * uniformDist ( rndEngine ) ) ;
2020-07-28 20:20:38 +02:00
instanceData [ i ] . rot = glm : : vec3 ( 0.0f , float ( M_PI ) * uniformDist ( rndEngine ) , 0.0f ) ;
2016-08-01 21:43:47 +02:00
instanceData [ i ] . pos = glm : : vec3 ( sin ( phi ) * cos ( theta ) , 0.0f , cos ( phi ) ) * PLANT_RADIUS ;
2018-01-18 21:31:19 +01:00
instanceData [ i ] . scale = 1.0f + uniformDist ( rndEngine ) * 2.0f ;
2016-08-01 21:43:47 +02:00
instanceData [ i ] . texIndex = i / OBJECT_INSTANCE_COUNT ;
2016-07-22 23:16:08 +02:00
}
2017-02-12 10:44:51 +01:00
vks : : Buffer stagingBuffer ;
2016-07-22 23:16:08 +02:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_TRANSFER_SRC_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& stagingBuffer ,
instanceData . size ( ) * sizeof ( InstanceData ) ,
instanceData . data ( ) ) ) ;
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT ,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ,
& instanceBuffer ,
stagingBuffer . size ) ) ;
2016-08-01 21:43:47 +02:00
vulkanDevice - > copyBuffer ( & stagingBuffer , & instanceBuffer , queue ) ;
2016-07-22 23:16:08 +02:00
stagingBuffer . destroy ( ) ;
}
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 ,
& uniformData . scene ,
sizeof ( uboVS ) ) ) ;
VK_CHECK_RESULT ( uniformData . scene . map ( ) ) ;
updateUniformBuffer ( true ) ;
}
void updateUniformBuffer ( bool viewChanged )
{
if ( viewChanged )
{
2016-08-01 21:43:47 +02:00
uboVS . projection = camera . matrices . perspective ;
uboVS . view = camera . matrices . view ;
2016-07-22 23:16:08 +02:00
}
memcpy ( uniformData . scene . mapped , & uboVS , sizeof ( uboVS ) ) ;
}
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
2017-03-31 12:37:53 +02:00
// Command buffer to be submitted to the queue
2016-07-22 23:16:08 +02:00
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 ( ) ;
2016-08-01 21:43:47 +02:00
loadAssets ( ) ;
2016-07-22 23:16:08 +02:00
prepareIndirectData ( ) ;
prepareInstanceData ( ) ;
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
{
return ;
}
draw ( ) ;
2020-07-28 20:20:38 +02:00
if ( camera . updated )
{
updateUniformBuffer ( true ) ;
}
2016-07-22 23:16:08 +02:00
}
2022-06-13 23:04:53 -04:00
virtual void viewChanged ( )
{
updateUniformBuffer ( true ) ;
}
2017-11-01 14:22:10 +01:00
virtual void OnUpdateUIOverlay ( vks : : UIOverlay * overlay )
2016-07-22 23:16:08 +02:00
{
2017-11-01 14:22:10 +01:00
if ( ! vulkanDevice - > features . multiDrawIndirect ) {
if ( overlay - > header ( " Info " ) ) {
overlay - > text ( " multiDrawIndirect not supported " ) ;
}
}
if ( overlay - > header ( " Statistics " ) ) {
overlay - > text ( " Objects: %d " , objectCount ) ;
2016-08-04 18:57:32 +02:00
}
2016-07-22 23:16:08 +02:00
}
} ;
2022-06-13 23:04:53 -04:00
VULKAN_EXAMPLE_MAIN ( )